• Java IO源码分析(二)——ByteArrayInputStream 和 ByteArrayOutputStream


    简介

    ByteArrayInputStream 是字节数组输入流,它继承于InputStream。
    它的内部数据存储结构就是字节数组。

    ByteArrayOutputStream是字节数组输出流,它继承于OutputStream。
    它的内部数据存储结构也是字节数组。

    源码分析

    InputStream

    在分析ByteArrayInputStream之前,应该先看InputStream,父类InputStream是ByteArrayInputStream的父类,主要实现了读取和跳跃的方法。

    public abstract class InputStream implements Closeable {
    
        // 最大可跳过的字节数
        private static final int MAX_SKIP_BUFFER_SIZE = 2048;
    
    	// 向后读取一个字节
        public abstract int read() throws IOException;
    
    	// 将字节流中的数据装到字节数组的0位开始的位置
        public int read(byte b[]) throws IOException {
            return read(b, 0, b.length);
        }
    
    	// 将字节流中的数据装到字节数组的指定位置当中
        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) {}
    
    	// 重置
        public synchronized void reset() throws IOException {
            throw new IOException("mark/reset not supported");
        }
    
    	// 是否支持标记方法
        public boolean markSupported() {
            return false;
        }
    
    }
    

    ByteArrayInputStream

    public
    class ByteArrayInputStream extends InputStream {
    
      	// 字节数组,存储数据
        protected byte buf[];
    
    	// 记录当前可读的第一个位置
        protected int pos;
    
    	// 标记的位置
        protected int mark = 0;
    
    	// 数据最大的可读长度
        protected int count;
    
    	// 初始化字节流数组
        public ByteArrayInputStream(byte buf[]) {
            this.buf = buf;
            this.pos = 0;
            this.count = buf.length;
        }
    
    	// 初始化字节流,填入字节数组的指定位置
        public ByteArrayInputStream(byte buf[], int offset, int length) {
            this.buf = buf;
            this.pos = offset;
            // 设置最大可读长度,数组的长度比设置的长度还短,说明传入的长度有问题,就设置为数组的长度
            this.count = Math.min(offset + length, buf.length);
            this.mark = offset;
        }
    
    	// 读取单个字节
        public synchronized int read() {
        	// 这里& 0xff的操作是为了只取低八位
            return (pos < count) ? (buf[pos++] & 0xff) : -1;
        }
    
    	// 读取数据到数组的指定位置
        public synchronized int read(byte b[], int off, int len) {
    		// 边界判断
            if (b == null) {
                throw new NullPointerException();
            } else if (off < 0 || len < 0 || len > b.length - off) {
                throw new IndexOutOfBoundsException();
            }
    		// 读完了
            if (pos >= count) {
                return -1;
            }
    		// 剩余长度
            int avail = count - pos;
            // 如果要求读的长度大于剩余长度,就设置读的长度为剩余长度
            if (len > avail) {
                len = avail;
            }
            if (len <= 0) {
                return 0;
            }
            // 底层采用的
            System.arraycopy(buf, pos, b, off, len);
            pos += len;
            return len;
        }
    
    	// 跳过指定长度的字节
        public synchronized long skip(long n) {
            long k = count - pos;
            if (n < k) {
            	// 如果是负数那么就不动
            	// 取n和k最小的一个
                k = n < 0 ? 0 : n;
            }
    		// 移动k位
            pos += k;
            return k;
        }
    
    	// 是否还有数据可以读
        public synchronized int available() {
            return count - pos;
        }
    
    	// 是否支持标记功能
        public boolean markSupported() {
            return true;
        }
    
    	// 标记当前位置,这个传入参数是个摆设
        public void mark(int readAheadLimit) {
            mark = pos;
        }
    
    	// 重置,也就是将当前指针指向之前mark的位置
        public synchronized void reset() {
            pos = mark;
        }
    
    	// 关闭字节流
        public void close() throws IOException {
        }
    
    }
    
    

    OutputStream

    OutputStream是ByteArrayOutputStream的父类,先看看它的源码。

    很短,实现了Closeable, Flushable。

    public abstract class OutputStream implements Closeable, Flushable {
    
        public abstract void write(int b) throws IOException;
    
        public void write(byte b[]) throws IOException {
            write(b, 0, b.length);
        }
    
        public void write(byte b[], int off, int len) throws IOException {
            if (b == null) {
                throw new NullPointerException();
            } else if ((off < 0) || (off > b.length) || (len < 0) ||
                       ((off + len) > b.length) || ((off + len) < 0)) {
                throw new IndexOutOfBoundsException();
            } else if (len == 0) {
                return;
            }
            for (int i = 0 ; i < len ; i++) {
                write(b[off + i]);
            }
        }
    
        public void flush() throws IOException {
        }
    
        public void close() throws IOException {
        }
    
    }
    

    ByteArrayOutputStream

    public class ByteArrayOutputStream extends OutputStream {
    
    	// 存储数据的字节数组
        protected byte buf[];
    
    	// 数组长度
        protected int count;
    
    	// 默认构造,默认大小是32
        public ByteArrayOutputStream() {
            this(32);
        }
    
    	// 初始化长度的构造
        public ByteArrayOutputStream(int size) {
            if (size < 0) {
                throw new IllegalArgumentException("Negative initial size: "
                                                   + size);
            }
            // 初始化一个数组对象
            buf = new byte[size];
        }
    
    	// 查看是否需要扩容
        private void ensureCapacity(int minCapacity) {
            // overflow-conscious code
            if (minCapacity - buf.length > 0)
                grow(minCapacity);
        }
    
    	// 数组的最大长度
        private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    
    	// 扩容
        private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = buf.length;
            // 默认先扩容两倍
            int newCapacity = oldCapacity << 1;
            // 如果还是不够就将容量扩到需求的大小
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
            // 边界判断
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            // 将旧数组复制到新数组
            buf = Arrays.copyOf(buf, newCapacity);
        }
    	
    	// 边界判断
        private static int hugeCapacity(int minCapacity) {
            if (minCapacity < 0) // overflow
                throw new OutOfMemoryError();
            return (minCapacity > MAX_ARRAY_SIZE) ?
                Integer.MAX_VALUE :
                MAX_ARRAY_SIZE;
        }
    
    	// 写入单个字节
        public synchronized void write(int b) {
        	// 先判断是否需要扩容
            ensureCapacity(count + 1);
            // 写入字节 
            buf[count] = (byte) b;
            // 增加可用长度
            count += 1;
        }
    
    	// 写入字节数组
        public synchronized void write(byte b[], int off, int len) {
            if ((off < 0) || (off > b.length) || (len < 0) ||
                ((off + len) - b.length > 0)) {
                throw new IndexOutOfBoundsException();
            }
            ensureCapacity(count + len);
            // 将数组整个复制过去 
            System.arraycopy(b, off, buf, count, len);
            count += len;
        }
    
    	// 将当前Stream输出到指定的Streamz中
        public synchronized void writeTo(OutputStream out) throws IOException {
        	// 直接将可用长度的数组写入
            out.write(buf, 0, count);
        }
    
    	// 重置
        public synchronized void reset() {
            count = 0;
        }
    
    	// 转化为字符数组
        public synchronized byte toByteArray()[] {
            return Arrays.copyOf(buf, count);
        }
    
    	// 获取可用长度
        public synchronized int size() {
            return count;
        }
    
    	// 转化为字符串
        public synchronized String toString() {
            return new String(buf, 0, count);
        }
    
    	// 根据指定编码转化为字符串
        public synchronized String toString(String charsetName)
            throws UnsupportedEncodingException
        {
            return new String(buf, 0, count, charsetName);
        }
    	
    	
        @Deprecated
        // 获取高位的字节
        public synchronized String toString(int hibyte) {
            return new String(buf, hibyte, 0, count);
        }
    
        public void close() throws IOException {
        }
    
    }
    

    总结

    输入输出流的本质就是一个中间缓存器,暂时将数据放在中间的缓存区,然后根据指定要求输出或如输入。

    ByteArrayInputStream 特点

    • 数组实现中间缓存;
    • 修改和读取操作都是线程安全了,因为加了synchronized;
    • 具有标记回读的功能,就是可以先读后面的数据,然后经过重置,再去读前面标记位置的数据。

    ByteArrayOutputStream特点

    • 数组实现中间缓存;
    • 修改读取也是具有线程安全的;
    • 具有扩容功能,应为想要写入的数据是增长的,在写入之前,就会进行依次扩容判断;
    • 默认的初始大小是32,如果一个个数据写入的扩容,每次是扩一倍的大小;
    • 可以写入到其他的输出流上。
  • 相关阅读:
    Centos7LDAP LDAPadmin的完整部署记录(改良版,其它文档太多坑)
    linux weblogic11g 部署
    redis离线集群安装
    Weblogic11g 10.3部署
    jdk安装部署
    tar.xz文件如何解压
    linux-Centos7安装python3并与python2共存
    ssh免密码登录配置方法
    Docker容器安装weblogic详细教程
    linux命令分块总结---多操作才是真理
  • 原文地址:https://www.cnblogs.com/lippon/p/14117597.html
Copyright © 2020-2023  润新知