Java中的流
Java中的字节流
InputStream
是所有用于操作字节输入流的祖先类,OutputStream
是所有用于操作字节输入流的祖先类,主要用来处理字节或二进制对象。Java中的字符流
Reader
用于读取字符输入流的祖先类,Writer
是用于写入字符输出流的祖先类。它们的子类都需要实现read(或write)、flush和close方法,并通过重写若干方法提供更高效和多功能的输入输出操作。
四个祖先类都是抽象类,无法被直接new,需要通过创建其子类的实例来对字节流和字符流进行操作。
字节流与字符流的区别
字节流
字节流的处理单元为1个字节,操作字节和字节数组。字节流更加适用于音频文件、图片、歌曲,操作中文文本可能会出现乱码。字节流在操作过程中不会使用到缓冲区,直接对文件本身进行操作。字符流
字符流的处理单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串。字符流是由Java虚拟机将字节转化为2个字节的Unicode字符为单位的字符而成的,因此对多国语言的支持性较好。字符流在操作的时候是需要使用到缓冲区的,因此需要使用close方法才能输出内容,或调用flush方法强制刷新缓冲区。
字节流的文件操作类
字节流操作类主要介绍FileInputStream
和FileOutputStream
两个类,这两个类分别继承InputStream
和OutputStream
,是字节流的文件操作实现类,读入和写出都有逐字节和按字节数组两种实现方式。字节流操作由于没有使用到缓冲区,所以不调用close方法也可以成功写入和读出。代码示例如下:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class BytesOperator {
public static void main(String[] args) throws Exception{
//读入
File file = new File("D:\\Test.txt");
FileInputStream inputStream = new FileInputStream(file);
//逐字节读入
int l = 0;
while ((l = inputStream.read()) != -1) {
System.out.println((char) l);
}
inputStream.close();
//按字节数组读入
// byte[] bytes = new byte[inputStream.available()];
// inputStream.read(bytes);
// String string = new String(bytes);
// System.out.println(string);
// inputStream.close();
//写出
File file2 = new File("D:\\result.txt");
FileOutputStream outputStream = new FileOutputStream(file2);
String test = "test1 test2";
byte[] bytes2 = test.getBytes();
//逐字节写出
for(int i = 0; i < bytes2.length; i++) {
outputStream.write(bytes2[i]);
}
outputStream.close();
//按字节数组写出
// outputStream.write(bytes2);
// outputStream.close();
}
}
字符流的文件操作类
InputStreamReader
是字节流与字符流之间的桥梁,可以读入字节流,并根据指定的编码方式转化成字符流。
OutputStreamWriter
是字符流与字节流之间的桥梁,可以从运行的程序中接收Unicode字符,然后使用指定的编码方式将字符流转化为字节流。
以上两个类都是字符流与字节流之间的桥梁,它们有转换的作用,其本身又是字符流,因此在构造时需要传入字节流对象。其使用的情况主要有如下两个:
- 源或者目的设备内部使用的是字节流,但操作的是文本数据;
- 操作文本时涉及具体编码表。
除了以上两个类以外,我们较为常用的还有FileReader
和 FileWriter
,这两个类是InputStreamReader
和 OutputStreamWriter
的子类,也可以称为 convenience class
(可译作方便类,大概就是说类的本身不提供任何方法,但是它提供操作其它类或者其它一系列类的简单入口,从类的继承关系图中也可以看出)。
FileReader
的继承关系如下图:
FileWriter
的继承关系如下图:
FileReader
和 InputStreamReader
的主要区别在于FileReader
的构造方法使用系统默认的字符编码和默认的字节缓存大小,如果需要指定这些值,可以通过传入一个FileInputStream
对 InputStreamReader
进行构造。若操作Test.txt时使用的是系统默认的GBK,则以下两种读入字节流的代码意义相同。
//使用FileReader读入字节流
FileReader fr = new FileReader("D:\\Test.txt");
//使用InputStreamReader读入字节流
InputStreamReader sr = new InputStreamReader(newFileInputStream("D:\\Test.txt"));
若Test.txt中的字符数据是通过utf-8或者其他形式进行编码,则读取时必须指定编码表,只能使用 InputStreamReader
,示例代码如下:
InputStreamReader sr = new InputStreamReader(new FileInputStream("D:\\Test.txt"), "UTF-8");
FileWriter
和 InputStreamWriter
的主要区别与上述相类似,不再赘述,下面给出FileReader
和 FileWriter
的完整使用示例代码。
import java.io.*;
public class CharacterOperator {
public static void main(String[] args) throws Exception{
FileReader fr = new FileReader("D:\\Test.txt");
//使用转换流指定编码表
// FileInputStream inputStream = new FileInputStream("D:\\Test.txt");
// InputStreamReader sr = new InputStreamReader(inputStream, "UTF-8");
int l = 0;
while ((l = fr.read()) != -1) {
System.out.print((char) l);
}
fr.close();
FileWriter fw = new FileWriter("D:\\result.txt");
//使用转换流指定编码表
// FileOutputStream outputStream = new FileOutputStream("D:\\result.txt");
// OutputStreamWriter sr = new OutputStreamWriter(outputStream, "UTF-8");
String test = "test1 test2 测试1";
fw.write(test);
fw.close();
}
}
除了以上介绍的若干类之外,还有 BufferedReader
、BufferedWriter
等Reader
和 Writer
的子类,都可用于操作字符流,通过缓冲的方式实现字符、数组和行的高效读取,主要用于提高读取、写入的效率,在本文中不做讨论。