现实世界中我们总要处理不同的数据源:
1.字节数组。
2.String对象
3.文件。
4.“管道”,工作方式与实际管道相似。
5.一个有其他种类组成的序列。
6.其他数据源,如Interent连接等。 –From 《Java编程思想》
对于以上的数据源,Java io都提供了相对应的流处理类,如读取文件数据源FileInputStream,FileOutputStream,这些类都继承了InputStream这个抽象类,并重新实现了主要的接口。作为Java io的学习篇,这里主要分析了面向字节的io和面向字符的io。
面向字节
面向字节顾名思义他们在处理数据流时,是以字节为单位的。这些流处理类继承了InputStream和OutputStream两个抽象类。下面简单看下这两个父类具有的接口
InputStream
1 | public abstract int read() |
以上是InputStream所具有的接口,最主要的函数功能便是从流中读取数据,其子类都该函数都有各自实现。下图为继承了InputStream的一些子类(仅存在于io包内的类),其中FilterInputStream是一个装饰器类,他持有一个InputStream对象,所以继承了FilterInputStream的类可以添加其他的功能。这就是为什么我们经常会用到将各种流组合使用的原因。
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File(“”)));
OutputStream
1 | /*将b的低8位写到输出流中*/ |
可以看到OutputStream的接口就是将字节写入到输出流中,对于不同的数据源OutputStream同样有相应的子类。类似于FilterInputStream,FilterOutputStream同样是一个装饰器类以组合其他的流实现不同的功能。另外,我们常用的System.out对象就是一个PrintStream实例。
主要的Stream类
下面简单介绍下一些类的作用:
ByteArrayInputStream/ByteArrayOutputStream从缓冲区字节数组中读取到流/将流写到缓冲区字节数组(一种数据源)。
FileInputStream/FileOutputStream从文件读取数据到流/或将流写到文件中(数据源),经常搭配FileOutputStream使用。
DataInputStream/DataOutputStream以可移植的方式从流长读取/写入基本类型数据(int,char,long)。
BufferedInputStream/BufferedOutputStream每次读取/写入缓冲区(默认8k),避免每次进行实际操作,提高效率。
一个深拷贝的输入输出流示例,写入到byte数组中,同样也可以写入到其他流中:
1 | public Object deepCopy() throws Exception{ |
面向字符
以字符为单位处理流,有输入流抽象类Reader、输出流抽象类Writer。Reader、Writer并不能完全取代InputStream、OutputStream,但能更方便的对文本和字符类型的数据进行操作(支持Unicode,java的char是Unicode的)。Reader、Writer有类似于InputStream、OutputStream的接口,主要就是将byte操作改变成char操作,就不一一列出。
Reader
1 | protected Object lock; |
Reader提供了一个lock锁属性,和read到CharBuffer的操作。lock默认是Reader对象本身(this),这样SubClass便可以使用这个对象来同步流的操作,这能达到更细粒度的同步操作(以后学习线程同步再来详细介绍,这里就先忽略)。同样CharBuffer在学习nio再来介绍。
来看一下Reader的继承结构:
大多数类都与Stream类相对应,就不详细解释具体用法了。需要注意打是,InputStreamReader使用StreamDecoder类可以将字节流转换到字符流,所以有如下用法:
BufferedReader bReader = new BufferedReader(new InputStreamReader(System.in));
Writer
1 | private char[] writeBuffer; |
Writer增加了一些对String和CharSequence的写操作,writeBuffer用来暂时存放要被写入流的字符串或字符数组,其余的接口类似于OutputStream。Writer的类继承结构如下:
还是直接举一些流操作的例子,这些例子来自《Java编程思想》。这些流类需要结合使用,才能发挥其最大的功能
1 | public class BufferedInputFile { |
RandomAccessFile
RandomAccessFile适用于大小已知的记录组成的文件,可以使用seek()方法将记录转移。RandomAccessFile仅实现了DataInput和DataOutput接口(故有很多readDouble(),writeDouble()等操作),与InputStream和OutputStream没有关系。
在JDK1.4中,RandomAccessFile的大多数功能被nio存储映射文件取代。
看一个实际操作例子:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32public static void usingRandomAccessFile(String file){
try {
RandomAccessFile rFile = new RandomAccessFile(file, "rw"); //rw读写
for(int i=0; i<7; ++i){
rFile.writeDouble(i*1.414);
}
rFile.writeUTF("end");
rFile.close();
display(file);
rFile = new RandomAccessFile(file, "rw");
rFile.seek(5*8);
rFile.writeDouble(47.000144);
rFile.close();
display(file);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void display(String file) throws IOException {
RandomAccessFile rFile = new RandomAccessFile(file, "r"); //r仅读
for (int i = 0; i < 7; ++i) {
System.out.println("value " + i + ": " + rFile.readDouble());
}
System.out.println(rFile.readUTF());
rFile.close();
}
public static void main(String[] args) {
usingRandomAccessFile("hh.txt");
}
输出:
value 0: 0.0
value 1: 1.414
value 2: 2.828
value 3: 4.242
value 4: 5.656
value 5: 7.069999999999999
value 6: 8.484
end
value 0: 0.0
value 1: 1.414
value 2: 2.828
value 3: 4.242
value 4: 5.656
value 5: 47.000144
value 6: 8.484
end