IO流简介
常用流总览
1、字节流
1.1 字节输入流:InputStream
InputStream
是顶层抽象父类不能直接创建对象,一般使用它的子类FileInputStream
来创建文件字节输入流对象,其他常用子类:BufferedInputStream
、ObjectInputStream
。
常用方法子类继承:
- void close():关闭字节流
- int read():读取单个字节,返回值为读取到的字节
- int read(byte[]):读取多个字节,返回值是读取到的字节长度,读取到的内容存入byte[]数组中
read读取到文件结束标志返回-1
1.2 字节输出流:OutputStream
OutputStream
是顶层抽象父类一样的不能直接创建对象,需要使用实现子类来创建出对象,常用子类包括:FileOutputStream
、BufferedOutputStream
、ObjectOutputStream
、PrintStream
。
子类继承使用的常用方法:
- void close():关闭字节流
- void write():写入单个字节。
- void write(byte[]):写入一个字节数组
- void write(byte[],int offset,int len):写入指定的字节数量。参数:byte[] bs: 字节数组,int start: 开始索引,int len: 字节的个数
1.1.1 字节文件输入流:FileInputStream
FileInputStream
是InputStream
的子类,一般用于使用读取字节方式读取文件,可以读取任意文件。
-
构造方法的参数:
- 使用
File
对象:FileInputStream(File file) - 使用
文件路径
字符串:FileInputStream(String name)
- 使用
-
一般使用步骤:
- 创建
FileInputStream
对象fis
,绑定源文件 - 调用
read
方法读取文件内容 - 关闭流,释放资源
- 创建
1.2.1 字节文件输出流:FileOutputStream
FileOutputStream是OutputStream的子类,一般用于直接将字节流写入文件中,可以写入任何文件
- 构造方法:
- 使用
File
对象:FileOutputStream(File file) : - 使用
文件路径
字符串:FileOutputStream(String path)
一般使用步骤:
- 创建FileOutputStream类对象,绑定目标文件
- 调用
write
方法把字节写入文件 - 释放资源
1.1.2 字节缓冲输入流:BufferedInputStream
InputStream
的子类,用于高效的读取字节流,内部定义了一个长度为8192的字节数组作为缓冲区,可以提高效率。
-
构造方法参数:
BufferedInputStream(InputStream in):参数传入
InputStream
的子类对象,一种多态的使用方法。 -
一般使用步骤:
1.创建`InputStream`类的子类`FileInputStream`对象,绑定源文件 2.创建高效字节输入流`BufferedInputStream`对象bis,传递`InputStream`类的子类`FileInputStream`对象 3.高效字节输入流`BufferedInputStream`对象`bis`调用`read`方法,读取字节 4.关闭流
最后关闭缓冲字节输入流的时候会自动关闭文件字节输入流,因此不需要再关闭文件字节输入流。
1.2.2 字节缓冲输出流:BufferedOutputStream
OutputStream
的子类,用于高效的写入字节流,内部定义了一个长度为8192的字节数组作为缓冲区,可以提高效率。一般在需要大写入大量字节的时候会体现出优势。
- 构造方法:
BufferedOutputStream(OutputStream out):参数OutputStream out
是一种多态用法可以传递OutputStream
子类对象。
-
特有方法:
void flush():把缓冲区中的字节刷新到目的地。 -
一般使用步骤:
1.创建高效字节输入流`BufferedOutputStream`对象bis,传递`OutputStream`类的子类对象 2.高效字节输入流`BufferedOutputStream`对象`bos`调用`write`方法,写入字节 3.可以手动的把缓冲区中字节刷新到文件,但推荐不要手动进行刷新,因为这样会影响效率。 4.释放资源,同时会自动释放其余的字节流对象。
1.1.3 反序列化流
反序列化流的作用就是把一个序列化为字节流的对象还原成原本的对象。
-
构造方法:
ObjectInputStream(InputStream is):传递的参数是一个输入流的子类,多态传参的一个运用。 -
常用方法:
Object readObject(): 读取文件中的一个对象,返回形式为Object类型
1.2.3 序列化流:ObjectOutputStream
序列化流的作用就是把一个对象转换为字节流。
-
构造方法:
ObjectInputStream(OutputStream is):传递的参数是一个输出流子类,多态形式的参数传递
-
常用方法:
writeObject(Object obj): 把参数对象obj写入到文件中
序列化与反序列化流总结
- 注意事项
- 一个类要想实现序列化和反序列化,必须实现接口
java.io.Serializable
,该接口中没有抽象方法,它仅仅是起到一个标记作用,所以也称作标记型接口。
- 使用规范
- 被static修饰的成员变量不能被序列化,属于类,不属于对象,所以不能被序列化
- 被transient修饰的成员变量,不能被序列化
- 序列化版本号
java在编译的时候会为每一个带有序列化标记的类生成一个序列化版本号
,同时这个序列化版本号
会被写入编译后的文件。
如果一个类的对象在序列化以后,这个对象的类发生了变动,那么在进行反序列化的时候会抛出异常,这是因为只要改变了java文件,再次编译的时候就会为这个类重新生成序列化版本号
,在反序列化的时候与原来的序列化版本号
相对比,如果发现便会抛出无法反序列化
的异常。
解决办法:
在java类中指定一个固定的序列号,这样,不管如何修改java类,都不需要重新计算序列号了
public static final long serialVersionUID = 42L;
1.2.4 打印流
- 构造方法:
PrintStream(File file):传入文件对象的方式。
PrintStream(String path):传入文件所在路径的方式。
- 特有方法:
print(…): 原样输出,不换行,不会抛出异常
println(…): 原样输出,换行,不会抛出异常
其中System.out就是一个打印流,目的地是屏幕。
并且System类中还有个方法set
,**static void setOut(PrintStream out)**可以重定向“标准”输出流的目的地,比如可以把在显示器输出的东西输出到文件。
2、字符流
为什么要使用字符流?
如果只考虑文件都是使用编译器默认编码utf-8编码的情况下。当文件中既存在英文又存在中文的时候,如果想要操作文件中的内容,只使用字节流来操作的话会出现乱码问题。
这是为什么呢?
因为在utf-8规则中中文使用3个字节编码,而英文还是使用1个字节编码,要加载纯中文或纯英文文本文件的话可以限定一次读取的字节数量,但读取中英文混合文本文件就不能提前限定字节流一次读取的字节数量,这样就会在英文和中文混合拼接的地方出现乱码。
那有没什么办法解决这个问题呢?
有,就是使用字符流。
2.1字符输入流:Reader
Reader是顶层抽象父类,不能直接创建对象,一般使用子类来创建对象使用,常用子类:FileReader
、BufferedReader
、InputStreamReader
常用方法子类继承:
- void close() :关闭此流并释放与此流相关联的任何系统资源。
- int read(): 从输入流读取一个字符。
- int read(char[] chs): 从输入流中读取一些字符,并将它们存储到字符数组 chs中
2.2 字符输出流:Writer
Writer是顶层抽象父类,不能直接创建对象,一般使用子类实现类来创建对象,常用子类:FileWriter
、BufferedWriter
、OutputStreamWrtier
等
常用方法:
- public abstract void close() :关闭此输出流并释放与此流相关联的任何系统资源。
- public abstract void flush() :刷新此输出流并强制任何缓冲的输出字符被写出。
- public void write(int c) :写出一个字符。
- public void write(char[] cbuf):写出一个字符数组
- public abstract void write(char[] b, int off, int len) :写出一个字符数组的一部分,从指定的字符数组写出 len字符,从偏移量 off开始输出到此输出流。
- public void write(String str) :写出一个字符串。
- public void write(String str, int off, int len) :写出一个字符串的一部分。
2.1.1 字符文件输入流:FileReader
- 构造方法:
- FileReader(File file):文件对象作为参数
- FileReader(String path):文件路径作为参数。
-
特有方法:void newLine(); 输入一个换行符。
-
使用步骤:
- 创建文件字符输入流FileReader对象,关联源文件
- 调用read方法读取文件内容
- 关闭流,释放资源
2.2.1 字符文件输出流:FileWriter
-
构造函数:
FileWriter(File file):文件对象作为参数
FileWriter(String file):文件路径作为参数 -
特有方法
-
使用步骤:
- 创建文件字符输出流FileWriter对象fw,绑定目标文件
- 调用write方法写出字符
- 可以使用flush()来刷新缓冲区内容到
- 释放资源
flush和close的区别?
(1)字节流不用执行flush操作,因为内部没有缓冲数组,只要执行write就是直接写入文件
(2)字符流内部有一个缓冲数组执行write方法时,内容先写到缓冲数组中
(3)flush是把内容真正的写入到文件中,执行flush后,可以继续执行write
(4)close方法内部,会先执行flush操作,把内容写入到文件中。 close后不能继续write写数据
续写和换行
(1)续写:
FileWriter(File file, boolean append)
FileWriter(String file, boolean append)
参数:
File/String类型的 文件路径
boolean append: true 续写,false 覆盖写;不指定第二个参数,默认为false 覆盖写
(2)换行
winows: \r\n
Linux/Unix: \n
2.1.2 字符缓冲输入流:BufferedReader
Reader的子类,用于高效的字符输入流,内部定义了一个长度为8192的字符数组,可以提高效率。
-
特有方法:
String readLine():读取一行文本,遇到文件结束标志返回null
,读到的一行文本中不包含换行符
-
构造方法:
public BufferedReader(Reader r):参数: Reader r 一般传递字符输入流抽象类的子类FileReader的对象
-
使用步骤:
- 创建Reader类的子类FileReader对象,绑定源文件
- 创建高效字符输入流BufferedReader对象br,传递Reader类的子类FileReader对象
- 高效字符输入流BufferedReader对象br调用readLine方法,读取一行字符
- 关闭流
- 注意事项:只需要关闭缓冲流,自动会关闭所关联的其他流对象
2.2.2 字符缓冲输出流:BufferedWriter
高效字节输出流写入文件目标文件底层定义个了一个长度为8192的数组,提高效率。
- 构造方法:
BufferedOutputStream(OutputStream out):参数根据多态性质传递字节输出流的抽象父类引用的子类对象
- 使用步骤:
- 创建Writer类的子类FileWriter对象,绑定目标文件
- 创建高效字符输入流BufferedWriter对象bw,传递Writer类的子类FileWriter对象
- 高效字符输入流BufferedWriter对象bw调用write方法
- 可以代用flush()方法把缓冲区内容刷新进入目标文件,一般不推荐这么使用因为这会影响效率
- 释放资源
2.3 转换流:InputStreamReder、OutputStreamWriter
-
都是字符流抽象父类的子类。
windows默认的编码方式为GBK,中文规定占2个字节,而在IDEA中默认的编码表为UTF-8,中文规定占3个字节。因为这样,当我们读取的文件中存在中文的时候就会出现乱码。要解决这个问题就需要使用转换流来自定义指定读取或者写入文件时候的字符向字节的转换或者字节向字符转换的编码方式。
大致情况就如上图所示。在计算中一切信息都是以字节的形式存储,我们人能看懂的字符,也是通过一个叫编码表的映射表来作为对照,之后一一对应的不然的话不能读出我们想要的结果。可是目前编码的方式有很多,ASCII、GBK、IOSxxx、UNICODE等多种方式,因此对照不同的编码表规则也就会编码出不同的字节,解码也会产生出不同的结果。所以如你所想,字符流的底层也是有一个字符与字节的转换。 -
其实转换流就是一种字符流的一种,只是可以让我们自定义编码表避免乱码的异常。
2.3.1 字符流 --> 字节流 :OutputStreamWriter
- 构造函数:
OutputStreamWriter(OutputStream out):传入一个输出流父类引用的子类对象(数据最终都被存储为字节形式),默认使用平台的编码方式(IDEA->utf-8)
OutputStreamWriter(OutputStream out,String charsetName):传入一个输出流父类引用的子类对象(数据最终都被存储为字节形式),第二个参数自定义编码表。
2.3.2 字节流 --> 字符流 :InputStreamReader
-
构造函数
InputStreamReader(InputStream is):传入一个输入流父类引用的子类对象(数据在计算机中都是以字节形式存储),默认使用平台的编码方式(IDEA->utf-8)
InputStreamReader(InputStream is,String charsetName):传入一个输入流父类引用的子类对象(数据在计算机中都是以字节形式存储),第二个参数自定义编码表 -
扩展知识点
-
FileReader --> 底层: InputStream + 平台默认编码表
-
InputStreamReader --> 底层: InputStream + 指定编码表
从这里可以看出字符流的底层还是由字节流实现的,毕竟计算机中一切都是字节。