简介
- 流:流代表任何有能力产生数据的数据源对象或有能力接收数据的接收端对象,如文件、网络、内存等。
- Java I/O 类库的设计中采用了装饰器模式和适配器模式
- Java I/O 类库中的装饰器模式:FilterInputStream和FilterOutputStream类是Java I/O 类库中所有装饰器的抽象基类,这两个类继承自InputStream和OutputStream,都只是简单地重写了父类中的方法,而并未提供新功能。通过其子类为被包装对象提供新功能。装饰器模式的使用使得Java I/O 类库不那么容易使用,一般在进行I/O操作时,都会创建不止一个 I/O 对象,通过层层包装,最后才会获得我们真正想要进行 I/O 操作的对象。
如下面这个示例,为了创建一个DataInputStream 对象 从名为"filename"的文件中获取数据,首先创建了一个FileInputStream对象,然后创建了一个包装这个FileInputStream对象的BufferedInputStream对象,最后通过将BufferedInputStream对象传给DataInputStream,才获得我们真正想要获得的结果对象。
DataInputStream in = new DataInputStream(
new BufferedInputStream(
new FileInputStream("filename")));
- Java I/O 类库中的适配器模式:InputStreamReader和OutputStreamWriter这两个类可以完成“字节流”和“字符流”之间的适配,通过使用这两个类,就可以将面向“字节流”的InputStream和OutputStream转化为面向“字符流”的Reader和Writer。
InputStream & OutputStream
- Java最开始的 I/O 类库设计都是面向字节流的,所以在 JDK 1.0 中,I/O 类库主要分为两部分:所有与输入有关的类都继承自 InputStream,所有与输出有关的类都继承自 OutputStream,InputStream和OutputStream是这些类的抽象基类。
- InputStream用于表示那些从不同的数据源产生输入的类,这些输入源主要有:字节数组、String对象、文件、“管道”、一个由其他的流所组成的序列、其他数据源(Internet等)
- OutputStream主要用于将数据向目标输出,如向文件、内存、字节数组、网络等输出数据
下面两图展示了InputStream和OutputStream类层次关系,可以看到这两个类中包含的子类基本上呈现一一对应的模式。
- FilterInputStream和FilterOutputStream为“装饰器”类提供了一个基类,用于将属性或有用的接口分别与输入/输出流连接起来。
Reader & Writer
- Java 1.1对基本的 I/O流类库进行了重大修改,添加了一个面向Unicode字符的Reader和Writer类。设计Reader & Writer的目的在于国际化:旧的InputStream & OutputStream类库只能支持8为字节操作,不能很好地处理16位Unicode字符。而16位Unicode字符用于字符国际化(Java本身的char也是16位Unicode字符),所以添加Reader & Writer继承层次结构就是为了在所有的I/O操作中都支持Unicode。同时新类库也比旧类库快。
- 当需要将面向字节的流与面向字符的流结合使用时,可以使用Java I/O类库中的适配器类:InputStreamReader和OutputStreamWriter,通过这两个类分别将InputStream和OutputStream转化为Reader和Writer。
下面两图展示了Reader和Writer类层次关系:
下表展示了在这两个继承层次结构中数据来源与去向间的一一对应关系:
RandomAccessFile
- RandomAccessFile是一个完全独立的类,直接继承自Object,并且实现了DataOutput和DataInput接口。RandomAccessFile主要适用于由大小已知的记录所组成的文件,这样就可以使用seek()方法将记录从一处移到另一处。
- 在 JDK 1.4中,RandomAccessFile的大多数功能由nio中的存储映射文件所取代。
// RandomAccessFile的使用
public class RandomAccessFileTest {
static void display(RandomAccessFile read) throws IOException {
byte[] bytes = new byte[16];
read.seek(0);
read.read(bytes);
for (int j = 0; j < bytes.length; j++) {
printnb((char) bytes[j]);
}
print();
print(read.readBoolean());
print(read.readInt());
print(read.readUTF());
}
public static void main(String[] args) throws IOException {
String filename = "raf";
RandomAccessFile read = new RandomAccessFile(filename, "r");
RandomAccessFile write = new RandomAccessFile(filename, "rw");
//执行write操作,向文件--raf中写入数据
write.seek(0);
write.writeBytes("RandomAccessFile");
write.writeBoolean(true);
write.writeInt(123);
write.writeUTF("End writing");
display(read); // 显示写入文件中的数据
write.seek(2); // 将“光标”移到文件的第二个字节处
write.writeChar('k'); // 在“光标”当前位置写入字符'k'
display(read);
// 关闭文件
read.close();
write.close();
}
}
//Output:
RandomAccessFile
true
123
End writing
Ra komAccessFile // 一个字符占两个字节,所以文件的第二、三个字节用于写入字符'k'
true
123
End writing
注:
- 正如上面的代码所示,在使用RandomAccessFile时,一般需要预先知道文件的排版,这样才能正确地操作这个文件,并很好的利用其随机访问的功能
- 装饰器模式:装饰模式也称之为包装器模式(Wrapper),是一种为已有的功能动态添加新功能的方式,它把每个要装饰的功能都放在一个单独的类中,并让这个类包装它所要装饰的对象,因此,当需要执行某些特殊的行为时,客户端代码就可以根据需要有选择的、按顺序的使用装饰功能包装对象。
优点:把类中的核心职责和装饰功能区分开来,可以有效去除相关类中的重复装饰逻辑代码。
缺点:需要添加一些额外的装饰器代码,代码结构相对比较复杂。