ByteArrayOutputStream的研究

这是一段对输入流进行读取和输出的代码

  Resource classPathResource = new ClassPathResource("1.txt");
  InputStream inputStream = pathResource.getInputStream();
  ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
  int i;
  while ((i=inputStream.read())!=-1){
    byteArrayOutputStream.write(i);
  }
  System.out.println(byteArrayOutputStream.toString());

输出数据如下:

200809_ylfh_3490860.png

以前一直对这样的while循环理解不足,今天突然再度写到这样的代码,就想着深入研究一下。

inputStream.read()

这个方法的官方定义如下

    /**
     * Reads the next byte of data from the input stream. The value byte is
     * returned as an <code>int</code> in the range <code>0</code> to
     * <code>255</code>. If no byte is available because the end of the stream
     * has been reached, the value <code>-1</code> is returned. This method
     * blocks until input data is available, the end of the stream is detected,
     * or an exception is thrown.
     *
     * <p> A subclass must provide an implementation of this method.
     *
     * @return     the next byte of data, or <code>-1</code> if the end of the
     *             stream is reached.
     * @exception  IOException  if an I/O error occurs.
     */
    public abstract int read() throws IOException;

就是说,从输入流中读取数据的下一个byte字节,并返回int类型的字节值,这个字节的值范围在0~255之间。如果输入流已经被读完,就返回-1。

这里还是先讲一下byte的知识。

byte

bit:位。位是电子计算机中最小的数据单位。每一位的状态只能是0或1。比尔盖茨曾经说过,计算机世界都是由0和1组成的。计算机也只能读懂0和1这种二进制数据。

byte:字节。8个二进制位构成1个"字节(Byte)",它是存储空间的基本计量单位。即:1byte=8bit。

编码:编码的知识量太大,这里只是简单提一下开发中常用到的utf-8编码。在utf-8编码中,一个英文字符等于一个字节,一个中文(含繁体)等于三个字节。无论是中文还是英文,都叫字符。中文标点占三个字节,英文标点占一个字节。也就是说一个“千”字,它要被计算机识别,首先转化成对应它的三个byte字节(对应规则在相关的映射表中),再转化成3*8=24(bit)。‭最终结果是11100101 10001101 10000011‬ ,这个二进制数字才能被计算机读懂。而英文字母“Q”的转化简单些,它只需要一个字节去表示 ‭10000001‬。

这里分享个在线转换utf-8编码的地址http://www.mytju.com/classcode/tools/encode_utf8.asp

java中的流传输

我在开发过程中,遇到的流传输、数据传输,基本上都是用byte数组,所以这里也主要讲这个。从文章的第一段代码中,不难看出,inputStream.read()将读取到的文件1.txt的二进制数据赋予了变量i,ByteArrayOutputStream负责将i的值通过write()方法记录起来,然后使用toString()方法将记录的数据重新编码输出。这里看了一下ByteArrayOutputStream的底层源码,把重要的方法放上来,并注上中文注释:

public class ByteArrayOutputStream extends OutputStream {

    /**
     *  byte数组,用于存储读取到的byte字节。
     */
    protected byte buf[];

    /**
     * 有效字节数
     */
    protected int count;

    /**
     * 初始化byte[] 数组,32长度
     */
    public ByteArrayOutputStream() {
        this(32);
    }

    public ByteArrayOutputStream(int size) {
        if (size < 0) {
            throw new IllegalArgumentException("Negative initial size: "
                                               + size);
        }
        buf = new byte[size];
    }

    /**
     * byte数组扩容,这是为了防止传入的数据量大于byte[]初始容量
     * */
    private void ensureCapacity(int minCapacity) {
        // overflow-conscious code
        if (minCapacity - buf.length > 0)
            grow(minCapacity);
    }

    /**
     * 定义byte数组最大容量
     */
    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;
    }

    /**
     * 把读取的一个字节放入byte[]
     */
    public synchronized void write(int b) {
        ensureCapacity(count + 1);
        buf[count] = (byte) b;
        count += 1;
    }


    /**
     * 将byte[]数组中存储的字节以平台默认编码(这里是utf-8)重新编码,并返回String类型的数据,也就是文本的内容。
     */
    public synchronized String toString() {
        return new String(buf, 0, count);
    }

源码思路:定义一个byte[]变量,存储所有读到的字节,作为一个缓冲域。这个缓冲域随着对象不断的读入数据而不断增长,当数据量大于缓冲域的空间,就会对缓冲域进行扩容处理。当数据全部读入缓冲区中,里面的内容大概是这样的,buf[]={117,115,101,114...}。最后通过调用toString()方法将缓冲区的数据以特定编码再次输出。

讲一下源码中几个关键的点:

  1.  int newCapacity = oldCapacity << 1; 这里的意思是数组空间扩容1倍。oldCapacity 左移一位,即左边高位去掉,右边地位补0。例:十进制8的二进制表示成 0000 1000 左移1位,8<<1,变成0001 0000 。再转成十进制,即变成16。这就完成了扩容。
  2. new String(buf, 0, count) 表示 将字符数组buf 从第一个字符(java以0开始,也可以说是第0个字符)开始,长度为count的的所有字符构建成一个字符串,也就是String了。这个方法帮我们完成了由字节转化到字符的操作。

 

这样来看,这个ByteArrayOutputStream 其实很简单。我这里自己也写了个根据输入流输出文本数据的工具类,结果发现代码真的差不多,就稍微封装了一下。

/**
 *  千里明月
 */
public class OutputStreamUtil {
        private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
        private static int count=0;
        private static byte[] bytes=new byte[10];

        public static String write(InputStream inputStream) throws IOException {
            int i =0 ;
            while ((i=inputStream.read())!=-1){
                write(i);
            }
            return encode();
        }

        public static void write(int i){
            if (bytes.length<=count){
                grow(count+1);
            }
            bytes[count]= (byte) i;
            System.out.print(bytes[count]);
            count++;
        }

         private static void grow(int capacity) {
            int oldcapacity= bytes.length;
            int newcapacity= oldcapacity<<1;
             if (newcapacity<capacity){
                 newcapacity=capacity;
             }
             if (newcapacity<MAX_ARRAY_SIZE){
                 bytes = Arrays.copyOf(bytes, newcapacity);
             }
         }

        public static String encode(){
            return new String(bytes,0,count);
        }

}

测试类:

public class TestResource {

    public static void main(String[] args) throws IOException {

        Resource classPathResource = new ClassPathResource("1.txt");
        InputStream inputStream = classPathResource.getInputStream();
        String write = OutputStreamUtil.write(inputStream);
        System.out.println(write);
        inputStream.close();
    }
}

 

转载于:https://my.oschina.net/u/3490860/blog/1617718

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值