IO流概述
什么是IO流:存储和读取数据的解决方案
IO流的作用:用于读写数据(本地文件,网络)
IO流按照流向可以分类哪两种流:
输出流:程序 ——> 文件 (写出)
输入流: 文件 ——> 程序 (读入)
IO流按照操作文件的类型可以分为哪两种流?
字节流:可以操作所有类型的文件
字符流:只能操作纯文本文件
什么是纯文本文件?
用windows系统自带的记事本打开并且能读懂的文件,如txt文件、md文件、xml文件、lrc文件
IO流的体系
基本流实现类
字节流
FileOutputStream字节输出流
操作本地文件的字节输出流,可以把程序中的数据写到本地文件中
书写步骤
1、创建字节输出流对象
FileOutputStream fos = new FileOutputStream("C:\\a.txt");
2、写数据
fos.write(97);
3、释放资源
fos.close();
字节输出流的细节
1、创建字节输出流对象
细节1:参数是字符串表示的路径或者是File对象都是可以的
细节2:如果文件不存在会创建一个新的文件,但是要保证父级路 径是存在的
细节3:如果文件已经存在,则会清空文件
2、写数据
细节:write方法的参数是整数,但是实际上写到本地文件中的是 整数在ASCII上对应的字符
97 --> a 100 --> d
3、释放资源
每次使用完流之后都要释放资源
写出数据的三种方式
写数据的两个小问题
换行写
再次写一个换行符就可以了
windows:\r\n
Linux: \n
Mac: \r
细节:
在windows操作系统中,Java对回车换行进行了优化。虽然完整的是\r\n,但是我们写其中一个\r或\n,java也可以实现换行,因为java在底层会补全。
续写
如果想要续写,打开续写开关即可。
开关位置:创建对象的第二个参数
默认false:表示关闭续写,此时创建对象会清空文件
手动传递true:表示打开续写,此时创建对象不会清空文件
FileInputStream字节输入流
书写步骤
1、创建字节输入流对象
FileInputStream fis = new FileInputStream("C:\\a.txt");
2、读数据
fis.read();
3、释放资源
fis.close();
字节输入流的细节
1、创建字节输入流对象
细节1:如果文件不存在,就直接报错。
Java为什么会这么设计 (直接报错而不是创建一个文件) ?
因为创建出来的文件是没有数据的,没有任何意义,所 以为文件不存在直接报错。
2、读数据
细节1:一次读一个字节,读出来的是数据在ASCII上对应的数字
细节2:读到文件末尾了,read方法返回-1.
3、释放资源
每次使用完流之后都要释放资源
循环读取
//创建对象 FileInputStream fis = new FileInputStream("day01-code\a.txt"); //循环读数据 int b; while((b = fis.read()) != -1){ System.out.println(b); } //释放资源 fis.close();
返回值:本次读取到了多少个字节数据
文件拷贝
//创建对象 FileInputStream fis = new FileInputStream("day01-code\a.txt");
FileOutputStream fos = new FileOutputStream("day01-code\a.txt");
//拷贝
//核心思想:边读边写
int b; while((b = fis.read()) != -1){ fos.write(b); } //释放资源
//规则:先开的后关
fos.close();
fis.close();
字节流的弊端
一次只能读取一个字节,容易产生乱码现象
字符流
字符流的底层其实就是字节流
字符流 = 字节流 + 字符集
特点
输入流:一次读一个字节,遇到中文时,一次读多个字节
输出流:底层会把数据按照指定的编码方式进行编码,变成字节再写 到文件中
使用场景
对于纯文本文件进行读写操作
FileReader字符输入流
书写步骤
1、创建字符输入流对象
FileReader fr = new FileReader("C:\\a.txt");
2、读取数据
fr.read();
细节:
字符流的底层也是字节流,默认也是一个字节一个字节的读取的
如果遇到中文就会一次读取多个,GBK一次读两个字节,UTF-8一次读三个字节
read()细节:
1、read():默认也是一个字节一个字节的读取的,如果遇到中文就会一次读取多个
2、在读取之后,方法的底层换会进行解码并转成十进制。最终把这个十进制最为返回值。这个十进制的数据也表示再字符集上的数字。
英文:文件里面二进制数据 0110 0001
read方法进行读取,解码并转成十进制97
中文:文件里面的二进制数据 11100110 10110001 10001001
read方法进行读取,解码并转成十进制27721
想看到中文汉字,就是把这些十进制数据再进行强转就可以了
read(char[ ] buffer)细节:
读取数据、解码、强转三步合并了,把强转之后的字符放到数组当中
3、释放资源
fr.close();
FileWriter字符输出流
FileWriter书写细节:
1、创建字符输出流对象
细节1:参数是字符串表示的路径或者是File对象都是可以的
细节2:如果文件不存在会创建一个新的文件,但是要保证父级路 径是存在的
细节3:如果文件已经存在,则会清空文件,如果不想清空可以打 开续写开关
2、写数据
细节:write方法的参数是整数,但是实际上写到本地文件中的是 整数在字符集上对应的字符
3、释放资源
每次使用完流之后都要释放资源
高级流
缓冲流
字节缓冲流
原理:底层自带了长度为8192的缓冲区提高性能
字符缓冲流
原理:底层自带了长度为8192的缓冲区提高性能
转换流
作用:是字符流和字节流之间的桥梁
使用场景:
字节流想要使用字符流中的方法
序列化流
序列化流/对象操作输出流(ObjectOutputStream)
作用:可以把Java中的对象写到本地文件中
细节:
使用对象输出流将对象保存到文件时会出现NotSerializableException异常
解决方案:需要让JavaBean类实现serializable接口
反序列化流
反序列化流/对象操作输入流(ObjectInputStream)
序列化流/反序列化流的细节汇总:
打印流
特点1:打印流只操作文件目的地,不操作数据源
特点2:特有的写出方法可以实现数据原样写出
特点3:特有的写出方法,可以实现自动刷新,自动换行
打印一次数据 = 写出 + 换行 + 刷新
字节打印流
字符打印流
字节打印流:默认自动刷新,特有的println自动换行
字符打印流:自动刷新需要开启,特有的println自动换行
压缩流/解压缩流
解压缩流(ZipInputStream)
解压本质:把每一个ZipEntry按照层级拷贝到本地另一个文件夹中
压缩流(ZipOutputStream)
压缩本质:把每一个(文件/文件夹)看成zipEntry对象放到压缩包中
字符集详解
总结
1、在计算机中,任意数据都是以二进制的形式来存储的
2、计算机中最小的存储单元是一个字节
3、ASCII字符集中,一个英文占一个字节
4、简体中文版Windows,默认使用GBK字符集
5、GBK字符集完全兼容ASCII字符集
一个英文占一个字节,二进制第一位是0
一个中文占两个字节,二进制高位字节的第一位是1
Unicode字符集(万国码)的UTF-8编码格式
UTF-16编码规则:用2~4个字节保存
UTF-32编码规则:固定使用4个字节保存
UTF-8编码规则:用1~4个字节保存
一个英文占一个字节,二进制第一位是0,转成十进制是正数
一个中文占三个字节,二进制第一位是1,第一个字节转成十进制是负数
为什么会有乱码?
原因一:读取数据时未读完整个汉字
字节流:一次读取一个字节
原因二:编码和解码的方式不统一
如何不产生乱码?
1、不要用字节流读取文本文件
2、编码解码时使用同一个码表,同一个编码方式
疑问:字节流读取中文会乱码,但是为什么拷贝不会乱码呢?
因为在拷贝的时候,数据没有丢失,记事本本身在读取文件的时候要用的字符集和编码方式跟数据源保持一致
java中编码和解码的代码实现
·
常用工具包
Commons-io
作用:提高IO流的开发效率
使用步骤
常见方法