前言
上一篇IO流就介绍了File和基本流,这节介绍好用的高级流。
还有IO流的心得:随用随创建,什么时候不用什么时候关闭
目录
一、缓冲流
缓冲流顾名思义就是要多一个缓冲,加快数据的传输速度。但是相对字符流本身就有缓冲区的来说,字节流得到的增益比较多。
1.体系图
2.字节缓冲流
原理:底层自带了长度为8192的缓冲区提高性能
(1)用法
1.创建基本字节流对象
2.把基本字节流包装成字节缓冲流
3.读写数据
4.关闭流
1.包装基本字节流
2.读写数据
缓冲流的读写方法和基本流的一样,write和read系列方法。
3.释放资源
关闭流还是用close()方法,这里只需要关闭相关的高级流,高级流所包装的基本流在底层是有关闭的,不用写包装的基本流的关闭。
(2)底层原理
比较字节基本流效率提高的原理
包装基本流创建缓冲流,传递一个8192作为缓冲区大小,即字节数组的长度
创建一个长度8192的字节数组,作为缓冲区
每次写数据,将数据编码后的字节写到缓冲数组
刷新缓冲区(基本字节流的刷新方法是空方法,因为没有缓冲区)
刷新缓冲区其实就是调用基本字节流的write将数据写到文件
3.字符缓冲流
(1)用法
字符缓冲流的用法和字节缓冲流的用法一样,都是4步
1.创建基本字符流对象
2.把基本字符流包装成字符缓冲流
3.读写数据
4.关闭流
1.将基本流包装成高级流
原理也是底层自带了长度为8192的缓冲区提高性能
2.字符缓冲流特有的方法
细节:readLine方法在读取的时候,一次读一整行,遇到回车换行结束
但是他不会把回车换行读到内存当中
意思就是你用print两次输出readLine读到的数据,两行数据会在一行。
不同操作系统的换行符是不相同的,使用newLine()方法,可以无视平台限制
二、转换流
转换流就是,字符流和字节流之间的桥梁,用于字节流和字符流之间的转换。
为什么会有转换流?因为字节流想要使用字符流的方法。还有指定字符集读写(这个作用在JDK11就被淘汰了,JDK11时的Reader输入字符流的构造器就可以传入指定的字符集,底层原理就是输入字符流继承了InputStreamRead,实现字符集的指定)。
1.作用
InputStreamReader:将字节流转换为字符流
OutputStreamWriter:将字符流转换为字节流
2.示例
指定GBK读数据
指定GBK写数据
以GBK读,以UTF-8写
字节流用缓冲字符流的特殊方法实现一行一行读
三、序列化流和反序列化流
在数据库或文件系统中存储复杂数据对象时,序列化可以将对象转换为字节流,便于存储和后续读取。加密存储,防止用户更改数据。
1.序列化流ObjectOutputStream
作用就是,将程序中的对象序列化到文件中。
构造器和方法
示例
对象想要放入序列化流就必须实现Serializable接口
2.反序列化流ObjectInputStream
作用就是,把序列化到本地文件中的对象,读取到程序中来
构造器和方法
示例
3.关键细节
实现了 Serializable接口 的类,java会根据这个类的内容,计算一个序列号,将这个类的对象写到文件里面的时候,也会把这个类的序列号写好文件里面。
如果你修改了Javabean中的该类的代码内容,该类的序列号会变化,和反序列化的时候从文件得到的序列号发生冲突,反序列化程序就会报错。
所以如何防止出现这个问题呢?
其实我们只需要将javabean的类的添加一个属性,固定该类的序列号(版本号)就可以了。如下
注意:属性的四个修饰词是固定的,属性名也是
那么又有一个问题,如果我的一个类的一个成员不想被序列化到文件,要怎么做?
这样其实就要在成员变量前面用transient关键词修饰
最后一个问题,将多个自定义对象序列化到文件中,但是对象的个数不确定,该如何操作呢?
一般我们不会一个一个对象去序列化到文件中,我们通常将对象放到集合(实现 Serializable接口 )里面 ,再将这个集合序列化到文件里面,反序列化就只需要将一个集合反序列化到程序中就可以了。
案例如下
注意注意注意:我们一定要先序列化到文件,才有反序列化这个步骤。
四、打印流
1.字节打印流PrintStream
构造方法
注意:字符打印流是没有缓冲区的,所以开不开自动刷新都是一样的
成员方法(写到文件)
2.字符打印流PrintWriter
构造方法
成员方法
3.和标准输出流的关系
如果你关闭了系统的标准输出流,就必须重新运行程序,不然后面的输出语句输出不到控制台。
五、压缩流和解压缩流
1.解压缩流ZipInputStream
用法:
1.创建一个解压缩流对象(参数是zip压缩文件的字节输入流)
2.获取这个解压缩流中的每一个ZipEntry对象
3.对每个对象分文件和文件夹处理
4.每次处理一个ZipEntry对象释放一次资源
解压缩的本质:把压缩包里面的每一个文件或者文件夹读取出来,看作每一个ZipEntry对象拷贝到目的地当中
public class UnzipStreamDemo { public static void main(String[] args) throws IOException { File src = new File("D:\\aaa.zip"); File des = new File("D:\\des"); unzip(src, des); } private static void unzip(File src, File des) throws IOException { //解压的本质:把压缩包里面的每一个文件或者文件夹读取出来,按照层级拷贝到目的地当中 //创建一个解压缩流用来读取压缩包中的数据 ZipInputStream zip = new ZipInputStream(new FileInputStream(src)); //要先获取到压缩包里面的每一个zipentry对象 ZipEntry ze; while ((ze = zip.getNextEntry()) != null) { if (ze.isDirectory()){ //文件夹:需要在目的地dest处创建一个同样的文件夹 File dir = new File(des, ze.getName()); dir.mkdir(); }else { //文件:需要读取到压缩包中的文件,并把他存放到目的地dest文件夹中(按照层级目录进行存放) File file = new File(des, ze.getName()); FileOutputStream fos = new FileOutputStream(file); int b; while ((b = zip.read()) != -1) { fos.write(b); } fos.close(); //表示在压缩包中的一个文件的数据处理完毕了。 zip.closeEntry(); } } } }
2.压缩流ZipOutputStream
用法:
1.创建压缩流关联压缩包
2.创建ZipEntry对象,表示压缩包里面的每一个文件和文件夹(参数是相对压缩包的路径)
3.把ZipEntry对象放到压缩包当中
4.把src文件中的数据写到压缩包当中
5.每次处理一个ZipEntry对象释放一次资源
压缩本质:把每一个(文件/文件夹)看成ZipEntry对象放到压缩包中,最终产生一个.zip的压缩包
(1).压缩单个文件
(2).压缩文件夹
下面压缩的实现方法,最重要的是每一个ZipEntry对象的路径,是压缩包的路径