本章内容:
1.IO概述
2.字节流
3.字符流
4.文件异常的处理
1.IO概述
- 什么是IO
生活中,你肯定经历过这样的场景。当你编辑一个文本文件,忘记了ctrl+s
,可能文件就白白编辑了。当你电脑上插入一个U盘,可以把一个视频,拷贝到你的电脑硬盘里。那么数据都是在哪些设备上的呢?键盘、内存、硬盘、外接设备等等。
我们把这种数据的传输,可以看做是一种数据的流动,按照流动的方向,以内存为基准,分为输入input
和输出output
,即流向内存是输入流,流出内存的输出流。
Java中I/O操作主要是指使用java.io
包下的内容,进行输入、输出操作。输入也叫做读取数据,输出也叫做作写出数据。
- IO的分类
根据数据的流向分为:输入流和输出流。
- 输入流*:把数据从
其他设备
上读取到内存
中的流。 - 输出流*:把数据从
内存
中写出到其他设备
上的流。
根据数据的类型分为:字节流和字符流。
- 字节流:以字节为单位,读写数据的流
- 字符流:以字符为单位,读写数据的流
- 顶级父类们
输入流 | 输出流 | |
---|---|---|
字节流 | 字节输入流 InputStream |
字节输出流 OutputStream |
字符流 | 字符输入流 Reader |
字符输出流 Writer |
2.字节流
一切皆为字节(一切皆字节,让我联想到了在python和js中,一切皆对象;在资本博弈中,一切皆营销)
一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都一个一个的字节,那么传输时一样如此。所以,字节流可以传输任意文件数据。在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底层传输的始终为二进制数据。
字节流包含InputStream和OutputStream,这两类又是所有字节流的父类。
InputStream类
字节输入流InputStream又包含类诸多子类字节流:FileInputStream(写入文件的输入流)、PipedInputStream(输入管道)、FilterInputStream(过滤输出流)。。。
FileInputStream
java.io.FileInputStream 类是文件输入流,从文件中读取字节到内存中。
方法创建
- FileInputStream(File file):通过打开与实际文件的连接来创建一个 FileInputStream,该文件由文件系统中的 File对象 file命名
- FileInputStream(String name):通过打开与实际文件的连接来创建一个 FileInputStream,该文件由文件系统中的路径名 name命名
当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有该文件,会抛出 FileNotFoundException 。
1 | // 使用File对象创建流对象 |
常用方法
方法 | 描述 |
---|---|
void close() | 关闭输入流 |
void mark() | 标记输入流的当前位置 |
void reset() | 将读取位置返回到标记处 |
int read() | 从输入流中当前位置读入一个字节的二进制数据,以此数据为低位字节,补足16位的整型量(0~255)后返回,若输入流中当前位置没有数据,则返回-1 |
int read(byte b[]) | 从输入流中的当前位置连续读入多个字节保存在数组中,并返回所读取的字节数 |
int read(byte b[], int off, int len) | 从输入流中当前位置连续读len长的字节,从数组第off+1个元素位置处开始存放,并返回所读取的字节数 |
int available() | 返回输入流中可以读取的字节数 |
1 | public class FISRead { |
PS:使用数组读取,每次读取多个字节,减少了系统间的IO操作次数,从而提高了读写的效率,建议开发中使用。
OutputStream类
java.io.OutputStream 抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地,它定义了字节输出流的基本共性功能方法。FileOutputStream、PipedOutputStream、FilterOutputStream
- void close():关闭此输出流并释放与此流相关联的任何系统资源
- void flush():刷新此输出流并强制任何缓冲的输出字节被写出
- void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流
- void write(byte[] b, int offset, int len):从指定的字节数组写入 len字节,从偏移量 offset开始输出到此输出流
- abstract void write(int b):将指定的字节输出流
注:close方法,当完成流的操作时,必须调用此方法,释放系统资源
FileOutputStream
OutputStream超类有很多的子类,FileOutputStream类也是超类中的的一个简单子类,表示文件输出流,用于将数据写出到文件
创建FileOutputStream
- FileOutputStream(File file):创建文件输出流以写入由指定的 File对象表示的文件。
- FileOutputStream(String name):创建文件输出流以指定的名称写入文件(推荐使用相对路径)。
当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有这个文件,会自动创建该文件;如果文件存在,则会清空这个文件的数据。
常用方法
- close():关闭输出流
- flush():强制清空缓冲区并执行向外设输出数据
- write(int b):b表示为一个ASCII码值的字节,每次写入一个字节数据(a-97 A-65)
- write(byte[] b):b表示为一个字节型数组,每次写入数组中的数据
- write(byte[] b, int offset, int len):写入指定长度的字节数组,起始索引为offset(默认为0),长度为len字节
- 调用string.getBytes()方法将字符串转换为字节数组 byte[] b = “hello world”.getBytes();
1 | import java.io.File; |
数据追加续写:
经过以上的演示,每次程序运行,创建输出流对象,都会清空目标文件中的数据。如何保留目标文件中数据,还能继续添加新数据呢?
1 | public FileOutputStream(File file, boolean append):创建文件输出流以写入由指定的 File对象表示的文件 |
这两个构造方法,参数中都需要传入一个boolean类型的append值,true
表示追加数据,false
表示清空原有数据。这样创建的输出流对象,就可以指定是否追加续写了
写出换行:
- 回车符
\r
和换行符\n
:- 回车符:回到一行的开头(return)
- 换行符:下一行(newline)
- 系统中的换行:
- Windows系统里,每行结尾是
回车+换行
,即\r\n
- Unix系统里,每行结尾只有
换行
,即\n
- Mac系统里,每行结尾是
回车
,即\r
。从 Mac OS X开始与Linux统一
- Windows系统里,每行结尾是
1 | fos.write(97); |
3.字符流
当使用字节流读取文本文件时,可能会有一个小问题。就是遇到中文字符时,可能不会显示完整的字符,那是因为一个中文字符可能占用多个字节存储。所以Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。
字符流包含Reader和Writer,这两类又是所有字符流的父类。
Reader
java.io.Reader
字符输入流,是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。
FileReader
java.io.FileReader
类是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
创建方式
- FileReader(File file):创建一个新的FileReader,给定要读取的File对象
- FileReader(String fileName):创建一个新的FileReader,给定要读取的文件的名称
当你创建一个流对象时,必须传入一个文件路径,类似于FileInputStream,如果文件不存在则会报错。
1 | // 使用File对象创建流对象 |
常用方法
- void close():关闭此流并释放与此流相关联的任何系统资源
- int read():从输入流读取一个字符
- int read(char[] ch):从输入流中读取字符数组
读取字符数据:
- read()方法读取字符:每次可以读取一个字符的数据,提升为int类型,读取到文件末尾,返回
-1
,循环读取,代码使用演示:
1 | import java.io.FileReader; |
- read(char[] ch)使用字符数组读取文件,每次读取b的长度个字符到数组中,返回读取到的有效字符个数,读取到末尾时,返回
-1
,代码使用演示
1 | public class FISRead { |
Writer
java.io.Writer抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。它定义了字节输出流的基本共性功能方法。
FileWrite类
Java.io.FileWriter类是写出字符到文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
创建方式
1 | FileWriter(File file):创建一个新的 FileWriter,给定要读取的File对象 |
当你创建一个流对象时,必须传入一个文件路径,类似于FileOutputStream。构造举例,代码如下:
1 | // 使用File对象创建流对象 |
常用方法
- void write(int c):写入单个字符
- void write(char[] ch):写入字符数组
- void write(char[] ch, int off, int len):写入字符数组的某一部分,off数组的开始索引,len写的字符个数
- void write(String str):写入字符串
- void write(String str, int off, int len):写入字符串的某一部分,off字符串的开始索引,len写的字符个数
- void flush():刷新该流的缓冲
- void close():关闭此流,但要先刷新它
数据写入:
- write(int b):每次可以写出一个字符数据
1 | public class FWWrite { |
小结:
- 字符流,只能操作文本文件,不能操作图片,视频等非文本文件。
- 当我们单纯读或者写文本文件时使用字符流,其他情况使用字节流
- java的字节字符流调用close方法会清空缓存区的内容,也就是flush一下,不过为了保险起见,应该先调用flush()再close()
- flush():刷新缓冲区,流对象可以继续使用,还可以继续读写
- close():先刷新缓存区,告知系统关闭资源,流对象不再使用
4.文件异常的处理
之前的入门练习,我们一直把异常抛出,而实际开发中并不能这样处理,建议使用try...catch...finally
代码块,处理异常部分,代码使用演示:
java的io异常一定要用try…catch…finally,因为直接throws抛出异常是交给jvm,jvm直接中断程序运行,这会导致流对象无法关闭,会占用资源,这是要避免的
1 | public class HandleException1 { |