什么是IO流
存储和读取数据的解决方案
I:input:输入
O:output:输出
流:像水流一样传输数据
IO流的作用
用于读取数据(本地文件,网络)
IO流的分类
流的方向:
输入流:从文件或其他外部源读取数据到程序中(内存)
输出流:将程序中的数据写入文件或其他外部目标
文件——输入——程序———输出——文件
IO流操作文件的类型:
字节流:可以操作所有类型的文件
纯文本文件:只能操作纯文本文件(Windows自带的记事本打开能读懂的就可以用字符流操作)
IO流的体系:
字节流:(可以读取和写入所有类型的文件)
体系:
FileInputStream类(输入流,文件-程序)
1:创建对象
2:读取数据
3:释放数据
注意:
1:如果文件不存在直接报错
2:读取数据一次读一个 字节,读出来的是数据在ASCll上对应的数字
3:读到文件末尾了,read方法返回-1(读一次移一次,读到末尾返回-1)
循环读取:
FileOutputStream类(输出流,程序-文件)
使用步骤:
1:创建对象 (程序-文件建立通道)
2:输出数据(利用通道传输 数据)
3:释放数据(把通道断掉)
注意:
1:参数是字符串表示的路径或者是File对象都是可以的
2:如果文件不存在则会创建一个,但是要保证父级路径存在(父级路径不存在则会报错)
3:如果文件已存在,则会清空文件,创建一个新的
4:write写入的数据是整数,但是实际上写到本地文件是整数对应的ASCII表上的字符
5:每次使用完流之后都在释放资源(如果不释放时打不开这个文件的,因为Java正在操作这个文件)
FileOutputStream写数据的三种方式:
换行和续写
换行符:
windows:\r\n
Linux:\n
Mac:\r
续写:在创建对象构造方法的第二个参数写一个true打开续写(打开续写创建对象时就不会清空文件了,就接着写)
练习:
文件拷贝:
先读取(输入流)在写入(输出流)
文件拷贝改进版本:
字符集:
ASCll字符集:
规则1:汉字是两个字节存储的,高位字节二进制一定以1开头,转成10进制之后是一个负数(GBK编码:每个汉字由2个字节组成,且两个字节的最高位(首位)均为1)
规则2:英文是一个字节存储的,兼容ASCll,二进制前面补0
Unicode字符集:万国码
美国:ASCll字符集
中国:GBK字符集(兼容ASCll字符集)
其他国家还有很多字符集
Unicode字符集是把这些国家的字符集统一,并且一般都使用UTF-8编码标准
英文以0未开头,占一个字节,转成十进制是正数
中文以1110为开头,占三个字节 ,转成10进制是负数
乱码:
读取数据时未都完整整个汉字(UTF-8在编码时确实会将字符转换为多个字节,但解码时需要一个完整的字节序列。如果输入流在读取时被分割在不正确的字节边界上,解码器无法正确识别字符,从而产生乱码)
编码和解码时的方式不统一
编码:
把数据编码成二进制的形式并存到数组当中,打印的是十进制的形式,这是java底层默认的
解码:
把二进制解码成数据原来的样子
字符流:
FileReader(字节输入流):
他也是一次读一个字节,但是遇到中文一次读多个字节
读取完字节解码成十进制,这个十进制正好十字符集上的,并进行返回,如果你想看到中文汉字,在将这些十进制数据进行强转
FileWrite(字节输出流):
write:先进行编码,把编码后的数据写到文件中,文件在解码显示出来
字节输入流
1:无参read:一次读一个字节,并返回这个字节的十进制形式返回,没有则返回-1
2:有参read:一次读取一个字节或者多个字节(数组的前提下),并存到数组当中,返回值是读取了多少个字节,没有则返回-1,(这里的二进制就是返回的十进制对应的)
字节输出流
1:如果是整数写的是这个整数二进制的低八位,如果是字符会强转成int类型的,然后再截取低八位
字符输入流(FileReader)的底层原理:
1:关联文件,并创建缓冲区(长度为8192的字节的数组)
2:缓冲区没有数据,就从文件中获取数据,装到缓冲区中,每次尽可能装满缓冲区,如果文件中也没有数据了,返回-1
3:缓冲区有数据,就从缓冲区读取数据,并自动解码成十进制(这里的十进制就是字符集里面字符对应的数字)返回
空参read:一次读取一个字节,遇到中文一次读取多个字节,把字节自动解码成十进制(这里的十进制就是字符集里面字符对应的数字)进行返回
有参read:把读取字节,解码,强转三步合一,强转后的字符放到数组当中,返回读取字符的个数
字符输出流(FileWrite)的底层原理:
1:关联文件,并创建缓冲区(长度为8192的字节的数组)
2:写出数据时,先把数据编码写到缓冲区,在把缓冲区的编码后的数据写到文件当中
3:如果缓冲区装满了自动把所有的数据写到文件当中
4:如果关流会检查缓冲区里面有没有数据,然后再把缓冲区的数据写到文件当中
5:如果使用flush会将缓冲区中的数据刷新到文件当中(调用 flush()
后,缓冲区会被清空,数据被持久化到文件中)
6:传入的整数参数会被视为 字符的 Unicode 码点(即字符在 Unicode 编码表中的位置),写入65就是写入的是A
字节流和字符流的使用场景
字节流:拷贝任意的文件
字符流:读取和写出纯文本文件的数据
练习:
1:拷贝一个文件夹,考虑子文件夹
思路分析:
1:遍历文件夹下面的数据
2:判断是否为文件或者为文件夹,如果为文件拷贝,如果为文件夹递推
代码分析:
1:我们首先遍历拷贝的这个文件下的所有文件
2:如果是文件,则就拷贝到目的的文件当中
3:如果是文件夹,则就递归,如果这个文件夹在目的文件夹里面没有则就创建一个,如果有则创建失败但是不会报错,下面的代码还会继续执行
2:文件加密和解密
3:修改文件中的数据并进行排序
字节缓冲流:
字节缓冲输入流和字节缓冲输出流的创建和关闭:
字节缓冲输入流和字节缓冲输出流的一个字节一个字节的读取和多个字节读取:
字节缓存流的读写原理:
缓存区先向文件中一次读取8192个字节,文件如果没有8192个字节,则有多少装多少,然后利用变量b去往缓冲区进行读取并写到缓冲输出流的缓冲区中,如果缓冲输出流的缓冲区装满了则会自动写入到目的地中,如果关流会检查缓冲区里面有没有数据,然后再把缓冲区的数据写到文件当中,数组是一样的原理
字符缓冲流:
字符缓冲输入流,创建对象并使用特有的方法输出
字符缓冲输出流,创建对象并使用特有的方法输出
转换流
作用:
1:指定字符集读写数据(已淘汰)
2:字节流想用字符集里面的方法
转换输入流:把字节输入流转换成字符输入流
转换输出流:把字符输出流转换成字节输出流
序列化流和反序列化流:
可以把java中的对象写到本地文件当中,写出的这个对象要实现Serializable这个接口作为一个标记,说明这个对象可以被序列化写入文件当中
注意:我们把对象写到文件中,我们在读取之前修改了这个对象里面的属性值,那么将读取失败,这是因为属性变量修改后,序列号也发生了修改
这时我们要给他一个固定的序列号
如果我们不想把里面的属性写到文件中,我们要给属性加一个关键字,transient,这时我们打印出来的id属性将不会显示
总结:
1:我们要将对象写到文件时,我们要将对象实现Serializable这个接口,只有实现这个接口我们才能将对象给序列化写入文件当中,否则不行
2:我们把对象读取到文件当中,文件中的数据不能修改,一旦修改就不能反序列化读取这个对象了
3:我们把对象序列化后写进文件当中,然后修改对象里面的属性值,然后在反序列化,序列号会改变,这时我们要给这个对象固定序列号
4:如果我们不想让对象里面的属性序列化到文件中,我们可以在属性前面加上transient关键字
练习:
读写多个对象
1:创建对象并实现Serializable这个接口
2:生成标准的javbeen
3:生成固定ID
4:创建对象
5:把创建的对象加入到集合当中,把集合给写入到文件(集合本身就已经序列化了),然后读取集合
字节打印流:PrintStream
无缓冲区,自动自动刷新,无需手动开启
字符打印流:PrintWriter
有缓冲区,需要手动开启自动刷新,若不开启则会等流结束后才把数据写到文件当中
解压缩流(输入流,读取压缩包中的文件)和压缩流(输出流,把文件写到压缩包中)
解压缩流:
压缩流: