• Java IO 之 InputStream源码


    Writer      :BYSocket(泥沙砖瓦浆木匠)

    微         博:BYSocket

    豆         瓣:BYSocket

    FaceBook:BYSocket

    Twitter    :BYSocket

    一、InputStream

    InputStream是一个抽象类,即表示所有字节输入流实现类的基类。它的作用就是抽象地表示所有从不同数据源产生输入的类,例如常见的FileInputStream、FilterInputStream等。那些数据源呢?比如:

    1) 字节数组(不代表String类,但可以转换)

    2) String对象

    3) 文件

    4) 一个其他种类的流组成的序列化 (在分布式系统中常见)

    5) 管道(多线程环境中的数据源)

    等等

    二者,注意它是属于字节流部分,而不是字符流(java.io中ReaderWriter,下面会讲到)。

    FilterInputStream是为各种InputStream实现类提供的“装饰器模式”的基类。因此,可以分为原始的字节流和“装饰”过的功能封装字节流。

    二、细解InputStream源码的核心

    源码如下:

    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    /**
     * 所有字节输入流实现类的基类
     */
    public abstract class SInputStream {
     
        // 缓存区字节数组最大值
        private static final int MAX_SKIP_BUFFER_SIZE = 2048;
     
        // 从输入流中读取数据的下一个字节,以int返回
        public abstract int read() throws IOException;
     
        // 从输入流中读取数据的一定数量字节,并存储在缓存数组b
        public int read(byte b[]) throws IOException {
            return read(b, 0, b.length);
        }
     
        // 从输入流中读取数据最多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;
        }
     
        // 跳过输入流中数据的n个字节
        public long skip(long n) throws IOException {
     
            long remaining = n;
            int nr;
     
            if (n <= 0) {
                return 0;
            }
     
            int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
            byte[] skipBuffer = new byte[size];
            while (remaining > 0) {
                nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
                if (nr < 0) {
                    break;
                }
                remaining -= nr;
            }
     
            return n - remaining;
        }
     
        // 返回下一个方法调用能不受阻塞地从此读取(或者跳过)的估计字节数
        public int available() throws IOException {
            return 0;
        }
     
        // 关闭此输入流,并释放与其关联的所有资源
     
        public void close() throws IOException {}
     
        // 在此输出流中标记当前位置
        public synchronized void mark(int readlimit) {}
     
        // 将此流重新定位到最后一次对此输入流调用 mark 方法时的位置。
        public synchronized void reset() throws IOException {
            throw new IOException("mark/reset not supported");
        }
     
        // 测试此输入流是否支持 mark 和 reset 方法
        public boolean markSupported() {
            return false;
        }
     
    }

    其中,InputStream下面三个read方法才是核心方法:

    1
    public abstract int read()

    抽象方法,没有具体实现。因为子类必须实现此方法的一个实现。这就是输入流的关键方法。

    二者,可见下面两个read()方法都调用了这个方法子类的实现来完成功能的。

     

    1
    public int read(byte b[])

    该方法是表示从输入流中读取数据的一定数量字节,并存储在缓存字节数组b。其效果等同于调用了下面方法的实现:

    1
    read(b, 0, b.length)

    如果b的长度为 0,则不读取任何字节并返回 0;否则,尝试读取至少 1 字节。如果因为流位于文件末尾而没有可用的字节,则返回值 -1;否则,至少读取一个字节并将其存储在 b 中。

    思考:这时候,怪不得很多时候, b != –1 或者 b != EOF

     

    1
    public int read(byte b[], int off, int len)


    在输入数据可用、检测到流末尾或者抛出异常前,此方法一直阻塞。

    该方法先进行校验,然后校验下个字节是否为空。如果校验通过后,
    如下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    int i = 1;
    try {
        for (; i < len ; i++) {
            c = read();
            if (c == -1) {
                break;
            }
            b[off + i] = (byte)c;
        }
    catch (IOException ee) {
    }

    将读取的第一个字节存储在元素 b[off] 中,下一个存储在 b[off+1] 中,依次类推。读取的字节数最多等于 len。设 k 为实际读取的字节数;这些字节将存储在 b[off] 到 b[off+k-1] 的元素中,不影响 b[off+k] 到 b[off+len-1] 的元素。



     

    因为有上面两个read的实现,所以这里InputStream设计为抽象类。 

    三、小结

    1. InputSream 对应着 OutputStream

    2. 看源码是享受人家写代码中流露的How

    3. 泥瓦匠学习的代码都在github上(同步osc git),欢迎大家点star,提意见,一起进步。地址:https://github.com/JeffLi1993

  • 相关阅读:
    Spring配置文件中别名的使用
    Spring IOC容器创建对象的方式
    sass安装及使用
    word转html 压缩图片网站
    Yarn 和 Npm 命令行切换 摘录
    react生命周期
    event事件
    Flex 布局
    YYYY-mm-dd HH:MM:SS 时间格式
    页面按钮埋点+跟踪location.search
  • 原文地址:https://www.cnblogs.com/Alandre/p/4844241.html
Copyright © 2020-2023  润新知