1. Descripción general del flujo
1.1 ¿Qué es la OI?
IO
:Entrada/Salida significa entrada y salida, también llamado flujo (río, flujo de agua), que se refiere al proceso de movimiento de datos de un lugar a otro; para las computadoras, el proceso de copia de archivos, escritura y guardado de archivos y funciones de visualización son Se utilizan todas las IO y el proceso de transmisión de datos se considera como entrada y salida. Desde una perspectiva práctica, puedes utilizar los oídos como flujo de entrada y la boca como flujo de salida.
- Medios de entrada y salida:
- documento
- red
- entrada de teclado)
- Pantalla (salida)
1.2 Clasificación IO
Java proporciona las API correspondientes para operaciones IO. Casi todas las operaciones IO en Java requieren el uso de java.io
paquetes; la clasificación de flujos en Java incluye varios métodos:
- Dividido por la dirección del flujo (los procesos de entrada y salida generalmente se consideran desde la perspectiva del programa )
- Flujo de entrada (entrada)
- Producción
- Según el tipo de procesamiento de flujo.
- Flujo de bytes (byte)
- Flujo de caracteres (char)
- Según la función del flujo.
- Flujo de nodos (interactuar directamente con fuentes de entrada y salida)
- Procesamiento de flujos (flujos que envuelven otros flujos: flujos envueltos)
1.3 Clase para padres de nivel superior
Aunque los streams tienen una gran variedad de contenidos, son muy regulares, casi todos los streams heredan de cuatro streams básicos:
flujo de entrada | flujo de salida | |
---|---|---|
flujo de bytes | java.io.InputStream | java.io.OutputStream |
flujo de personajes | java.io.Reader | java.io.Escritor |
Las cuatro secuencias anteriores son las clases principales de nivel superior de todas las secuencias de Java y todas son clases abstractas ;
La identificación del tipo de flujo es muy regular: generalmente, el Stream
flujo que termina en es un flujo de bytes; generalmente Reader/Writer
el flujo que termina en es un flujo de caracteres.
1.4 Escenarios de uso
- Copia de archivo (Archivo)
- Carga y descarga de archivos
- Importación y exportación de Excel.
- Transmisión de datos en programas de red (herramientas de chat)
- …
2. Flujo de bytes
2.1 Descripción general de bytes
En un sistema informático, todo son bytes: varios archivos (documentos de texto, imágenes, videos, audios) almacenados en el sistema se almacenan en forma de bytes en el nivel inferior de la computadora, por lo que para cualquier operación con archivos, todos pueden ser operado byte a byte (lectura, escritura); la clase principal de nivel superior del flujo de bytes en java.io es: InputStream/OutputStream
.
2.2 Flujo de entrada de bytes
Todos los flujos de entrada de bytes en Java se java.io.InputStream
heredan. Dado que esta clase es una clase abstracta, no se pueden crear instancias de ella, por lo que jdk proporciona algunas subclases para la entrada de bytes que se pueden usar directamente:
FileInputStream
ByteArrayInputStream
BufferedInputStream
ObjectInputStream
Métodos comunes de InputStream
int available()
: obtiene el número de bytes legibles en la secuenciaint read()
:Lee un byte de la secuencia y devuelve los datos del byte leído actualmente.int read(byte[] b)
: almacena los datos del byte leído en el búfer de bytes y devuelve el número total real de bytes leídos.skip(int b)
:Omitir los bytes especificados para la siguiente lectura
2.2.1. flujo de entrada de archivos
FileInputStream
Es un flujo de implementación para el flujo de entrada de bytes, utilizado principalmente para leer archivos, y los métodos internos son principalmente para la implementación de la clase principal.
-
Constructores comunes
FileInputStream(File file)
:Un objeto creado a partir del archivo proporcionadoFileInputStream(String filePath)
: construye un objeto basado en la ruta del archivo proporcionada
-
Usar
FileInputStream
para leer archivos-
Lectura básica (leer un byte a la vez)
//创建File对象 File file = new File("C:\\Users\\Administrator\\Desktop\\新建文本文档.txt"); //基于File创建字节输入流 InputStream in = new FileInputStream(file); //读取一个字节 int i = in.read(); System.out.println((char)i);
IO de Java solo puede leer y escribir archivos estándar y no le permite crear directamente flujos de entrada o salida para un directorio (causará
java.io.IOException(拒绝访问)
) -
Leer usando el búfer de bytes (el tamaño del búfer es el total de bytes legibles)
Dado que el método de lectura anterior lee un byte a la vez, la eficiencia de lectura es muy baja, por lo que en el desarrollo real, generalmente se usa un búfer de bytes para mejorar la eficiencia de lectura:
//创建File对象 File file = new File("C:\\Users\\Administrator\\Desktop\\新建文本文档.txt"); //基于File创建字节输入流 InputStream in = new FileInputStream(file); //创建字节缓冲区(大小为总可读字节数) byte[] arr = new byte[i]; //将流中读取的字节内容存储到数组中 int total = in.read(arr); //将字节数组转换为String字符串 System.out.println(new String(arr));
-
Leer utilizando un búfer de bytes del tamaño adecuado
La lectura anterior es leer los datos del archivo en el búfer a la vez. Por lo tanto, la capacidad del búfer puede ser muy grande. Si se utiliza un búfer demasiado grande para leer un archivo grande, puede causar problemas. Esto causa Consume espacio y afecta la ejecución de otros programas, por lo que se requiere un búfer del tamaño adecuado para la lectura repetida.
//创建文件输入流对象(文件:一缸水) InputStream in = new FileInputStream("C:\\Users\\Administrator\\Desktop\\Hero.java"); //创建缓冲区(购买一个大小适中的水桶) byte[] b = new byte[1024]; //声明临时变量表示每次读取的实际字节数 int len = 0; while((len = in.read(b)) != -1){ String s = new String(b,0,len); System.out.println(s); }
El método de lectura anterior es una solución general para leer flujos de bytes.
-
2.3 Flujo de salida de bytes
Además de las operaciones de lectura (entrada) de acuerdo con la dirección del flujo de datos, las operaciones de escritura (salida) de datos también son muy comunes. Los flujos de salida de bytes en Java se heredan java.io.OutputStream
de
Dado que OutputStream
es una clase abstracta y no se pueden crear instancias, jdk también proporciona subclases para esta secuencia:
FileOutputStream
ByteArrayOutputStream
BufferedOutputStream
ObjectOutputStream
PrintStream
Métodos comunes de la clase OutputStream:
write(int b)
:Escribe un byte a través del flujo de salida en la fuente de salida de destino.write(byte[] b)
: escriba una matriz de bytes en la fuente de salida de destino a través del flujo de salidawrite(byte[] b,int offset,int len)
Escriba len bytes comenzando desde el desplazamiento de una matriz hasta la fuente de salida de destino
2.3.1. Flujo de salida de archivos
FileOutputStream
Es un flujo de implementación para un flujo de salida de bytes, que se utiliza principalmente para escribir archivos. Los métodos internos se utilizan principalmente para la implementación de la clase principal.
-
Constructores comunes
FileOutputStream(String filePath)
:Crea un flujo de salida para operaciones en un archivo estándar según su rutaFileOutputStream(String filePath,boolean append)
: cree un flujo de salida para operaciones en un archivo estándar según su ruta, usando el modo de agregar.FileOutputStream(File file)
:Crea un flujo de salida para operaciones en un archivo estándar según su rutaFileOutputStream(File file,boolean append)
Crea un flujo de salida para operaciones en un objeto de archivo estándar, usando el modo agregar
-
Usar
FileOutputStream
para escribir archivos//try...with语句:JDK1.7 //针对所有的第三方资源不再需要手动回收(关闭) //因为从jdk1.7开始,很多资源类都实现过Closeable接口,但凡实现过该接口类 //只要将其在try()中创建,不再手动关闭资源,会由JVM自动回收 try(OutputStream os = new FileOutputStream("readme.txt",true);){ //写出一个字节到文件中 // os.write(101); //荀子·劝学篇 String msg = "不积小流无以成江海,不积跬步无以至千里"; os.write(msg.getBytes()); }catch (IOException e){ e.printStackTrace(); }
2.4 Copia completa del expediente
/**
* 将一个源文件拷贝到一个目标目录中
* @param src 源文件
* @param targetDir 目标目录
*/
public static void copyFile(File src, File targetDir) throws IOException {
InputStream is = null;
OutputStream os = null;
try {
//获取源文件的输入流
is = new FileInputStream(src);
//获取目标文件的输出流(目标文件是由:目录+源文件名称构成):文件输出流可以创建文件(前提:父目录必须存在)
os = new FileOutputStream(new File(targetDir,src.getName()));
//声明字节缓冲区(缓冲区越大,拷贝效率越高,但是带来空间损耗也越大)
byte[] b = new byte[1024*1024];
//临时变量表示实际读取的字节数
int len = 0;
System.out.println("开始拷贝...");
while((len = is.read(b)) != -1){
//写出读取的内容到输出流
os.write(b,0,len);
}
System.out.println("拷贝完成!");
} finally{
if(os != null){
os.close();
}
if(is != null){
is.close();
}
}
}
//测试
public static void main(String[] args) throws IOException {
//源文件
File src = new File("D:\\素材\\视频\\短视频\\test.mp4");
//目标目录
File targetDir = new File("C:\\Users\\Administrator\\Desktop");
//文件拷贝
copyFile(src,targetDir);
}
Implementar copia de directorio basada en copia de archivo:
public class FileCopy { /** * 将一个源目录拷贝到另一个目标目录中 * @param srcDir * @param targetDir */ public static void copyDir(File srcDir,File targetDir) throws IOException { //获取新目录对象 targetDir = new File(targetDir,srcDir.getName()); //如果新目录不存在,则创建 if(!targetDir.exists()){ targetDir.mkdirs(); } File[] files = srcDir.listFiles(); if(Objects.nonNull(files)){ for (File file : files) { if(file.isDirectory()){ //目录递归拷贝 copyDir(file,targetDir); }else{ //执行文件拷贝 copyFile(file,targetDir); } } } } /** * 将一个源文件拷贝到一个目标目录中 * @param src 源文件 * @param targetDir 目标目录 */ public static void copyFile(File src, File targetDir) throws IOException { InputStream is = null; OutputStream os = null; try { //获取源文件的输入流 is = new FileInputStream(src); //获取目标文件的输出流(目标文件是由:目录+源文件名称构成) os = new FileOutputStream(new File(targetDir,src.getName())); //声明字节缓冲区(缓冲区越大,拷贝效率越高,但是带来空间损耗也越大) byte[] b = new byte[1024*1024]; //临时变量表示实际读取的字节数 int len = 0; System.out.println("开始拷贝..."); while((len = is.read(b)) != -1){ //写出读取的内容到输出流 os.write(b,0,len); } System.out.println("拷贝完成!"); } finally{ if(os != null){ os.close(); } if(is != null){ is.close(); } } } public static void main(String[] args) throws IOException { //源目录 File srcDir = new File("D:\\素材\\视频"); //目标目录 File targetDir = new File("C:\\Users\\Administrator\\Desktop\\video"); //目录拷贝 copyDir(srcDir,targetDir); } }
3. Flujo de personajes
3.1 Descripción general de los personajes
Por lo general 文本文件
, el contenido del archivo generalmente existe en forma de caracteres (un carácter chino, una letra en inglés, un símbolo), generalmente en el modo de codificación GBK; los 1个字符=2个字节
flujos de caracteres generalmente son adecuados para procesar datos de texto, java Casi todos los flujos de caracteres en Reader/Writer
, y todos heredan de dos clases abstractas básicas:
java.io.Reader
: Flujo de entrada de caracteresjava.io.Writer
: Flujo de salida de caracteres
3.2 Flujo de entrada de caracteres
Los flujos de entrada de caracteres se utilizan generalmente para leer datos de texto.Subcategorías comunes:
InputStreamReader
FileReader
BufferedReader
CharArrayReader
3.2.1. Lector de archivos
FileReader es una secuencia que lee el contenido del archivo en forma de secuencia de caracteres. Se InputStreamReader
hereda de FileReader y no tiene métodos internos redundantes (consistentes con la API de la clase principal). Proporciona los siguientes métodos de construcción comunes:
-
Métodos de construcción comúnmente utilizados.
FileReader(File file)
: Obtiene el flujo de entrada de caracteres del archivo según el objeto de archivo proporcionadoFileReader(String path)
:Obtiene el flujo de entrada de caracteres del archivo según la ruta del archivo proporcionada
-
Uso específico
//创建文件字符输入流 FileReader fr = new FileReader("D:\\文档资料\\电子书\\书籍推荐.txt"); //获取当前流使用的默认编码模式(并非目标文件的编码) // String encoding = fr.getEncoding(); // System.out.println(encoding); //读取一个字符(以int类型存在) // int read = fr.read(); // System.out.println((char)read); // int c = 0; // while((c = fr.read()) != -1) // { // System.out.print((char)c); // } //使用字符缓冲区 char[] ch = new char[100]; int len = 0; while((len = fr.read(ch)) != -1){ String s = new String(ch,0,len); System.out.print(s); }
3.3 Flujo de salida de caracteres
Los flujos de salida de caracteres se utilizan generalmente para escribir datos de texto.Subcategorías comunes:
- Lector`
OutputStreamWriter
BufferedWriter
CharArrayWriter
FileWriter
PrintWriter
3.3.1 Escritor de archivos
FileWriter es una secuencia que escribe el contenido del archivo en forma de secuencia de caracteres. Se hereda de OutputStreamWriter
FileWriter y no tiene métodos internos redundantes (consistentes con la API de la clase principal). Proporciona los siguientes métodos de construcción comunes:
-
Métodos de construcción comúnmente utilizados.
FileWriter(File file)
: Obtiene el flujo de entrada de caracteres del archivo según el objeto de archivo proporcionadoFileWriter(String path)
:Obtiene el flujo de entrada de caracteres del archivo según la ruta del archivo proporcionadaFileWriter(File file,boolean append)
: obtiene el flujo de entrada de caracteres del archivo según el objeto de archivo proporcionado (usando el modo agregar)FileWriter(String path,boolean append)
: Obtenga el flujo de entrada de caracteres del archivo según la ruta del archivo proporcionada (usando el modo agregar)
-
Uso específico
//基于指定的文件创建字符输出流 FileWriter fw = new FileWriter("readme.txt"); fw.write("路漫漫其修远兮,吾将上下而求索"); //允许在流未关闭之前,强制将字符缓冲区中的数据写出到目标输出源 fw.flush(); Thread.sleep(10000); fw.write("好好xio习,天天up!!!"); fw.close();
Para el flujo de salida de caracteres, se usa uno internamente.Al
字符缓冲区(字符数组)
escribir datos, los datos que se escribirán generalmente se almacenan en caché en la matriz de caracteres, y luego, cuando el flujo se cierra (o cuando el búfer está lleno), el búfer se almacena en caché en uno. ir. Los datos en el búfer de caracteres se escriben en la fuente de salida de destino. Si necesita forzar que los datos en el búfer de caracteres se escriban antes de que se cierre la secuencia (o cuando el búfer no esté lleno), puede hacerlo manualmente. llamarflush()
a salida forzada.
4. Flujo de procesamiento
El tipo de procesamiento (función) de la secuencia se divide en secuencia de nodo y secuencia de procesamiento:
-
flujo de nodo
También llamado flujo de bajo nivel, un flujo que se comunica directamente con la fuente de entrada y salida (por ejemplo: FileReader, FileWriter, FileInputStream, FileOutputStream)
-
flujo de procesamiento
El flujo de procesamiento también se denomina flujo avanzado o flujo de empaquetado , que se puede utilizar para envolver otros flujos de nodos para lograr
类型转换
una mejora del flujo o la eficiencia. El flujo de procesamiento consiste principalmente en缓冲流
y转换流
El embalaje suele utilizar un patrón de diseño (patrón decorador)
4.1. Flujos almacenados en buffer
La secuencia almacenada en búfer es una secuencia con su propio búfer, que consta principalmente de los cuatro tipos siguientes:
BufferedInputStream
: flujo de entrada almacenado en búfer de bytesBufferedOutputStream
: flujo de salida almacenado en búfer de bytesBufferedReader
: Flujo de entrada del búfer de caracteresBufferedWriter
: Flujo de salida almacenado en búfer de caracteres
long start = System.currentTimeMillis();
try(
//获取输入流
InputStream in = new FileInputStream("D:\\集团资料\\宣讲\\video\\云计算&大数据\\阿里云.mp4");
//包装节点流
BufferedInputStream bis = new BufferedInputStream(in);
//获取输出流
OutputStream os = new FileOutputStream("C:\\Users\\Administrator\\Desktop\\阿里云.mp4");
//包装节点流
BufferedOutputStream bos = new BufferedOutputStream(os);
) {
System.out.println("开始拷贝");
byte[] b = new byte[1024*1024*8];
int len = 0;
while((len = bis.read(b)) != -1){
bos.write(b,0,len);
}
System.out.println("拷贝结束");
}catch (IOException e){
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
El principio de implementación interna de la secuencia almacenada en búfer utiliza un
8kb
búfer de bytes con un tamaño predeterminado de .Cada vez que se llena el espacio del búfer, los datos se leen y escriben a través del objeto de secuencia.
4.2 Flujo de conversión
En algunos requisitos, a menudo es necesario convertir los datos de un flujo de bytes en un flujo de caracteres, o es necesario convertir un flujo de caracteres en un flujo de bytes. En este caso, es necesario utilizar un flujo de conversión para completar la función. El flujo de conversión se usa generalmente en: transcodificación de archivos, cuando los datos leídos de la red son un flujo de bytes, pero el contenido del flujo son caracteres, puede usar el flujo de conversión para lograr la conversión; el flujo de conversión en Java. io consta principalmente de las dos clases siguientes:
InputStreamReader
: Se utiliza para convertir un flujo de entrada de bytes en un flujo de entrada de caracteres (bytes -> caracteres)OutputStreamWriter
: Se utiliza para convertir un flujo de salida de caracteres en un flujo de salida de bytes (Carácter -> Byte)
Ejemplos de uso:
//获取标准输入流
InputStream is = System.in;
//将字节流转换为字符流
Reader isr = new InputStreamReader(is);
//将字符节点流包装为缓冲流
BufferedReader br = new BufferedReader(isr);
//读取一行
String line = br.readLine();
System.out.println("输入的内容--->"+line);
La conversión de una transmisión solo puede convertir el tipo de transmisión, no la dirección de la transmisión.
4.3 Caso integral: herramienta de transcodificación de archivos
En la copia diaria de archivos, debido a las diferencias en los modos de codificación de múltiples editores (sistemas), es muy probable que ocurra el problema de archivos confusos (más común para archivos de texto). La función de transcodificación de archivos se puede lograr mediante la conversión de secuencias:
public class FileCharacterConvert {
/**
* 将一个目标文件的编码转换为新的编码
* @param file 原始文件
* @param targetDir 目标目录
* @param oldEncoding 原始编码
* @param newEncoding 新编码
*/
public static void convert(File file,File targetDir, String oldEncoding, String newEncoding) throws IOException {
//使用特定的编码获取文件的输入流
FileInputStream fis = new FileInputStream(file);
Reader reader = new InputStreamReader(fis,oldEncoding);
BufferedReader br = new BufferedReader(reader);
//使用特定的编码获取文件的输出流
FileOutputStream fow = new FileOutputStream(new File(targetDir,file.getName()));
Writer writer = new OutputStreamWriter(fow,newEncoding);
BufferedWriter bw = new BufferedWriter(writer);
//开始读写
String line = "";
//循环读取每一行文本以换行符作为一行的结束标记(但是换行符不会被作为内容读取)
while((line = br.readLine()) != null){
//写入读取的一行文本()
bw.write(line);
//手动加入一个换行标记到文件,否则所有内容会在同一行显示
bw.newLine();
//将缓冲区的数据强制输出到目标输出源
bw.flush();
}
bw.close();
br.close();
}
public static void main(String[] args) throws IOException {
//准备需要转换的文件
File f = new File("C:\\Users\\Administrator\\Desktop\\GamePanel.java");
convert(f,new File("C:\\Users\\Administrator\\Desktop\\temp"),"gbk","utf-8");
}
}
5. Imprimir secuencia
java.io proporciona dos flujos especiales para la salida de datos: flujo de impresión. El flujo de impresión solo tiene salida pero no entrada. Se puede crear directamente para la fuente de salida o se pueden empaquetar otros flujos de salida. El flujo de impresión incluye principalmente los dos flujos siguientes .:
PrintStream
: flujo de impresión de bytesPrintWriter
: Flujo de impresión de caracteres
5.1. Flujo de impresión
PrintStream
Se hereda de java.io.OutputStream
, es una secuencia utilizada para la salida de datos de bytes, contiene una gran cantidad de print/prinln
métodos sobrecargados y, System.out
en realidad, es unPrintStream
-
Constructores comunes
PrintStream(File file)
: crea una secuencia de impresión basada en el archivo especificadoPrintStream(OutputStream os)
: Envuelve otros flujos de salida de bytesPrintStream(OutputStream os,boolean autoFlush)
: Envuelve otros flujos de salida de bytes para actualizar automáticamente los datos en el flujo
-
Métodos comunes
print(...)
println(...)
-
Uso básico
// PrintStream ps = new PrintStream(new File("readme.txt"),true); OutputStream os = new FileOutputStream("readme.txt", true); PrintStream ps = new PrintStream(os,true); ps.println("你好中国");
5.2. Imprenta
PrintWriter
La diferencia PrintStream
es que PrintWriter
es un flujo de impresión basado en caracteres (incluido el búfer de caracteres) y su API es PrintStream
muy similar; recuerde ejecutar el método cuando utilice PrintWriter
datos de impresión .flush()
FileWriter fw = new FileWriter("readme.txt",true);
PrintWriter pw = new PrintWriter(fw);
pw.println("哪里有彩虹告诉过我!!!");
pw.flush();
pw.close();
6. Lectura de archivos de recursos (clase de propiedades)
En el proceso de aprendizaje posterior del marco, a menudo está involucrada la escritura de algunos archivos de configuración. Los archivos de propiedades son un tipo de archivo muy común. Java proporciona una clase para leer y escribir archivos de propiedades java.util.Properties
:
InputStream in = PropertiesDemo.class.getResourceAsStream("/jdbc.properties");
//创建属性对象
Properties prop = new Properties();
// Properties prop = System.getProperties();
//加载流到属性对象中
prop.load(in);
String user = prop.getProperty("user");
String url = prop.getProperty("url");
System.out.println(user);
System.out.println(url);
prop.setProperty("maxActive","10");
File f = new File("D:\\带班资料\\2021\\J2106\\课程资料\\code\\part1-javase\\java高级\\lesson_01_IO\\resources\\jdbc.properties");
FileWriter fw = new FileWriter(f);
prop.store(fw,"this is jdbc config file");
fw.close();
7. Resumen del flujo
- Todos los nombres de flujo de entrada contienen
input
,read
- Todos los nombres de flujo de salida contienen
output
,write
- Todos los nombres de flujos de bytes
stream
terminan con - Todos los nombres de secuencias de caracteres terminan con
reader
owriter
- Generalmente, los archivos de tipo binario (imágenes, videos, audios, archivos comprimidos, etc. que no se pueden abrir directamente usando documentos de texto) utilizan principalmente operaciones de flujo de bytes.
- Los archivos de tipo de texto generales (txt, md, java y otros archivos que se pueden abrir directamente usando documentos de texto) utilizan principalmente operaciones de flujo de caracteres.