java io
近来碰到许多关于IO 的代码,于是动了把IO好好梳理一趟的想法。通过这几天的查阅资料、动手写代码总算把Java中IO的结构理清楚了。
一切从流(Stream)开始。所谓流,就是数据的有序排列。而流可以是从某个源(称为流源或Source of Stream)出来,到某个目的地(称为流汇或Sink of Stream)去的。由流的方向,可以分成输入流和输出流,一个程序从输入流读取数据向输出流写数据。如下图1和图2
图1
图2
注意这里提到是字节而不是字。字节是8个bits。这点也决定了在InputStream和OutputStream中读和写的基本单位是字节,也就是byte()。
但是在实际应用这种机制存在一个问题:那就是从图中我们可以看出这种机制从数据源中读出的或是向数据源中写入的都是内存中最基本的单位即一个字节。而程序中的数据确实某些类型:如一个char,一个String,一个Int类型。因而Java的I/O库提供了一个称做链接(Chaining)的机制,即某些Stream会以其他的Stream为输入来构造。在IO结构中这些流被称为过滤流。他们主要是FilterInputStream/FilterOutputStream以及他们的子类,而那些不需要其他Stream来构造的流则称为节点流。那么用图3,图4来描述可以如下。其中图3描述的是从源中通过过滤流的机制读取某种类型的数据。而图4则是将某种类型的数据写到目的中。
图 3
图 4
通过图3,4我们可以看到区分节点流和过滤流的方法是看构造这个流是否需要其他流。这点在下面的代码的中将理解得更清楚。
图5更清楚的描叙Java中IO的体系结构。在我们理解了这个体系结构后便可以深入去理解IO中各个具体的类。
在这当中我们常用的节点流的类有:ByteArrayInputStream,FileInputStream。通过这两个类的使用我们也可以对其他节点流的类做到管中窥豹。当然有读就有写。同样ByteArrayOutStream,FileOutStream也是常用的。为了简洁,这里只介绍读。
ByteArrayInputStream:数据的源是Byte 类型的数组。
FileInputStream:数据源是文件。可以是一个File对象也可以是文件的路径。(适配器模式)
这两者的不同在于源的不同。但是它们同时作为InputStream的子类在从流中读出数据的方法相同。读取的结果都是byte活bytes。主要方法有:
abstract int read() :读取一个字节数据,并返回读到的数据,如果返回-1,表示读到了输入流的末尾。
int read(byte[]b) :将数据读入一个字节数组,同时返回实际读取的字节数。如果返回-1,表示读到了输入流的末尾。
int read(byte[]b, int off, int len) :将数据读入一个字节数组,同时返回实际读取的字节数。如果返回-1,表示读到了输入流的末尾。off指定在数组b中存放数据的起始偏移位置;len指定读取的最大字节数。
其它方法
long skip(long?n) :在输入流中跳过n个字节,并返回实际跳过的字节数。
int available() :返回在不发生阻塞的情况下,可读取的字节数。
void close() :关闭输入流,释放和这个流相关的系统资源。
void mark(int?readlimit) :在输入流的当前位置放置一个标记,如果读取的字节数多于readlimit设置的值,则流忽略这个标记。
void reset() :返回到上一个标记。
boolean markSupported() :测试当前流是否支持mark和reset方法。如果支持,返回true,否则返回false。
其他的参考java API。
下面引入一段测试代码:
byte [] bytes=new byte[10];
inFile.read(bytes);
System.out.println(new String(bytes));
ByteArrayInputStream bytein=new ByteArrayInputStream(bytes);
inFile.close();
bytein.close();
第一行构造FileInputStream,第三行读数据。
第四行用byte数组来构造ByteArrayInputStream
为了证实节点流读取的数据的单位是byte可以用如下代码进行测试:
ByteArrayInputStream byteIn= newByteArrayInputStream(chinese.getBytes());
byte[]bytes2=new byte[2];
byteIn.read(bytes2);
System.out.println(new String(bytes2));
通过测试我们可以得出结果输出的是一个中文字“我”。
我们常用的过滤流有:DataInputStream,ObjectInputStream,DataOutStream,ObjectOutStream。
DataInputStream,DataOutStream:它们可以读取和写入各种类型的数据而不再局限于byte数组。下面先使用DataOutStream把整型,以及字符串数据写到一个文件中,然后再使用DataInputStream来读取数据。
new DataOutputStream(new FileOutputStream("d:\\data.txt"));
dataOut.writeInt(100);
dataOut.writeChars("test\\n");//写入一行
dataOut.writeChars("famale");//再写入一行
dataOut.close();
DataInputStream dataIn= new DataInputStream(new FileInputStream("d:\\data.txt"));
System.out.println(dataIn.readInt()+"" +dataIn.readLine()+""+dataIn.readLine());
dataIn.close();
以代码上在理解了过滤流之后就比较容易懂,这里就不一一解释。
ObjectInputStream,ObjectOutputStream:可以读取一个对象以及 把一个对象写到一个节点流中去。相当于序列化和反序列化。如下是代码示例:
1.创建一个javaBean。注意要实现serializable接口
/**
*
*/
private static final long serialVersionUID = 1L;
private int id;
private String name;
private String sex;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String toString() {
return new String("name: " + this.name + " id: " + this.getId()
+ " sex: " + this.sex);
}
}
2.测试程序。先创建一个ObjectOutputStream流,它是用一个FileOutputStream构建的。那么往ObjectOutputStream中写数据则是将数据写到了FileOutputStream对象的目的地中去。同样通过FileInputStream来构建ObjectInputStream读取对象。
person.setId(100);
person.setName("test");
person.setSex("famale");
ObjectOutputStream objOut=new ObjectOutputStream(new FileOutputStream(new File("d:\\object.txt")));
objOut.writeObject(person);
objOut.close();
ObjectInputStream objIn=new ObjectInputStream(new FileInputStream("d:\\object.txt"));
Person person2=(Person) objIn.readObject();
System.out.println(person2);
objIn.close();
在图5中我们注意到Reader、Writer类和Input、Output是同级的。他们之间的不同时Reader、Writer读写的基本数据是byte而Input、Output读写的一个字,也就一个Unicode编码字符,16个bit。
通过如下代码我们可以进行测试:
char []chars=new char[3];
stringReader.read(chars);
System.out.println(chars);
stringReader.close();
我们得到的输出结果是三个汉字。
对于Reader和Writer中的其他类可以参照前面的思想进行学习。这是一个对称的设计。
在Java IO设计中运用装饰模式和适配器设计模式。至于这两者模式以及为什么要如此设计可以参考资料:
http://hi.baidu.com/xiaoheblog/blog/item/f9967b340f9bd94d241f147a.html