[Java]BufferedOutputStream、DataInputStream源码分析

阅读 BufferedOutputStream 和 DataInputStream 的源代码 (openjdk), 理解这两个类的继承关系, 每个方法的具体实现, 提交两个代码的逐行注释 (对 DataInputStream, 可以忽略 readUTF() 函数).源文件可以在这里下载BufferedOutputStream.javaDataInputStream.java.

一、BufferedOutputStream类:

package java.io; //该类属于java.io包

public class BufferedOutputStream extends FilterOutputStream { //该类继承了FilterOutputStream类
    
    protected byte buf[]; //字节型的缓冲区,该数据成员只能被同属于java.io包的类访问

    protected int count; //缓冲区buf中有效数据的个数,即buf[0]~buf[count-1]是有效数据

    public BufferedOutputStream(OutputStream out) { //带有参数为OutputStream类型的构造函数
        this(out, 8192); //为输出流out构造一个大小为8KB的缓冲区
    }

    public BufferedOutputStream(OutputStream out, int size) { //带有参数为OutputStream类型和大小size的构造函数
        super(out); //为输出流out构造一个过滤输出流对象FilterOutputStream
        if (size <= 0) { // 如果申请缓冲区的大小为负值
            throw new IllegalArgumentException("Buffer size <= 0"); // 抛出参数非法的异常,异常信息为"Buffer size <= 0"
        }
        buf = new byte[size]; // 对于合法的参数size,创建一个大小为size的字节型buf
    }

    private void flushBuffer() throws IOException { //私有方法清空缓冲区函数,因为涉及对输出流的写,需抛出IO异常
        if (count > 0) { // 如果有效数据个数大于0
            out.write(buf, 0, count); // 将缓冲区buf中的count个自己写为0(清空)
            count = 0; // 将有效数据个数清零
        }
    }


    public synchronized void write(int b) throws IOException { // 公有方法写入数据b,参数为int型,只有低八位有效。
    //此方法保证任何BufferedOutputStream对象在写的时候其他同类对象不能进行写操作,同样,所有BufferedOutputStream类的同步代码块都将被阻塞访问
        if (count >= buf.length) { // 如果当前有效字节数已经填满buf
            flushBuffer(); // 将buf清空
        }
        buf[count++] = (byte)b; // 将数据b的低八个字节写入buf[count],计数器加一
    }

    public synchronized void write(byte b[], int off, int len) throws IOException { // 参数重载写方法,从字节数组b的第off个字节开始拷贝len个字节的数据放入buff中
        if (len >= buf.length) { // 如果要拷贝的数据量大于buf的长度
            /* If the request length exceeds the size of the output buffer,
               flush the output buffer and then write the data directly.
               In this way buffered streams will cascade harmlessly. */
            flushBuffer(); // 清空buff
            out.write(b, off, len); // 直接调用输出流的write方法,将b从off开始的len个数据写入buf中
            return;
        }
        if (len > buf.length - count) { // 如果buf中剩余空间不足以写入len个字节
            flushBuffer(); // 清空缓冲区
        }
        System.arraycopy(b, off, buf, count, len); // 调用系统方法,将b数组中的b[off]~b[off+len-1]这些字节写入缓冲区的buf[count]~buf[count+len-1]
        count += len; // 有效数据计数器加len
    }

    public synchronized void flush() throws IOException { // 公有同步方法清空缓冲区和输出流
        flushBuffer(); // 将缓冲区清空
        out.flush(); // 将输出流清空,实际上是什么都不做,OutputStream中是一个空方法
    }
}

二、DataInputStream类:

package java.io; //该类属于java.io包

public class DataInputStream extends FilterInputStream implements DataInput { // DataInputStream 为 FilterInputStream的子类,实现了 DataInput接口

    public DataInputStream(InputStream in) { // 带有输入流参数的构造函数
        super(in); // 为输入流创建一个FilterInputStream对象
    }

    private byte bytearr[] = new byte[80]; // 私有数据成员
    private char chararr[] = new char[80]; // 私有数据成员

    public final int read(byte b[]) throws IOException { // 公有不可继承读方法
        return in.read(b, 0, b.length); // 从输入流in中读取字节数组b容量的字节数据放入b[0]~b[b.length-1],返回所读取的字节数
    }

    public final int read(byte b[], int off, int len) throws IOException { // 公有不可继承读方法,参数重载
        return in.read(b, off, len); // 从输入流in中读取len个字节的数据放入数组b[off]~b[off+len-1],返回所读取的字节数
    }

    public final void readFully(byte b[]) throws IOException { // 公有不可继承,读取数据填充满字节数组b
        readFully(b, 0, b.length); // 从输入流in中读取数据并填满字节数组b,如果没有填满数组b则一直读取,直到填满位置,从字节数组b的位置0开始存储,并且读取的字节个数等于b的长度
    }

    public final void readFully(byte b[], int off, int len) throws IOException { // 公有不可继承,读取数据从b[off]~b[off+len-1]填充字节数组b
        if (len < 0) // 如果长度小于0,抛出越界异常?为什么要抛出越界异常?off+len可能合法啊
            throw new IndexOutOfBoundsException();
        int n = 0; // n为在off基础上的偏移量,也可以看做读入的字节数
        while (n < len) { // 读入字节数小于目标字节数,继续读
            int count = in.read(b, off + n, len - n); // 从输入流in中读取len-n个数据,放入b[off+n]~b[off+len-1],注意,有可能读取不到那么多,读取到的字节数为count个
            if (count < 0) // 如果读取的字节数小于0
                throw new EOFException(); // 抛出读入异常,虽说是异常,但也是正常运行结束的标志,表示读到了输入流尾
            n += count; // 读入了count个字节,总体读入了n个字节
        }
    }

    public final int skipBytes(int n) throws IOException { // 公有不可继承,跳过n个字节
        int total = 0;  // total记录总共跳过了多少个字节
        int cur = 0; // cur为每次跳过的字节数

        while ((total<n) && ((cur = (int) in.skip(n-total)) > 0)) { // 跳过的总字节数不足n且当前能从输入流跳过的字节数大于0,期望从输入流中跳过n-total个字节
            total += cur; // 总跳过字节数 = 之前跳过的字节数+本次跳过的字节数
        }

        return total; // 返回真正跳过的字节数
    }

    public final boolean readBoolean() throws IOException { // 公有不可继承,从输入流中读入boolean型的数据
        int ch = in.read(); // 从输入流中读取一个字节
        if (ch < 0) // 如果读取的字节小于0
            throw new EOFException(); // 抛出EOF异常
        return (ch != 0); // 如果读到的数据为0,返回false;如果读到的数据不为0,返回true
    }

    public final byte readByte() throws IOException { // 公有不可继承,从输入流中读入byte型的数据
        int ch = in.read(); // 从输入流中读取一个字节
        if (ch < 0) // 如果读取的字节小于0
            throw new EOFException(); // 抛出EOF异常
        return (byte)(ch); // 取读取到的数据的低八个字节返回
    }

    public final int readUnsignedByte() throws IOException { // 公有不可继承,从输入流中读入无符号byte型的数据,即读取值为正数的byte值
        int ch = in.read(); // 从输入流中读取一个字节
        if (ch < 0) // 如果读取的字节小于0
            throw new EOFException(); // 抛出EOF异常
        return ch; // 读取到的字节以int形式返回
    }

    public final short readShort() throws IOException { // 公有不可继承,从输入流中读入short型的数据
        int ch1 = in.read(); // 从输入流中读取一个字节
        int ch2 = in.read(); // 从输入流中读取一个字节
        if ((ch1 | ch2) < 0) // 如果两个字节任意一个为负
            throw new EOFException(); // 抛出EOF异常
        return (short)((ch1 << 8) + (ch2 << 0)); // 将第一个字节作为short型的高八位,第二个字节作为short行的低八位,并以short数据类型返回
    }

    public final int readUnsignedShort() throws IOException { // 公有不可继承,从输入流中读入无符号short型的数据
        int ch1 = in.read(); // 从输入流中读取一个字节
        int ch2 = in.read(); // 从输入流中读取一个字节
        if ((ch1 | ch2) < 0) // 如果两个字节任意一个为负
            throw new EOFException(); // 抛出EOF异常
        return (ch1 << 8) + (ch2 << 0); // 将第一个字节作为无符号short型的高八位,第二个字节作为无符号short型的低八位,并以int数据类型返回
    }

    public final char readChar() throws IOException { // 公有不可继承,从输入流中读入char型的数据
        int ch1 = in.read(); // 从输入流中读取一个字节
        int ch2 = in.read(); // 从输入流中读取一个字节
        if ((ch1 | ch2) < 0) // 如果两个字节任意一个为负
            throw new EOFException(); // 抛出EOF异常
        return (char)((ch1 << 8) + (ch2 << 0)); // 将第一个字节作为char型的高八位,第二个字节作为char型的低八位,并以char数据类型返回
    }

    public final int readInt() throws IOException { // 公有不可继承,从输入流中读入int型的数据
        int ch1 = in.read(); // 从输入流中读取第一个字节
        int ch2 = in.read(); // 从输入流中读取第二个字节
        int ch3 = in.read(); // 从输入流中读取第三个字节
        int ch4 = in.read(); // 从输入流中读取第四个字节
        if ((ch1 | ch2 | ch3 | ch4) < 0) // 如果四个字节任意一个为负
            throw new EOFException(); // 抛出EOF异常
        return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0)); // 将第一个字节作为int型的24~31位,第二个字节作为int型的16~23位,第三个字节作为int型的8~15位,第四个字节作为int型的0~7位,并以int数据类型返回
    }

    private byte readBuffer[] = new byte[8]; // 私有数据成员读缓冲区,大小为8个字节

    public final long readLong() throws IOException { // 公有不可继承,从输入流中读入long型的数据
        readFully(readBuffer, 0, 8); // 从输入流中读取8个字节放入readBuffer中
        return (((long)readBuffer[0] << 56) +
                ((long)(readBuffer[1] & 255) << 48) +
                ((long)(readBuffer[2] & 255) << 40) +
                ((long)(readBuffer[3] & 255) << 32) +
                ((long)(readBuffer[4] & 255) << 24) +
                ((readBuffer[5] & 255) << 16) +
                ((readBuffer[6] & 255) <<  8) +
                ((readBuffer[7] & 255) <<  0)); // 8个字节根据先后顺序依次放入long型数据的最高8位到最低8位
    }

    public final float readFloat() throws IOException { // 公有不可继承,从输入流中读入float型的数据
        return Float.intBitsToFloat(readInt()); // 以int形式读取4个字节,将其转化为float,因为float类型的数据也是32位
    }

    public final double readDouble() throws IOException { // 公有不可继承,从输入流中读入double型的数据
        return Double.longBitsToDouble(readLong());// 以long形式读取8个字节,将其转化为double,因为double类型的数据也是64位
    }

    private char lineBuffer[]; // 私有数据成员char型数组lineBuffer

    @Deprecated //这个注释是一个标记注释。所谓标记注释,就是在源程序中加入这个标记后,并不影响程序的编译,但有时编译器会显示一些警告信息。 
    // 如果经常使用eclipse等IDE编写java程序时,可能会经常在属性或方法提示中看到这个词。
    // 如果某个类成员的提示中出现了个词,就表示这个并不建议使用这个类成员。
    // 因为这个类成员在未来的JDK版本中可能被删除。之所以在现在还保留,是因为给那些已经使用了这些类成员的程序一个缓冲期。
    // 如果现在就去了,那么这些程序就无法在新的编译器中编译了。 
    public final String readLine() throws IOException { // 公有不可继承,从输入流中读入一行的数据
        char buf[] = lineBuffer; // 局部变量char型数组buf,与lineBuffer有相同的引用

        if (buf == null) { // 如果buf为空
            buf = lineBuffer = new char[128]; // buf和lineBuffer均置为128个char型的大小
        }

        int room = buf.length; // 空间为buf的长度
        int offset = 0; // 偏移量初始化为0
        int c;

loop:   while (true) { // 循环读取操作
            switch (c = in.read()) {
              case -1: // 如果读入的数据为-1
              case '\n': // 或者读入了换行符
                break loop; //说明该行已读完,跳出循环读取操作

              case '\r': // 如果读入了\r,即将光标移到行首
                int c2 = in.read(); // 再读一个字节
                if ((c2 != '\n') && (c2 != -1)) { // 如果该字节既不是结束符也不是换行符,即正常字符
                    if (!(in instanceof PushbackInputStream)) { // 如果in不是PushbackInputStream类的实例
                        this.in = new PushbackInputStream(in); // 则将in创建为PushbackInputStream的实例
                    }
                    ((PushbackInputStream)in).unread(c2); // PushbackInputStream拥有一个PushBack缓冲区,从PushbackInputStream读出数据后,只要PushBack缓冲区没有满,就可以使用unread()将数据推回流的前端
                }
                break loop; // 跳出循环读取操作

              default: // 如果读入的是其他字符
                if (--room < 0) { // buf已经读够了所能放下的数据量
                    buf = new char[offset + 128]; // 将buf的大小扩大为offset+128
                    room = buf.length - offset - 1; // 剩余空间即为新的大小减去偏移量+1
                    System.arraycopy(lineBuffer, 0, buf, 0, offset); // 将lineBuffer中的数据,即原有数据放入buf中
                    lineBuffer = buf; // lineBuffer与buf重新指向相同的引用
                }
                buf[offset++] = (char) c; // 当前读入的字符放入buf中,计数器offset加一
                break;
            }
        }
        if ((c == -1) && (offset == 0)) { // 如果读入的为-1且无偏移量,说明这是空的一行
            return null; // 返回null
        }
        return String.copyValueOf(buf, 0, offset); // 将字符数组转化为String对象返回
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值