我将按照基类的顺序:InputStream、OutPutStream、Reader、Writer来分别对Java I/O加以总结。
。。。。。整理中
java中的流,简单理解就是管道里有流水,这个管道连接了程序和文件。
InputStream、OutPutStream是字节输入流的所有类的超类。
Reader、Writer是字符输入流的所有类的超类。
Java IO流对象(其实大家都和懂啦,我这里再赘述一下)
1.输入字节流InputStreamIO 中输入字节流的继承图可见上图,可以看出:
InputStream 是所有的输入字节流的父类,它是一个抽象类。
ByteArrayInputStream、StringBufferInputStream、FileInputStream 是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据。PipedInputStream 是从与其它线程共用的管道中读取数据。
ObjectInputStream 和所有FilterInputStream 的子类都是装饰流(装饰器模式的主角)。
2.输出字节流OutputStream
IO 中输出字节流的继承图可见上图,可以看出:
OutputStream 是所有的输出字节流的父类,它是一个抽象类。
ByteArrayOutputStream、FileOutputStream 是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。PipedOutputStream 是向与其它线程共用的管道中写入数据,
ObjectOutputStream 和所有FilterOutputStream 的子类都是装饰流。
Decorator模式又名包装器(Wrapper),它的主要用途在于给一个对象动态的添加一些额外的职责。与生成子类相比,它更具有灵活性。
用我的理解就是当流经过输入输出的管道时候给它添加一些额外的功能。(装饰模式和继承有什么区别呢?这个后续讨论)下面的图来自《Head first design patterns》中装饰者模式一章
Component为组件和装饰的公共父类,它定义了子类必须实现的方法。
ConcreteComponent是一个具体的组件类,可以通过给它添加装饰来增加新的功能。
Decorator是所有装饰的公共父类,它定义了所有装饰必须实现的方法,同时,它还保存了一个对于Component的引用,以便将用户的请求转发给Component,并可能在转发请求前后执行一些附加的动作。
ConcreteDecoratorA和ConcreteDecoratorB是具体的装饰,可以使用它们来装饰具体的Component.
众人皆知java的输入输出包"java.io"是一个装饰者模式的典型案例,这从下面这小段代码就可以大致看出:
BufferedOutputStream bw = new BufferedOutputStream(new FileOutputStream("d:tina.txt"));
我使用了一个BufferedOutputStream封装了一个FileOutputStream,FileOutputStream相当于一个Component,BufferedOutputStream就是一个Decorator。
说的通俗一点,装饰者模式就是需要添加一个新功能就包一层,同时需要另外一个新功能就再包一层。基本的原理是这样的:如果需要对A类装饰,就从A类的父类继承一个装饰者虚拟类(当然也可以用接口,可能接口更理想),然后从这个虚拟类(接口)实现具体的装饰者类,在具体装饰者类中从构造函数引入被包装者对象,然后对该对象添加额外的功能。(The decorator adds its own behavior either before and/or after delegating to the object itdecorates to do the rest of the job.) 这样很容易就扩展了被包装类的功能,也没有修改被包装类和他的父类。
上面的图告诉我们,所有的类都是用来包装inputStream的,输出流outputStream的设计方式也是一样的,搞清楚这点,我们就不会再为io里面数量繁多的类而感到头痛了!
/**
* @author tina.wyn
*
*/
class MyInputStream extends FilterInputStream {
protected MyInputStream(InputStream in) {
super(in);
}
public int read() throws IOException {
int c = super.read();
return (c == -1 ? c : Character.toLowerCase((char) c));
}
public int read(byte[] b, int offset,int len)throws IOException{
int result = super.read(b, offset, len);
for(int i = offset;i<offset+result;i++){
b[i] = (byte) Character.toLowerCase((char)b[i]);
}
return result;
}
}
public class InputTest{
public static void main(String[] args) throws IOException{
int c;
try {
InputStream in = new MyInputStream(new BufferedInputStream(new FileInputStream("d:/tina.txt")));
while ((c = in.read())!=-1) {
System.out.print((char)c);
}
in.close();
} catch (IOException e) {
}
}
}
在所有的流操作里。字节永远是最基础的。任何基于字节的操作都是正确的。无论你是文本文件还是二进制的文件。
如果确认流里面只有可打印的字符,包括英文的和各种国家的文字,也包括中文,那么可以考虑用字符流。
由于编码不同,多字节的字符可能占用多个字节。比如GBK的汉字就占用2个字节,而UTF-8的汉字就占用3个字节。
所以,字符流是根据指定的编码,将1个或多个字节转化为java里面的unicode的字符,然后进行操作。
字符操作一般使用Writer,Reader等, 字节操作一般都是InputStream, OutputStream 以及各种包装类,比如BufferedInputStream和BufferedOutputStream等。