IO流
1.File类只能对文件本身进行操作,不能读取里面存储的数据。
2.IO流:存储和读取数据的解决方案,用于读写文件中的数据(可以读写文件,或网络中的数据...)。
3.IO流的分类:
- 按流向分,分为输出流(从程序到文件)和输入流(从文件到程序)
- 按操作文件的类型分,分为字节流(可以操作所有类型的文件)和字符流(只能操作纯文本文件)
- 纯文本文件:可以使用Windows自带的记事本打开且内容不会发生乱码即为纯文本文件,如txt文件,md文件,xml文件,lrc文件等
4.创建关闭的时机:随用随创建,不用就关闭。
IO体系示意图
字节流
1.InputStream为字节输入流,OutputStream为字节输出流,这两类都是抽象类,因此需要写继承其的子类。
2.子类的命名一般分为两部分,第一部分表示其作用,第二部分直接使用父类全程,表明继承的父类。
3.不要使用字节流去读取文本文件,不然容易导致数据读取不完整,导致乱码,但是拷贝文件不会导致乱码,因为拷贝是将所有的数据都搬运过去,除非出现编码和解码方式不一致。
4.字节流可以拷贝任意类型的文件。
字节输出流的子类FileOutputStream(基本流)
1.操作本地文件的字节输出流 ,可以把程序中的数据写道本地文件中。
2.书写步骤:
- 创建字节输出流对象(构建程序与文件的通道);
- 写数据(通过通道传输);
- 释放资源(关闭通道)。
3.创建对象时:
- 创建对象有两种方法,但都是带参构造,第一种只传递路径或者File对象,第二种传递路径或者File对象还有一个布尔值,这个布尔值决定文件是否续写(true续写,false不续写),如果使用第一种方法,则默认不续写
- 参数是字符串表示的路径或者是File对象都是可以的
- 如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的
- 如果文件已经存在,且续写没打开,则会清空(是清空还是覆盖)文件
4.写数据:
- 使用方法write
- write方法的参数是整数,但是实际上写到本地文件中的是整数在ASCII上对应的字符
- void write(byte[] b,int off,int len)中第一个参数是数据数组,第二个参数是开始索引,第三个参数是写入数据的个数
- 如果想一次性写入一串字符串,可以先通过String的getBytes()方法返回一个字符数组,再调用write方法
- 换行:
如果想要换行,只需要写入换行符即可:
Windows:\r\n
Linux:\n
Mac:\r
可以只写一个\r或者\n,Java底层会补全,但是建议不省略。
方法名 | 说明 |
void write(int b) | 一次写一个字节数据 |
void write(byte[] b) | 一次写一个字节数组数据 |
void write(byte[] b,int off,int len) | 一次写一个字节数组的部分数据 |
5.释放资源:
- 使用方法close()释放资源
- 如果不释放资源,那么该文件会被占用,不能被其它人操作
- 一般是先开的通道最后关闭
字节输入流的子类FileInputStream(基本流)
1.FileInputStream可以操作本地文件的字节输入流,可以把本地文件中的数据读取到程序中来。
2.书写步骤:
- 创建字节输入流对象(创建通道)
- 读取数据
- 释放资源(关闭通道)
3.创建对象时:如果文件不存在,就直接报错。
4.读取数据时:
- 使用read方法
- 读到文件末尾了,read方法返回-1
- 在创建字节数组时,一般都会创建大小为1024整数倍
- 在使用带参的read方法时,如果希望输出刚读取到的数据,可以用new String(bytes,0,len),因为读取到的数据都会从字节数组的0索引开始放入,如果将字节数组全部转化为字符串,可能会有部分数据来源于之前读取的结果
方法名 | 说明 |
public int read() | 一次读取一个字节数据 |
public int read(byte[] buffer) | 一次读一个字节数组数据(每次读取会尽可能把数组装满),返回读取字节数据的个数,如果读不到数据,返回-1 |
5.释放资源:
- 使用方法close()释放资源
- 如果不释放资源,那么该文件会被占用,不能被其它人操作
- 一般是先开的通道最后关闭
字符流
1.字符流的底层其实就是字节流:字符流 = 字节流 + 字符集。
2.特点:
- 输入流:一次读一个字节,遇到中文时,一次读多个字节
- 输出流:底层会把数据按照指定的编码方式进行编码,变成字节再写到文件中
3.使用场景:对于纯文本文件进行读写操作。
4.字符流不能用于拷贝,因为无参的read方法太慢了,带参的read方法不适合。
字符输入流的子类FileReader(基本流)
1.操作本地文件的字符输入流FileReader。
2.操作步骤:
- 创建字符输入流对象
- 读取数据
- 释放资源
3.构造方法:
构造方法 | 说明 |
public FileReader(File file) | 创建字符输入流关联本地文件 |
public FileReader(String pathname) | 创建字符输入流关联本地文件 |
如果文件不存在,直接报错。
4.读取数据:
- 按字节进行读取,遇到中文,一次读取多个字节,读取后解码,返回一个整数(如果是带参的read方法,返回的就是读取到的数据个数,如果是无参的,返回的就是对应的ASCII码)
- 使用无参的read方法,在读取之后,方法的底层还会将二进制解码并转换成十进制,如果想要获得字母或者汉字,还需要对获得的十进制进行强转(强转为char)
- 使用带参的read方法,将读取数据,解码,强转三步进行合并,并把强转之后的字符放到数组当中
成员方法 | 说明 |
public int read() | 读取数据,读到末尾返回-1 |
public int read(char[] buffer) | 读取多个数据,如果本次读取到了数据,返回读取到的数据的个数,如果没有读到数据并且读到末尾返回-1 |
5.释放资源:
成员方法 | 说明 |
public int close() | 释放资源/关流 |
字符输出流的子类FileWriter(基本流)
1.操作步骤:
- 创建字符输出流对象
- 写数据
- 释放资源
2.构造方法:
- 参数是字符串表示的路径或者File对象都是可以的
- 如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的
- 如果文件已经存在,则会清空文件,如果不想情况可以打开续写开关
构造方法 | 说明 |
public FileWriter(File file) | 创建字符输出流关联本地文件 |
public FileWriter(String pathname) | 创建字符输出流关联本地文件 |
public FileWriter(File file,boolean append) | 创建字符输出流关联本地文件,续写 |
public FileWriter(String pathname,boolean append) | 创建字符输出流关联本地文件,续写 |
3.写数据:当需要写出一个数字到文件时,要先将其转换为字符串再输出!
方法名 | 说明 |
void write(int c) | 写出一个字符 |
void write(String str) | 写出一个字符串 |
void write(String str,int off,int len) | 写出一个字符串的一部分 |
void write(char[] cbuf) | 写出一个字符数组 |
void write(char[] cbuf,int off,int len) | 写出字符数组的一部分 |
字符输入流原理解析
1.创建字符输入流对象:底层会关联文件,并且创建缓冲区(长度为8192的字节数组)。
2.读取数据:底层:
- 判断缓冲区中是否有数据可以读取
- 缓冲区如果没有数据,那就从文件中获取数据,装到缓冲区中,每次尽可能装满缓冲区(是在第一次读取文件的时候就会将缓冲区尽量装满,不是读取一次就装对应长度的数据);如果文件中也没有数据了,返回-1
- 缓冲区如果有数据,就从缓冲区中读取
- 如果缓冲区满了,而文件中仍有数据,则会在第一次读取完缓冲区的数据后,再次读取数据时将剩下的内容放到缓冲区中覆盖原有的内容,直至读取完所有的数据为止
- 空参的read方法:一次读取一个字节,遇到中文一次都多个字节,把字节解码并转成十进制返回
- 有参的read方法:把读取字节,解码,强转三步合并了,强转之后的字符放到数组中
字符输出流原理解析
1.创建字符输出流对象:底层会关联文件,并且创建缓冲区(长度为8192的字节数组)。
2.写入数据时:
- 先往缓冲区中写入数据
- 若缓冲区满、手动使用方法flush()刷新或者释放资源(使用close方法)都可以将缓冲区内容保存到文件中
- 不同的是缓冲区满和flush方法不会将通道破坏,在保存后还能继续写入数据