第十七章 二进制I/O
17.1 引言
文件可以不严谨的分类为文本文件和二进制文件。文本文件指的是可以用文件编辑器进行查看和修改的,二进制文件则不可以使用文本编辑器查看和修改。
例如,Test.java
文件储存在文本文件中,因此可以用文本编辑器读取,但是Test.class
则是二进制文件,由java虚拟机读取
Java提供了许多实现文件输入/输出的类。这些类可以分为文本I/O类和二进制I/O类。在12.11节中我们已经介绍过文本I/O类,因此本章开始介绍二进制I/O的类
17.2 在Java中如何处理文本I/O
使用Scanner类读取文本数据,使用PrintWriter类写入文本数据
17.3 文本I/O与二进制I/O
计算机并不区分二进制文件与文本文件,所有的文件都是以二进制形式来存储的,二进制I/O不涉及编码和解码,因此比文本I/O更加高效。
17.4 二进制I/O类
InputStream类是二进制输入类的根类,而OutputStream类是二进制输出类的根类
二进制I/O类中的所有方法都声明为抛出java.io.IOException或其子类
17.4.1 FilelnputStream和FileOutputStream
FilelnputStream类用来从文件中读取数据,FileOutputStream类用来向文件中写入数据
import java.io.*;
public class Test {
public static void main(String[] args) throws java.io.IOException {
FileOutputStream output = new FileOutputStream("Exercise17_01.txt");
int randomInt = 65;
output.write(randomInt);
output.close();
FileInputStream input = new FileInputStream("Exercise17_01.txt");
System.out.print(input.read());
input.close();
}
}
write()方法接收一个byte类型的参数,且这个参数忽略符号位,因此,参数的范围是0-255(1个byte是8个bit,因此就是8个0或1,最大能表示255)
如果输入的参数是0-255之间的数字,就会将这个数字储存进文件,如果输入的参数是负数,或者大于255,这个参数就会被截断,只保留后8位
如果现在你去看Exercise17_01.txt文件本身,就会发现文件内容是A,这是因为,虽然各种二进制类的方法本身不涉及编码和解码,但是文本编辑器进行了ASCII解码,让用户看到解码后的对应字符
需要注意的是,用于进行二进制数据输入输出的类,并不会将数据按照文本的形式保存到文件中,有时可能会出现乱码,因此,我们有的时候确实写入了数据,只不过由于编译器的编码方案等局限,无法直接从文本编辑器查看内容
17.4.2 FilterlnputStream和FilterOutputStream
过滤器数据流可以用来读取某些特殊的字符(串),如基本数据类型和字符串
17.4.3 DatalnputStream和DataOutputStream
FilterlnputStream和FilterOutputStream是过滤数据的基类,例如,如果需要处理基本数值类型,就需要用到DatalnputStream类和DataOutputStream类
具体一点来说,我们刚刚学过,write()方法如果输入超出了范围0-255,就会被截断,为了避免截断,我们引入本节内容
如果文件读取到了末尾再次尝试读取,read()方法会返回-1,readInt()等方法就会发生EOFException异常
不同的数据写入时有自己特殊的编码方式,读取数据的类型取决于写入数据的类型,例如,如果一个文件不是由writeInt()创建的,直接读取readInt()可能会有异常
读取数据的顺序需要按照写入数据的顺序来进行,如果先写入了一个writeUTF,再写入了一个writeDouble,读取的时候也必须先读取writeUTF,再读取writeDouble,直接读取writeDouble会报异常
17.4.4 BufferedlnputStream和BufferedOutputStream
BufferedlnputStream类和BufferedOutputStream类可以通过减少磁盘读写次数来提高输人和输出的速度
17.5 示例学习:复制文件
17.6 对象 I/O
ObjectlnputStream类和ObjectOutputStream类除了可以实现基本数据类型与字符串的输入和输出之外,还可以实现对象的输入和输出。由于ObjectlnputStream类和ObjectOutputStream类包含DatalnputStream类和DataOutputStream类的所有功能,所以,完全可以代替
写入/读取流中的对象需要是可序列化的,因为可序列化的对象是java.io.Serializable接口的实例,所以可序列化对象的类必须实现Serializable接口(基本类型
在进行序列化时会被自动拆箱/装箱并通过)
如果一个对象是Serializable的实例,但它包含了非序列化的实例数据域,对象就不是可序列化的,需要给这些数据域加上关键字transient, 告诉Java虚拟机将对象写入对象流时忽略这些数据域
17.7 随机访问文件
Java提供了RandomAccessFile类,允许从文件的任何位置进行数据的读写