深入理解java I/O系统

来源:互联网 发布:傲友软件 编辑:程序博客网 时间:2024/05/02 12:52

       提起JAVA I/O系统,大家会想到很多常用的类和固定用法,这些使用方式对实现读写操作很有帮助。但让我们给这些类和方法划分种类时,往往十分困难,因为他们完成的功能都类似。而且从功能角度出发,正常工作才是王道。处于好学,参照JAVA编程思想,对JAVA I/O系统梳理了一番。说的不到或不对的地方,还请看客给些积极意见!

       要弄清楚I/O系统,就要了解通道和流是什么。

      通道是任何能够连接到可以进行I/O操作实体的连接。

     是可操作I/O实体从一存储或操作空间到另一存储或操作空间的中间状态。

        这两者之间的关系就像自来水管道和水流之间的关系,可操作I/O实体就是水源,水从自来水厂发出,通过管道流向各家各户,这之间水以水流的状态存在管道中。在I/O操作中,对实体操作要先建立通道,实体以流的方式进行转移和变化。计算机最底层以字节为单位对数据存储,因此在管道中数据是以字节流的形式传递。为适应不同语言(中文),通过上层封装,可以对字符直接操作,产生字符流。这也就是常见的InputStream、OutputStream和Reader、Writer类由来。下面就从源码层次上了解下这些类的特点。

        InputStream类中我们主要关注下两个read方法,实际的读操作是由子类实现的read方法完成,InputStream类提供一个使用byte数组存储字节的read方法。OutputStream类的实现思想与之类似,底层实际的写操作使用子类中的write方法实现。

    //抽象方法,用来被子类实现    public abstract int read() throws IOException;    //该方法将数据源中从off开始,长度为len的数据读到b数组中    public int read(byte b[], int off, int len) throws IOException {if (b == null) {    throw new NullPointerException();} else if (off < 0 || len < 0 || len > b.length - off) {    throw new IndexOutOfBoundsException();} else if (len == 0) {    return 0;}int c = read();if (c == -1) {    return -1;}b[off] = (byte)c;int i = 1;try {    for (; i < len ; i++) {c = read();if (c == -1) {    break;}b[off + i] = (byte)c;    }} catch (IOException ee) {}return i;    }
       Reader类中主要方法为,由子类来实现具体方法。这里的参数是char数组类型,而不再是byte数组,这也充分说明Reader类主要是处理字符。字符和字节虽然都是8位表示,且能够进行安全转换,但人们喜欢看字符和不是转换为字节的数字。因此要让Reader类正常工作,中间还需要一个适配器,将字节转换为字符。
    abstract public int read(char cbuf[], int off, int len) throws IOException;
      最简单的适配子InputStreamReader,该类使用装饰者模式,实现Reader的所有方法,底层再调用InputStream的实现。如何工作让我们看看他的构造函数就知道了。在类中有一个StreamDecoder类型的final变量sd,构造函数主要对他进行初始化。适配器中的所有read方法,都自然而然的转换到sd.read()上。截止到这,我们还是没看到最底层的字符与字节的转换,虽然能够想象,但还是很想看到源码中的这些操作。那就继续深耕查看下StreamDecoder类吧。

    public InputStreamReader(InputStream in) {super(in);        try {    sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object        } catch (UnsupportedEncodingException e) {    // The default encoding should always be available    throw new Error(e);}    }
       StreamDecoder类的源码操作。这些真相大白了,虽然封装很多层,但原理还是一样的,只是在读取byte后将其强转为char类型,原理很简单的。
public int read(char cbuf[], int offset, int length) throws IOException {149     int off = offset;150     int len = length;151         synchronized (lock) {152             ensureOpen();153             if ((off < 0) || (off > cbuf.length) || (len < 0) ||154                 ((off + len) > cbuf.length) || ((off + len) < 0)) {155                 throw new IndexOutOfBoundsException();156             }157         if (len == 0)158                 return 0;159 160         int n = 0;161 162         if (haveLeftoverChar) {163         // Copy the leftover char into the buffer164         cbuf[off] = leftoverChar;165         off++; len--;166         haveLeftoverChar = false;167         n = 1;168         if ((len == 0) || !implReady())169             // Return now if this is all we can produce w/o blocking170             return n;171         }172 173         if (len == 1) {174         // Treat single-character array reads just like read()175         int c = read0();176         if (c == -1)177             return (n == 0) ? -1 : n;178         cbuf[off] = (char)c;179         return n + 1;180         }181 182         return n + implRead(cbuf, off, off + len);183         }184     }
      到现在大家对字节流类和字符流类有了更深一层了解,使用时无外乎不同的子类之间的搭配,工作原理和上面介绍的都是一样的。具体有哪些使用方法,可以上网查找。到现在为止还有一个重要的类RandomAccessFile,它的实现思路与前两类都不同,但功能更加强大。它可以随意读取文件的某一段内容,不许要按照顺序。也可以随意的对某一段内容修改,然后写回。是一个具备读、写双向功能的类。它常与mappedbytebuffer类搭配使用,完成十分强大的工作。下一节将对其进行介绍。



0 0
原创粉丝点击