一.概述
Java中的IO流是一种计算机用语,主要是用于处理数据的传输。IO流是数据传输所需的通道,用于实现数据在程序间的有序传输,可以提高数据传输效率和稳定性
也就是IO流用于读写文件中的数据
比如游戏的进度需要备份,这就是写。在下次登录游戏时数据还在,这就是读
IO流中以程序为参照物,程序向文件中读取数据,程序向文件中写入数据
⑴IO流的分类
①按照流的方向
IO流可以分为输入流和输出流
②按照操作的文件类型
IO流可以分为字节流和字符流
其中字节流可以操作所有类型的文件,如图片,视频
而字符流只能操作纯文本文件( Windows自带的记事本可以打开读懂的文件)
⑵IO流的作用
用于读写数据(本地文件,网络文件)
二.IO流体系结构
IO流按照操作文件可分为字节流与字符流,其中的字节流与字符流又有许多子类,如缓冲流,转换流,序列化流,压缩流以及打印流等等
字节流与字符流是基本流,他们的子类全都是在他们的基础上改写的
下面我们来一一学习
⑴字节流
⒈字节输入流
字节输入流可以把文件中的数据读取到程序中
FileInputStream:操作本地文件的字节输入流
①构造方法
②常用成员方法
Ⅰ. read
read读取数据,读取的返回值int是在ASCll上对应的数据,当读到文件末尾了,方法返回-1
因此我们可以根据read的这一特性来读取文件
③如何读取数据?
要利用字节输入流读取文件中的数据
首先我们要创建一个FileInputStream对象,就相当于是程序和文件之间的连接通道
然后我们利用read方法读取数据
最后close关闭通道
其中循环读取数据时,我们可以定义一个变量b用来记录每次read读取的数据,当b=-1时,就表示文件读取完了
Ⅰ.一次读取一个字节
int read()
Ⅱ.一次读取一个字节数组
int read( byte[] bytes)
⒉字节输出流
字节输出流可以把程序中的数据写入到文件中
FileOutPutStream:操作本地文件的字节输出流
①构造方法
创建对象就表示程序与文件之间的连接通道
Ⅰ. FileOutPutStream(File file, boolean append)
其中的第二个参数表示:是否开启续写功能,下面会有对方法的细节说明
②常用成员方法
②如何写入数据?
要利用字节输出流将数据写入文件
首先要创建FileOutPutStream对象,连接通道
然后利用write方法将数据写入
最后利用close关流
Ⅰ.一次写一个字节数据
void write(int b)
Ⅱ.一次写一个字节数组
viod write( byte[] bytes)
③方法细节
Ⅰ.构造方法细节
FileOutputStream(File file)
如果文件不存在,则创建对象时会自动创建一个新的文件
如果文件存在,则创建对象时会覆盖原来的文件
如图:该方法底层就是调用的可以续写的构造方法FileOutputStream(File file, boolean append),其中的append就是续写的开关
因此若我们在创建对象时不想原来的文件被覆盖,我们就可以传递true打开续写的开关
3.结合字节流的文件拷贝
知道了如何利用FileOutputStream和FileInputStream去读写数据,那么这两种方法结合的应用场景有什么?
FileOutputStream与FileInputStream结合的利用场景之一就是文件拷贝
首先利用字节输入流读取文件的数据
然后再利用字节输出流将读取的数据拷贝到我们事先准备好的文件中
最后关流
这样一读一写,就完成了我们文件拷贝的逻辑
⑵字符流
字节流读取数据时,文件中若有中文,就会出现乱码
为了解决字节流读取中文乱码的现象,字符流应运而生,字符流的底层就是字节流,只不过在字节流的基础上多了一个字符集
⒈字符输入流
字符输入流读取数据,一次读取一个字节,遇到中文时,一次读取多个字节
FileReader:操作本地文件的字符输入流
其中的方法细节与字节流一致
①构造方法
②常用成员方法
read方法读取数据时按照字节进行读取,遇到中文时一次读取多个字节,当读到末尾时返回-1
③如何读取数据
利用字符输入流读取文件中的数据
首先我们要创建一个FileWriter对象,就相当于是程序和文件之间的连接通道
然后我们利用read方法读取数据
最后close关闭通道
Ⅰ.一次读取一个字符
int read()
空参的read方法一次读取一个字节,遇到中文时一次读取多个字节,并把字节解码转成十进制返回
Ⅱ.一次读取一个字符数组
int read( char[] chars)
⒉字符输出流
把数据按照指定的编码方式进行编码,变成字节再写到文件中
FileWriter:操作本地文件的字符输出流
其中的方法细节与字节流一样
①构造方法
②常用成员方法
Ⅰ. flush
字符流传输数据时,底层会关联文件并创建一个长度为8192的字节数组
读取数据时,read方法会先到缓冲区中读取
若缓冲区中没有数据可以读取,就从文件中读取数据并装到缓冲区中,read方法再到缓冲区中读取数据,若文件中也没有数据了,read方法就会返回-1
flush方法手动将缓冲区里的数据传输到文件当中,这样我们就不用在缓冲区存满的时候才能将数据传输到文件中
如图:我们在运行write方法时,缓冲区会先存满数据,这时文件中是没有写入任何数据的
但当我们手动刷新或关流时,缓冲区里的数据会立刻写入到文件当中
③如何写入数据
Ⅰ.一次写入一个字符
void write(int c)
Ⅱ.一次写入一个字符数组
void write( char[] chars)
⑶缓冲流
缓冲流分为字节缓冲流与字符缓冲流,底层自带了一个长度为8192的缓冲区用来提高性能,因为字节流的底层没有缓冲区,而字符流有缓冲区,因此缓冲流对于字节流的提升大些
缓冲流是高级流,是在基本流的基础上进行的包装
1.字节缓冲流
⒈1字节缓冲输入流
BufferedInputStream:提高字节流读取数据的性能
①构造方法
其成员方法与FileInputStream一致
⒈2字节缓冲输出流
BufferedOutputStream:提高字节流写入数据的性能
①构造方法
②常用成员方法
在基本流FileOutputStream的基础上多了一个flush手动刷新缓冲区
⒈3缓冲流close方法细节
在缓冲流的close方法底层会先自己关闭基本流,在关闭基本流之后再关闭自己的
2.字符缓冲流
字符流基本流的底层本来就有缓冲区,因此缓冲流对其的效率提升不是太高
但是其中特有的方法值得我们了解
⒉1字符缓冲输入流
BufferedReader
①构造方法
②常用成员方法
Ⅰ. readLine(特有方法)
String readLine() 读取一行数据,读到末尾返回null
方法细节:一次读取一行,遇到回车换行结束,但是回车换行不会读取,需要我们自己手动换行
⒉2字符缓冲输出流
BufferWrite
①构造方法
②常用成员方法
Ⅰ. newLine(特有方法)
void newLine() 跨平台的换行
不同操作系统的回车换行符不同
newLine常常会结合readLine方法使用
⑷转换流
转换流也是高级流,是字符流与字节流之间的桥梁
⒈字节转换输入流
InputStreamReader:是字节流通向字符流的桥梁,可以使用指定的charset(字符集)读取字节并将其解码为字符
①构造方法
② 常用成员方法
③利用指定的编码方式读取文件
如图:若我们所要读取的文件是属于GBK字符集的,那么利用idea默认的字符集读取是会出现乱码的,因此我们要指定GBK字符集读取
⒉字符转换输出流
OutputStreamWrite:是字符流通向字节流的桥梁,可以使用指定的charset(字符集)将要写入流中的字符编码为字节
①构造方法
②常用成员方法
③利用指定的字符编码写入数据
如图:我们使用GBK字符集写入数据
利用idea默认的字符集去查看文件,会发现写入的文件是乱码现象
⒊JDK11之后的字符编码读写数据
在JDK11之后为了提高读写的效率,字符流也提供了指定字符编码的性能
如图:对于FileReader和FileWrite,我们可以指定其字符编码方式去读写数据
①不同字符集编码文件的拷贝
如图:我们要利用GBK去读取文件,再利用UTF-8去拷贝文件
⑸序列化流与反序列化流
序列化流与反序列化流是高级流,属于字节流的一种。通过序列化流,我们可以将对象进行保密,通过反序列化流,我们可以将获取保密的对象
⒈序列化流
ObjectOutputStream:可以把对象序列化到本地文件中
①构造方法
②特有方法
③如何序列化对象
使用序列化流将对象写到文件时,需要让JavaBean类实现Serializable接口
其中Serializable接口里面没有抽象方法,属于标记型接口,实现了该接口,就表示当前类可以被序列化
如图:我们要将Student对象序列化,就要先在Student类中实现Serializable接口
然后将要序列化的对象写入本地文件
如图便是序列到本地文件的对象
注:序列化后的文件不能修改,若修改反序列化就会报错
④SerialVersionUID固定序列号
当序列化运行时,将基于JavaBean类的各个方面会计算该类默认的SerialVersionUID
若我们没有在JavaBean类中显示序列号,那么序列化对象后,当我们修改JavaBean类后,再反序列化获取对象时,程序就会报错,因为修改前后的JavaBean类序列号不同
那么如何让JavaBean类修改后,仍然可以序列化对象呢?
我们可以固定序列号,那么在修改JavaBean类时就不会再计算该类的序列号
⑤transient瞬态关键字
若在JavaBean中的一些成员变量的值,我们不想将它序列化到本地文件中,那么我们就可以将该成员变量用transient修饰
transient瞬态关键字:不会将当前属性序列化到本地文件中
如图:在JavaBean类中我们将address瞬态修饰
然后我们将下面的对象序列化到本地文件当中
我们再打印出反序列的对象,可以看到我们transient修饰的属性并没有被写到文件中
⒉反序列化流
ObjectInputStream:将序列化的对象读取到程序中
①构造方法
②特有方法
③如何反序列化对象
如图:利用readObject方法读取数据对象存入到集合中来
⑹打印流
打印流属于高级流,只操作文件的目的地不操作数据源
使用打印流写入数据,我们可以方便的打印各种数据值表示形式,以及数据的自动刷新与自动换行
⒈字节打印流
PrintStream :字节打印流
①构造方法
Ⅰ. PrintStream(File, OutputStream, String) 不带自动刷新的打印流
Ⅱ.PrintStream(OutputStream out,boolean autoFlush) 可以规定是否自动刷新的打印流
Ⅲ.PrintStream(OutputStream out,boolean autoFlush,String encoding) 可以指定编码方式与自动刷新的打印流
注:因为字节流的底层是没有缓冲区的,自动刷新对于字节流来说没有影响
②特有方法
常用成员方法与字节流一样
如图演示:我们创建字节打印流的对象并规定编码方式,分别利用write,println, print已经printf方法写入数据
⒉字符打印流
PrintWrite:字符打印流
字符流底层有缓冲区,因此相比于字节打印流,字符打印流自动刷新很有必要
字符打印流构造方法与字节打印流一致
特有方法与字节打印流一样
常见成员方法与字符流一样
⒊打印流与输出语句sout
学习完打印流,我们发现打印流中的特有方法与输出语句好像啊,那他们俩之间有什么关系吗?
输出语句: System.out.println()
我们到System类中查找到out变量,发现他是静态的打印流对象
那么我们就可以手动获取out对象并调用其中的方法使用
细节:
①利用System.out获取打印流对象,此打印流由虚拟机创建,默认执向控制台
②该流在系统中是唯一的不能被关闭,否则下面将无法打印语句
⑺压缩流与解压缩流
压缩流与解压缩流是高级流,也是字节流的一种
当我们要传输的文件比较大时,我们可以先压缩再传输,当我们接受到文件时再去解压缩文件
压缩包文件以.zip为后缀,压缩包里的每一个文件在java中是一个ZipEntry对象
⒈压缩流
ZipOutputStream:以ZIP文件格式写入文件实现输出流过滤器
①构造方法
②特有方法
③如何压缩文件夹
压缩的本质就是把每一个文件或文件夹转换为Zipr对象放入到压缩包中
如图:压缩到的bbb文件路径后缀为.zip,文件夹bbb里面的文件名不变
要注意的是,当我们把文件转换为ZipEntry对象时,ZipEntry对象的参数就是压缩包内部的路径
所以在递归中,我们需要传递一个参数name,方便我们以后去规定ZipEntry的路径
如图:我们获取到压缩文件中的所有内容并遍历
若是单级文件则将该文件转换为ZipEntry对象并加入压缩包中
若是多级文件,则递归
⒉解压缩流
ZipInputStream:读取ZIP文件格式的文件实现输入流过滤器
①构造方法
②特有方法
③如何解压文件
解压的本质就是把每一个ZipEntry对象按照层级拷贝到本地的另一个文件当中
如图:首先我们要创建解压缩的对象并获取压缩包里的每一个ZipEntry对象
因为单级文件可以拷贝
若此ZipEntry对象是多级文件,那么我们就在压缩的目的地创建一个一样的文件夹并向下寻找单级文件
若此ZipEntry对象是单级文件,那么我们就指定路径进行拷贝
注:我们同样可以使用缓冲流提高拷贝效率