¿Qué es IO?
Primero, veamos la explicación dada por Baidu.
La entrada/salida de E/S (Entrada/Salida) se divide en dos partes: dispositivo IO e interfaz IO.
i es escribir, la primera letra de Entrada. o es salida, la primera letra de Salida.
IO también se conoce como flujo IO, IO = flujo, su núcleo es la operación de archivos, flujos de entrada y salida de tipos de bytes y caracteres.
clasificación OI
Los flujos de E/S se dividen principalmente en dos categorías, flujos de bytes y flujos de caracteres . Según la clasificación de funciones, se puede dividir en flujo de entrada y flujo de salida .
¿caudal?
Hay tres formas de almacenar datos en la computadora, una es la memoria externa, la otra es la memoria interna y la otra es el caché. Por ejemplo, el disco duro, el disco, el disco U, etc. en la computadora son todos almacenamiento externo, hay una tarjeta de memoria en la computadora y el caché está en la CPU. La capacidad de almacenamiento de la memoria externa es la más grande, seguida de la memoria y finalmente la caché, pero la lectura de datos en la memoria externa es la más lenta, seguida de la memoria y la caché es la más rápida. Aquí hay un resumen de la lectura de datos de la memoria externa a la memoria y la escritura de datos de la memoria a la memoria externa. Para la comprensión de la memoria y la memoria externa, podemos entenderla simplemente como un contenedor, es decir, la memoria externa es un contenedor y la memoria es otro contenedor. Entonces, ¿cómo leer los datos en el contenedor de almacenamiento externo en el contenedor de almacenamiento interno y cómo almacenar los datos en el contenedor de almacenamiento interno en el almacenamiento externo?
Podemos pensar en este conjunto como una piscina. La tubería de salida de agua y la tubería de inyección de agua están conectadas a la piscina. El agua saliente es equivalente a nuestra corriente saliente. La inyección de agua es equivalente a nuestro flujo de entrada.
Archivo (clase de archivo)
En primer lugar, si necesitamos usar flujos de E/S, definitivamente debemos crear lo que llamamos un "grupo".
¿Cómo crearlo? Creamos directamente un objeto de clase File.
package IoDemo;
import java.io.*;
public class IoDemo {
public static void main(String[] args) {
//创建File对象
File file = new File("D:\\test.txt");
}
}
复制代码
De hecho, este objeto, para decirlo sin rodeos, se utiliza para almacenar la dirección de archivo de un flujo de E/S.
Una vez que se crea el objeto, no tiene ninguna operación, la operación debe usar este objeto para llamar a los métodos.
Primero usamos el createNewFile()
método para crear el archivo de nuestra ruta anterior.
file.createNewFile();
复制代码
Tenga en cuenta que al definir la ruta del archivo, puede usar "/" o "\".
Y al crear un archivo, si hay un archivo con el mismo nombre en el directorio, se sobrescribirá.
因为有时候,可能我们的路径下已经存在了相对应的同名文件,所以我们要使用exists()
方法判断文件是否已经存在。
//创建File对象
File file = new File("D:\\test.txt");
//创建文件
try {
//判断文件是否存在
if(!file.exists()){
file.createNewFile();
}
} catch (IOException e) {
e.printStackTrace();
}
复制代码
其实,File类里面还存在许多方法,用法都是可以直接调用的,作为一个合格的程序员,我们可以直接阅读相关说明而在合适的时候使用对应方法。
下面列举一些常用方法。
①、创建方法
1.boolean createNewFile() 不存在返回true 存在返回false 2.boolean mkdir() 创建目录,如果上一级目录不存在,则会创建失败 3.boolean mkdirs() 创建多级目录,如果上一级目录不存在也会自动创建
②、删除方法
1.boolean delete() 删除文件或目录,如果表示目录,则目录下必须为空才能删除 2.boolean deleteOnExit() 文件使用完成后删除
③、判断方法
1.boolean canExecute()判断文件是否可执行 2.boolean canRead()判断文件是否可读 3.boolean canWrite() 判断文件是否可写 4.boolean exists() 判断文件或目录是否存在 5.boolean isDirectory() 判断此路径是否为一个目录 6.boolean isFile() 判断是否为一个文件 7.boolean isHidden() 判断是否为隐藏文件 8.boolean isAbsolute()判断是否是绝对路径 文件不存在也能判断
④、获取方法
1.String getName() 获取此路径表示的文件或目录名称 2.String getPath() 将此路径名转换为路径名字符串 3.String getAbsolutePath() 返回此抽象路径名的绝对形式 4.String getParent()//如果没有父目录返回null 5.long lastModified()//获取最后一次修改的时间 6.long length() 返回由此抽象路径名表示的文件的长度。 7.boolean renameTo(File f) 重命名由此抽象路径名表示的文件。 8.File[] liseRoots()//获取机器盘符 9.String[] list() 返回一个字符串数组,命名由此抽象路径名表示的目录中的文件和目录。 10.String[] list(FilenameFilter filter) 返回一个字符串数组,命名由此抽象路径名表示的目录中满足指定过滤器的文件和目录。
字节流的使用
我们现在已经建好这个**“水池”了,同时还可以使用方法来获取到“水池”**的一些信息。
那接下来,我们可以试着创建一套流。
字节流,相当于一滴滴的水在一个管道里运输。这个管道我们可以抽象的称之为流。
我们还是先看看知乎上,某些大佬的解释。
大佬的解释
图中蓝色为主要对应部分,红色为不对应部分,黑色的虚线部分代表这些流一般需要搭配使用。从上面的图中可以看出Java IO中的字节流是非常对称的。我们来看看这些字节流中不对称的几个类。
LineNumberInputStream 主要完成从流中读取数据时,会得到相应的行号,至于什么时候分行、在哪里分行是由改类主动确定的,并不是在原始中有这样一个行号。在输出部分没有对应的部分,我们完全可以自己建立一个LineNumberOutputStream,在最初写入时会有一个基准的行号,以后每次遇到换行时会在下一行添加一个行号,看起来也是可以的。好像更不入流了。
PushbackInputStream 的功能是查看最后一个字节,不满意就放入缓冲区。主要用在编译器的语法、词法分析部分。输出部分的BufferedOutputStream 几乎实现相近的功能。
StringBufferInputStream 已经被Deprecated,本身就不应该出现在InputStream 部分,主要因为String 应该属于字符流的范围。已经被废弃了,当然输出部分也没有必要需要它了!还允许它存在只是为了保持版本的向下兼容而已。
SequenceInputStream 可以认为是一个工具类,将两个或者多个输入流当成一个输入流依次读取。完全可以从IO 包中去除,还完全不影响IO 包的结构,却让其更“纯洁”――纯洁的Decorator 模式。
PrintStream 也可以认为是一个辅助工具。主要可以向其他输出流,或者FileInputStream 写入数据,本身内部实现还是带缓冲的。本质上是对其它流的综合运用的一个工具而已。一样可以踢出IO 包!System.out 和System.out 就是PrintStream 的实例。
对于不完全学透的看法
听起来有些难度哈,这些高级用法我们先不管。我们先来看看它到底怎么用的。
那有人又说了,那如果不去学完整,以后要是不会用怎么办?
答:其实只需要掌握主要方法就可以,因为你如果需要实现一个非常用的东西,你肯定是事先就需要去查阅相关资料,而日常开发中,常用的也就可以信手拈来啦。
OutputStream(字节输出流)
这个抽象类是表示输出字节流的所有类的超类。
!!!这里的输出可不是我们正常的输出,它反而想法,是将字节写入到文件,可以理解为将字节输出到文件。
看看里面有些啥常用方法。
我们先演示一下,字节输出流如何使用?
//定义一个String值
String str = "Hello World";
复制代码
等下我们利用字节流将这个String写入我们的文件里面。
等等!这里是字节流,我怎么可以直接写入**String
**?
嘿嘿,我们使用String类中的getBytes()方法将String转换成字节数组。
//将String值转换成字节数组
byte[] bytes = str.getBytes();
复制代码
整个代码是这样的:
package IoDemo;
import java.io.*;
public class IoDemo {
public static void main(String[] args) {
//创建File对象
File file = new File("D:\\test.txt");
//创建文件
try {
//判断文件是否存在
if(!file.exists()){
file.createNewFile();
}
} catch (IOException e) {
e.printStackTrace();
}
//定义一个String值
String str = "Hello World";
//创建字节流
FileOutputStream fos = null;
try {
//将File对象(即地址)给到FileInputStream
fos = new FileOutputStream(file);
//将String值转换成字节数组
byte[] bytes = str.getBytes();
//循环将字节数组写入到文件中
for (int i = 0; i < bytes.length; i++) {
//将bytes写入文件
fos.write(bytes[i]);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
复制代码
我们进我们的文件看看输出效果。
就已经写入了我们的String里面的内容了。
InputStream(字节输入流)
这里我也就不解释了,他这个输入不是将内容输入到文件,而是将文件里面的内容输入到我们的代码中。
//创建字节输入流
FileInputStream fis = null;
try {
//将File对象(即地址)给到FileInputStream
fis = new FileInputStream(file);
//创建字节数组
byte[] bytes = new byte[1024];
//输出字节数组
int len = 0;
while ((len = fis.read(bytes)) != -1) {
System.out.println(new String(bytes, 0, len));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
复制代码
在上文,我们已经写入了一个Hello Word
我们现在这段代码,我们将会输出文件中的内容。
len = fis.read(bytes)) != -1
复制代码
我现在还是先讲解一下这一段。
我们的
len
是定义的一个数值。而read()
方法,是为了读取到整个文件里面内容的字节长度,就像数组的lenth()
一样。而
!= -1
是因为,如果值为-1
那么,这个文件可以说是没有数据的,空的你也没必要输出。
而我们字节流呢,一般是用于读取二进制文件,如音频、图片这些,大片的文字内容还是交给字符流吧。
字符流的使用
上面的字节流,它是一个个的输出的,而我们现在的字符流,是大水管输出,一次可以运输一段。
一般可以用记事本打开的文件,我们可以看到内容不乱码的。就是文本文件,可以使用字符流。而操作二进制文件(比如图片、音频、视频)必须使用字节流。
FileWriter(字符输出流)
老规矩哈,方法自己看看。
先写入一个String
试试。
package IoDemo;
import java.io.*;
public class IoDemo {
public static void main(String[] args) {
String str = "嘿嘿!我是字符流·········";
//创建File对象
File file = new File("D:\\test.txt");
try {
//将str用writer写入文件
FileWriter fw = new FileWriter(file);
fw.write(str);
//关闭流,字符流必须关闭流才可以输出
fw.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
复制代码
写入效果如下:
实际上,写入字符串就几个步骤。
//1.将str用writer写入文件
FileWriter fw = new FileWriter(file);
//2.使用write()方法写入字符串
fw.write(str);
//3.关闭流,字符流必须关闭流才可以输出
fw.close();
复制代码
FileReader(字符输出流)
//输出文件内容
try {
//创建输入字符流
FileReader fr = new FileReader(file);
//输出文件内容
int ch = 0;
while ((ch = fr.read()) != -1){
System.out.print((char)ch);
}
//关闭流
fr.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
复制代码
这里采用
Char
挨个字符遍历数据。
包装流
缓冲流
为什么使用缓冲流呢?
简单地说就是,写入数据更快,可以加速写入。
缓冲流,也叫高效流。 能够高效读写缓冲流,能够转换编码的转换流,能够持久化存储对象的序列化对象等等。 它是四个基本File流的增强,所以也是4个流,按照数据类型分类。 缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO读取次数,从而提高读写的效率。
package IoDemo;
import java.io.*;
public class IoDemo {
public static void main(String[] args) {
//创建File对象
File file = new File("D:\\test.txt");
//创建缓冲流
BufferedWriter bw = null;
try {
//创建FileWriter对象
FileWriter fw = new FileWriter(file);
//创建BufferedWriter对象
bw = new BufferedWriter(fw);
//写入一首诗分四次写入
bw.write("窗前明月光,");
bw.write("疑是地上霜。");
bw.write("举头望明月,");
bw.write("低头思故乡。");
bw.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
复制代码
这个很简单,自己看就可以看懂。
转换流
InputStreamReader:把字节输入流转换为字符输入流
OutputStreamWriter:把字节输出流转换为字符输出流
我就不写示范了,网上找了一段。
//转换流实现将 a.txt 文件 复制到 b.txt 中
//1、创建源和目标
File srcFile = new File("io"+File.separator+"a.txt");
File descFile = new File("io"+File.separator+"b.txt");
//2、创建字节输入输出流对象
InputStream in = new FileInputStream(srcFile);
OutputStream out = new FileOutputStream(descFile);
//3、创建转换输入输出对象
Reader rd = new InputStreamReader(in);
Writer wt = new OutputStreamWriter(out);
//3、读取和写入操作
char[] buffer = new char[10];//创建一个容量为 10 的字符数组,存储已经读取的数据
int len = -1;//表示已经读取了多少个字符,如果是 -1,表示已经读取到文件的末尾
while((len=rd.read(buffer))!=-1){
wt.write(buffer, 0, len);
}
//4、关闭流资源
rd.close();
wt.close();
复制代码
扩展
然后,找相关资料时,还涨了个知识。大家可以看看。
合并流
合并流:把多个输入流合并为一个流,也叫顺序流,因为在读取的时候是先读第一个,读完了在读下面一个流。
SequenceInputStream seinput = new SequenceInputStream();
new FileInputStream("io/1.txt"), new FileInputStream("io/2.txt"));
byte[] buffer = new byte[10];
int len = -1;
while((len=seinput.read(buffer))!=-1){
System.out.println(new String(buffer,0,len));
}
seinput.close();
复制代码
这里先是创建了一个SequenceInputStream
对象,然后new
了两个FileInputStream
对象,我们使用合并流读取,先使用**seinput.read方法
**读取1.txt
的内容,然后再对其2.txt
的内容。