Java IO 过滤流 BufferedInput/OutputStream
@author ixenos
概念
BufferedInput/OutputStream是实现缓存的过滤流,他们分别是FilterInput/OutputStream的子类。
BufferedInputStream工作流程
stream-->buf--read buf-->I
1.当一个BufferedInputStream被创建时,一个内部的缓冲区 byte[] buf = new byte[size] 也被建立,size默认是8192也就是默认创建一个8kb大小的缓存空间,
工作时由内部方法fill()预先在缓冲区存储来自连接输入流的数据;最终的数据来源由底层流决定,比如new FileInputStream("a.txt")来源就是a.txt,而我们过滤流的数据来源是节点流!
1 private static int DEFAULT_BUFFER_SIZE = 8192; //默认缓冲数组长度大小,折合单位有8kb 2 protected volatile byte buf[]; //底层维护的缓冲数组变量 3 4 public BufferedInputStream(InputStream in) { 5 this(in, DEFAULT_BUFFER_SIZE); //默认8192 6 } 7 8 9 public BufferedInputStream(InputStream in, int size) { 10 super(in); //引用基类FilterInputStream的构造方法,基类有底层流变量 11 if (size <= 0) { 12 throw new IllegalArgumentException("Buffer size <= 0"); 13 } 14 buf = new byte[size]; //初始化缓冲数组,指定大小为size 15 }
2.当BufferedInputStream的read方法被调用时,数据将从缓冲区中移出,而不是底层的输入流;
3.当BufferedInputStream缓冲区数据用完时,他自动从底层输入流中补充数据。
read()源码分析:
//返回下一个数据字节,如果到达流末尾则返回-1,数据流向返回值!
1 public synchronized int read() throws IOException { 2 if (pos >= count) { //当缓存指针等于或者超过count时,说明buf已满 3 fill(); //fill()从节点流中读取数据存放到buf缓冲区 4 if (pos >= count) 5 return -1; 6 } 7 return getBufIfOpen()[pos++] & 0xff; //得到非空buf缓冲当前索引为pos位置的一字节数据,pos++ 缓冲区指针移动一位 8 } 9 10 //getBufIfOpen对buf进行非空判断 11 private byte[] getBufIfOpen() throws IOException { 12 byte[] buffer = buf; //用一个引用变量引用buf 13 if (buffer == null) 14 throw new IOException("Stream closed"); 15 return buffer; //返回buffer,实质上指向buf 16 }
read(byte[], int, int)源码分析 :
//pos是起始/下一次读取buffer的位置,
//markpos是最新一次已读取的位置,
//count是缓冲数组被填充的大小-1(因为是作为index存在的,从0开始)
//len是b要读取的最大字节数
1 //返回读取的字节数,如果流到达末尾返回-1,数据流向b数组! 2 public synchronized int read(byte b[], int off, int len) 3 throws IOException 4 { 5 6 /*这里只做检查,流关闭则提前抛出,以避免拖到fill()中的getBufIfOpen()再抛出的情况 7 private byte[] getBufIfOpen() throws IOException { 8 byte[] buffer = buf; //引用传递,指向当前buf变量指向的内存 9 if (buffer == null) //就为了这个非空判断而已 10 throw new IOException("Stream closed"); 11 return buffer; //这里返回值是没人接收的,方法结束抛弃局部变量buffer,不对buf造成影响 12 } 13 */ 14 getBufIfOpen(); // Check for closed stream 关闭流将抛出异常 15 //指定数组的越界判断 16 if ((off | len | (off + len) | (b.length - (off + len))) < 0) { 17 throw new IndexOutOfBoundsException(); 18 } else if (len == 0) { 19 return 0; 20 } 21 22 int n = 0; 23 //循环写数据到b,直到buffer数据不足返回-1,此时nread已累加记录 24 for (;;) { 25 int nread = read1(b, off + n, len - n);//读缓冲区数据到b 26 if (nread <= 0) 27 return (n == 0) ? nread : n; 28 n += nread; 29 //读完指定长度时返回 30 if (n >= len) 31 return n; 32 // if not closed but no bytes available, return 33 //没读完指定长度,但缓冲区没数据时返回当前n 34 InputStream input = in; 35 if (input != null && input.available() <= 0) 36 return n; 37 } 38 } 39 40 41 /** 42 * read1方法是该read底层用来读取缓冲区buf的数据进指定数组的方法 43 * 而fill方法又是read1方法中用来读取底层字节流到缓冲区buf的方法 44 * 读取数据写进指定数组的一部分,必要时每次从底层流尽量读取数据 45 */ 46 private int read1(byte[] b, int off, int len) throws IOException { 47 int avail = count - pos; //开始时为0 48 if (avail <= 0) { 49 /* 如果所请求的长度大于等于缓冲区,并且还没读取过buf
50 (即刚开始使用时,扩充缓冲区成本不值得,回归底层流), 51 就没必要用缓冲区了,将直接从底层流读取数据。
52 */ 53 if (len >= getBufIfOpen().length && markpos < 0) { 54 //直接调用底层流的read方法,写进指定的内存b 55 return getInIfOpen().read(b, off, len); 56 } 57 //请求fill()方法来读取底层流数据(多次使用后如果长度大于缓冲区,为了数据保护,将扩充缓冲区) 58 fill(); 59 avail = count - pos; //avail是 60 if (avail <= 0) return -1; 61 } 62 int cnt = (avail < len) ? avail : len; //cnt是要写入b的字节数,如果b中剩余字节数avail比要刷入的数据长度len大,则cnt为len 63 System.arraycopy(getBufIfOpen(), pos, b, off, cnt); //复制指定范围非空buf数据到b 64 pos += cnt; 65 return cnt; 66 } 67 68 69 //fill()方法填充空间,是底层用来读取流数据到缓冲区buf 70 /** 71 * Fills the buffer with more data, taking into account 72 * shuffling and other tricks for dealing with marks. 73 * Assumes that it is being called by a synchronized method. 74 * This method also assumes that all data has already been read in, 75 * hence pos > count. 76 */ 77 private void fill() throws IOException { 78 byte[] buffer = getBufIfOpen(); 79 //pos是起始/下一次读取buffer的位置,markpos是最新一次已读取的位置,count是缓冲数组被填充的大小-1(因为是作为index存在的,从0开始) 80 //起始小于零表明没有pos移动,从源码可知 81 if (markpos < 0) 82 pos = 0; /* no mark: throw away the buffer */ 83 //当pos比buffer更长时,即 84 else if (pos >= buffer.length) /* no room left in buffer */ 85 //markpos也有前移时,数组自我复制丢弃早期部分 86 if (markpos > 0) { /* can throw away early part of the buffer */ 87 int sz = pos - markpos; 88 System.arraycopy(buffer, markpos, buffer, 0, sz); 89 pos = sz; 90 markpos = 0; 91 //buffer大小超过理论规模时,重置,通过改变pos“丢弃”缓冲区内容 92 } else if (buffer.length >= marklimit) { 93 markpos = -1; /* buffer got too big, invalidate mark */ 94 pos = 0; /* drop buffer contents */ 95 //buffer大小超过本地VM内存限制:MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8 96 } else if (buffer.length >= MAX_BUFFER_SIZE) { 97 throw new OutOfMemoryError("Required array size too large"); 98 } else { /* grow buffer */ //没有顾忌时,扩充缓冲区大小 99 int nsz = (pos <= MAX_BUFFER_SIZE - pos) ? 100 pos * 2 : MAX_BUFFER_SIZE; 101 if (nsz > marklimit) 102 nsz = marklimit; 103 byte nbuf[] = new byte[nsz]; 104 System.arraycopy(buffer, 0, nbuf, 0, pos); 105 /*AtomicReferenceFieldUpdater是一个基于反射的工具类, 106 它能对指定类的指定的volatile引用字段进行原子更新。 107 (注意这个字段不能是private的) , 108 从源码知getBufIfOpen返回的是值传递的protected volatile byte buf[] 109 */ 110 if (!bufUpdater.compareAndSet(this, buffer, nbuf)) { 111 // Can't replace buf if there was an async close. 112 // Note: This would need to be changed if fill() 113 // is ever made accessible to multiple threads. 114 // But for now, the only way CAS can fail is via close. 115 // assert buf == null; 116 throw new IOException("Stream closed"); 117 } 118 buffer = nbuf; 119 } 120 //pos小于buf.length时,从底层流到缓冲区,使用InputStream的read(byte[])方法 121 count = pos; 122 int n = getInIfOpen().read(buffer, pos, buffer.length - pos); 123 if (n > 0) 124 count = n + pos; 125 } 126
总结:通过以上源码我们可以看到:
1)read(byte[])方法运行初始就用getBufIfOpen判断流关闭没有,因为缓冲流关闭后缓冲区失效,getBufIfOpen将抛出异常;
2)read(byte[])方法中的read1方法是该read底层,用来读取缓冲区buf的数据进指定数组的方法,而fill方法又是read1方法中,用来读取底层字节流到缓冲区buf的方法,层层封装外包;
3)read(byte[]) <-- read1(byte[]) <-- fill() <-- getInIfOpen().read(buf) <-- InputStream 层层外包,
这里getInIfOpen()返回一个非空底层流InputStream,并调用底层流的read(byte[])方法读取流数据到缓冲区buf,
所以当我们只是关闭底层流这些嵌套流也跟着“关闭”,但是会留下缓冲数组buf,这有时可以利用,有时要注意关闭避免浪费资源;
以下是close()的源码分析:
1 1 public void close() throws IOException { 2 2 byte[] buffer; 3 3 while ( (buffer = buf) != null) { 4 4 if (bufUpdater.compareAndSet(this, buffer, null)) { // 会将缓冲区buf的数据清空并释放缓冲区,也就是buf=null 5 5 InputStream input = in; //引用传递,input和in同指向节点流对象 6 6 in = null; //修改强引用,使底层流引用变量失效,即任何人都不能通过in去引用底层流了 7 7 if (input != null) //然后交给input单独来非空判断和执行关闭操作 8 8 input.close(); //调用节点流的close(),和native相关 9 9 return; 10 10 } 11 11 // Else retry in case a new buf was CASed in fill() 12 12 } 13 13 }
4)当BufferedInputStream缓冲区数据用完时,自动从底层输入流中补充数据(stream-->buf--read buf-->I)
5)关于read(byte[])缓冲区buf空间大小限制性能的问题:
(1)一开始使用缓冲流时,如果请求填充的长度len大于缓冲区大小buf.length,将直接使用底层流的read(byte[])方法,原因是扩充缓冲区的成本大于缓冲带来的便利;
(2)多次使用缓冲流后,一样如果请求填充的长度len大于缓冲区大小buf.length,为了数据的完整安全,在内部的fill()方法中扩充缓冲区大小,而这要付出相应成本限制性能,所以缓冲区大小要谨慎设定。
6)在缓冲区可用数据小于目标数组b的时候,使用System.arraycopy整体将缓冲区从pos位置起的数据覆盖到目标数组上
1 int cnt = (avail < len) ? avail : len; //cnt是要写入b的字节数,如果b中剩余字节数avail比要刷入的数据长度len大,则cnt为len 2 3 System.arraycopy(getBufIfOpen(), pos, b, off, cnt); //复制指定范围非空buf数据到b
BufferedOutputStream工作流程
流程:I--write buf-->buf-->stream,我们通过两个write输出我们的数据,而数据暂时积聚在buf中,等待flush
1.当一个BufferedOutputStream被创建时,一个内部的缓冲区 byte[] buf = new byte[size] 也被建立,size默认是8192也就是默认创建一个8kb大小的缓存空间,
最终的数据去向由底层流决定,比如new FileOutputStream("a.txt")去向就是a.txt,而我们过滤流的数据来源是节点流!
2.BufferedOutputStream在内部缓冲区存储程序的输出数据,这样就不会每次调用write方法时,就把数据写到底层的输出流;
3.当BufferedOutputStream的内部缓冲区满或者它被刷新(flush),数据一次性写到底层的输出流。
constructor源码分析:
1 public BufferedOutputStream(OutputStream out) { 2 this(out, 8192); 3 } 4 5 //这里与BufferedInputStream同理,不再赘述 6 public BufferedOutputStream(OutputStream out, int size) { 7 super(out); 8 if (size <= 0) { 9 throw new IllegalArgumentException("Buffer size <= 0"); 10 } 11 buf = new byte[size]; 12 }
write(int)源码分析:
1 public synchronized void write(int b) throws IOException { 2 if (count >= buf.length) { //当count++到buf.length的长度,表明缓冲区已满 3 flushBuffer(); //刷出缓存到底层字节输出流 4 } 5 buf[count++] = (byte)b; //此时缓冲区本身未满或者flush后空了(count=0),先把b放在缓冲区buf中 6 } 7 8 9 /*刷出内部的缓存到底层字节输出流*/ 10 private void flushBuffer() throws IOException { 11 if (count > 0) { 12 out.write(buf, 0, count); //实质上利用底层流的write(byte[]) 13 count = 0; //全部刷出,count=0,作为标志位与buf[count++]联合来刷新 14 } 15 }
write(byte[], int, int)源码分析:
1 public synchronized void write(byte b[], int off, int len) throws IOException { 2 3 /* 在指定数组数据到buf时,如果数据的长度比buf长, 4 可能在传输过程中需要发出超长len,所以将buf中之前的工作数据刷出, 5 然后直接调用底层流的write(byte[])方法输出, 6 这个判断暗地里也限制了初始时就len过长的情况(count=0) 7 */ 8 if (len >= buf.length) { 9 flushBuffer(); 10 out.write(b, off, len); 11 return; 12 } 13 14 /*表明缓冲区已满,刷出*/ 15 if (len > buf.length - count) { 16 flushBuffer(); 17 } 18 19 /*在经过上面的判断后,说明len<buf.length且count>0, 20 已使用过buf,只是剩余空间不足以承载,刷出数据后, 21 利用arraycopy将数据存入buf 22 */ 23 System.arraycopy(b, off, buf, count, len); 24 count += len; //count增加填充的字节数 25 }
flush()源码分析:
1 public synchronized void flush() throws IOException { 2 /*刷出缓冲区的数据到基流*/ 3 flushBuffer(); 4 5 /* 调用基流的flush方法,这里考虑到了嵌套流, 6 普通的节点流flush方法是空方法, 7 如果嵌套的依旧是一个带缓存的流,那么可递归每一层的flush 8 这迫使任何缓存输出的字节被写到底层流上 9 */ 10 out.flush(); 11 }
总结:通过以上源码我们可以看到:
1)write(int)时,若buf已满,将自动刷出buf中的数据到流;
2)刷出到流的内部方法flushBuffer利用的是基流的write(byte[], int, int)方法;
3)没有close方法,只给关闭底层流,缓存输出流无需关闭;
4)flush方法调用基流的flush方法,这里考虑到了嵌套流,因为普通的节点流flush方法是空方法!
如果嵌套的依旧是一个带缓存的流,那么可递归flush,这迫使任何缓存输出的字节被写到底层流上;
5) write(byte[], int, int)时,
(1)如果数据的长度比buf长,可能在传输过程中需要发出超长len,所以将buf中之前的工作数据刷出,然后直接调用底层流的write(byte[])方法自行输出,这个判断暗地里也限制了初始时就len过长的情况(count=0);
(2)无需刷出缓存的情况下,使用arraycopy把要输出的byte[]数据,从count位置开始,拷贝到buf中。
和节点流ByteArrayInput/OutputStream的区别
1.ByteArrayInputStream是节点流;BufferedInputStream是过滤流;
2.ByteArrayInputStream需要使用者提供一个缓冲数组,是自带缓冲功能的流,由构造器传入;BufferedInputStream默认自己创建一个缓冲数组,默认4096长度,即4kB大小;
3.ByteArrayInputStream利用继承关系扩展流的功能;BufferedInputStream利用装饰模式扩展流的功能;
4.类中方法的原理都差不多,具体参阅源码
1 /* 2 * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. 3 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16 * 17 * 18 * 19 * 20 * 21 * 22 * 23 * 24 */ 25 26 package java.io; 27 28 /** 29 * A <code>ByteArrayInputStream</code> contains 30 * an internal buffer that contains bytes that 31 * may be read from the stream. An internal 32 * counter keeps track of the next byte to 33 * be supplied by the <code>read</code> method. 34 * <p> 35 * Closing a <tt>ByteArrayInputStream</tt> has no effect. The methods in 36 * this class can be called after the stream has been closed without 37 * generating an <tt>IOException</tt>. 38 * 39 * @author Arthur van Hoff 40 * @see java.io.StringBufferInputStream 41 * @since JDK1.0 42 */ 43 public 44 class ByteArrayInputStream extends InputStream { 45 46 /** 47 * An array of bytes that was provided 48 * by the creator of the stream. Elements <code>buf[0]</code> 49 * through <code>buf[count-1]</code> are the 50 * only bytes that can ever be read from the 51 * stream; element <code>buf[pos]</code> is 52 * the next byte to be read. 53 */ 54 protected byte buf[]; 55 56 /** 57 * The index of the next character to read from the input stream buffer. 58 * This value should always be nonnegative 59 * and not larger than the value of <code>count</code>. 60 * The next byte to be read from the input stream buffer 61 * will be <code>buf[pos]</code>. 62 */ 63 protected int pos; 64 65 /** 66 * The currently marked position in the stream. 67 * ByteArrayInputStream objects are marked at position zero by 68 * default when constructed. They may be marked at another 69 * position within the buffer by the <code>mark()</code> method. 70 * The current buffer position is set to this point by the 71 * <code>reset()</code> method. 72 * <p> 73 * If no mark has been set, then the value of mark is the offset 74 * passed to the constructor (or 0 if the offset was not supplied). 75 * 76 * @since JDK1.1 77 */ 78 protected int mark = 0; 79 80 /** 81 * The index one greater than the last valid character in the input 82 * stream buffer. 83 * This value should always be nonnegative 84 * and not larger than the length of <code>buf</code>. 85 * It is one greater than the position of 86 * the last byte within <code>buf</code> that 87 * can ever be read from the input stream buffer. 88 */ 89 protected int count; 90 91 /** 92 * Creates a <code>ByteArrayInputStream</code> 93 * so that it uses <code>buf</code> as its 94 * buffer array. 95 * The buffer array is not copied. 96 * The initial value of <code>pos</code> 97 * is <code>0</code> and the initial value 98 * of <code>count</code> is the length of 99 * <code>buf</code>. 100 * 101 * @param buf the input buffer. 102 */ 103 public ByteArrayInputStream(byte buf[]) { 104 this.buf = buf; 105 this.pos = 0; 106 this.count = buf.length; 107 } 108 109 /** 110 * Creates <code>ByteArrayInputStream</code> 111 * that uses <code>buf</code> as its 112 * buffer array. The initial value of <code>pos</code> 113 * is <code>offset</code> and the initial value 114 * of <code>count</code> is the minimum of <code>offset+length</code> 115 * and <code>buf.length</code>. 116 * The buffer array is not copied. The buffer's mark is 117 * set to the specified offset. 118 * 119 * @param buf the input buffer. 120 * @param offset the offset in the buffer of the first byte to read. 121 * @param length the maximum number of bytes to read from the buffer. 122 */ 123 public ByteArrayInputStream(byte buf[], int offset, int length) { 124 this.buf = buf; 125 this.pos = offset; 126 this.count = Math.min(offset + length, buf.length); 127 this.mark = offset; 128 } 129 130 /** 131 * Reads the next byte of data from this input stream. The value 132 * byte is returned as an <code>int</code> in the range 133 * <code>0</code> to <code>255</code>. If no byte is available 134 * because the end of the stream has been reached, the value 135 * <code>-1</code> is returned. 136 * <p> 137 * This <code>read</code> method 138 * cannot block. 139 * 140 * @return the next byte of data, or <code>-1</code> if the end of the 141 * stream has been reached. 142 */ 143 public synchronized int read() { 144 return (pos < count) ? (buf[pos++] & 0xff) : -1; 145 } 146 147 /** 148 * Reads up to <code>len</code> bytes of data into an array of bytes 149 * from this input stream. 150 * If <code>pos</code> equals <code>count</code>, 151 * then <code>-1</code> is returned to indicate 152 * end of file. Otherwise, the number <code>k</code> 153 * of bytes read is equal to the smaller of 154 * <code>len</code> and <code>count-pos</code>. 155 * If <code>k</code> is positive, then bytes 156 * <code>buf[pos]</code> through <code>buf[pos+k-1]</code> 157 * are copied into <code>b[off]</code> through 158 * <code>b[off+k-1]</code> in the manner performed 159 * by <code>System.arraycopy</code>. The 160 * value <code>k</code> is added into <code>pos</code> 161 * and <code>k</code> is returned. 162 * <p> 163 * This <code>read</code> method cannot block. 164 * 165 * @param b the buffer into which the data is read. 166 * @param off the start offset in the destination array <code>b</code> 167 * @param len the maximum number of bytes read. 168 * @return the total number of bytes read into the buffer, or 169 * <code>-1</code> if there is no more data because the end of 170 * the stream has been reached. 171 * @exception NullPointerException If <code>b</code> is <code>null</code>. 172 * @exception IndexOutOfBoundsException If <code>off</code> is negative, 173 * <code>len</code> is negative, or <code>len</code> is greater than 174 * <code>b.length - off</code> 175 */ 176 public synchronized int read(byte b[], int off, int len) { 177 if (b == null) { 178 throw new NullPointerException(); 179 } else if (off < 0 || len < 0 || len > b.length - off) { 180 throw new IndexOutOfBoundsException(); 181 } 182 183 if (pos >= count) { 184 return -1; 185 } 186 187 int avail = count - pos; 188 if (len > avail) { 189 len = avail; 190 } 191 if (len <= 0) { 192 return 0; 193 } 194 System.arraycopy(buf, pos, b, off, len); 195 pos += len; 196 return len; 197 } 198 199 /** 200 * Skips <code>n</code> bytes of input from this input stream. Fewer 201 * bytes might be skipped if the end of the input stream is reached. 202 * The actual number <code>k</code> 203 * of bytes to be skipped is equal to the smaller 204 * of <code>n</code> and <code>count-pos</code>. 205 * The value <code>k</code> is added into <code>pos</code> 206 * and <code>k</code> is returned. 207 * 208 * @param n the number of bytes to be skipped. 209 * @return the actual number of bytes skipped. 210 */ 211 public synchronized long skip(long n) { 212 long k = count - pos; 213 if (n < k) { 214 k = n < 0 ? 0 : n; 215 } 216 217 pos += k; 218 return k; 219 } 220 221 /** 222 * Returns the number of remaining bytes that can be read (or skipped over) 223 * from this input stream. 224 * <p> 225 * The value returned is <code>count - pos</code>, 226 * which is the number of bytes remaining to be read from the input buffer. 227 * 228 * @return the number of remaining bytes that can be read (or skipped 229 * over) from this input stream without blocking. 230 */ 231 public synchronized int available() { 232 return count - pos; 233 } 234 235 /** 236 * Tests if this <code>InputStream</code> supports mark/reset. The 237 * <code>markSupported</code> method of <code>ByteArrayInputStream</code> 238 * always returns <code>true</code>. 239 * 240 * @since JDK1.1 241 */ 242 public boolean markSupported() { 243 return true; 244 } 245 246 /** 247 * Set the current marked position in the stream. 248 * ByteArrayInputStream objects are marked at position zero by 249 * default when constructed. They may be marked at another 250 * position within the buffer by this method. 251 * <p> 252 * If no mark has been set, then the value of the mark is the 253 * offset passed to the constructor (or 0 if the offset was not 254 * supplied). 255 * 256 * <p> Note: The <code>readAheadLimit</code> for this class 257 * has no meaning. 258 * 259 * @since JDK1.1 260 */ 261 public void mark(int readAheadLimit) { 262 mark = pos; 263 } 264 265 /** 266 * Resets the buffer to the marked position. The marked position 267 * is 0 unless another position was marked or an offset was specified 268 * in the constructor. 269 */ 270 public synchronized void reset() { 271 pos = mark; 272 } 273 274 /** 275 * Closing a <tt>ByteArrayInputStream</tt> has no effect. The methods in 276 * this class can be called after the stream has been closed without 277 * generating an <tt>IOException</tt>. 278 */ 279 public void close() throws IOException { 280 } 281 282 }
1 /* 2 * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. 3 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16 * 17 * 18 * 19 * 20 * 21 * 22 * 23 * 24 */ 25 26 package java.io; 27 28 import java.util.Arrays; 29 30 /** 31 * This class implements an output stream in which the data is 32 * written into a byte array. The buffer automatically grows as data 33 * is written to it. 34 * The data can be retrieved using <code>toByteArray()</code> and 35 * <code>toString()</code>. 36 * <p> 37 * Closing a <tt>ByteArrayOutputStream</tt> has no effect. The methods in 38 * this class can be called after the stream has been closed without 39 * generating an <tt>IOException</tt>. 40 * 41 * @author Arthur van Hoff 42 * @since JDK1.0 43 */ 44 45 public class ByteArrayOutputStream extends OutputStream { 46 47 /** 48 * The buffer where data is stored. 49 */ 50 protected byte buf[]; 51 52 /** 53 * The number of valid bytes in the buffer. 54 */ 55 protected int count; 56 57 /** 58 * Creates a new byte array output stream. The buffer capacity is 59 * initially 32 bytes, though its size increases if necessary. 60 */ 61 public ByteArrayOutputStream() { 62 this(32); 63 } 64 65 /** 66 * Creates a new byte array output stream, with a buffer capacity of 67 * the specified size, in bytes. 68 * 69 * @param size the initial size. 70 * @exception IllegalArgumentException if size is negative. 71 */ 72 public ByteArrayOutputStream(int size) { 73 if (size < 0) { 74 throw new IllegalArgumentException("Negative initial size: " 75 + size); 76 } 77 buf = new byte[size]; 78 } 79 80 /** 81 * Increases the capacity if necessary to ensure that it can hold 82 * at least the number of elements specified by the minimum 83 * capacity argument. 84 * 85 * @param minCapacity the desired minimum capacity 86 * @throws OutOfMemoryError if {@code minCapacity < 0}. This is 87 * interpreted as a request for the unsatisfiably large capacity 88 * {@code (long) Integer.MAX_VALUE + (minCapacity - Integer.MAX_VALUE)}. 89 */ 90 private void ensureCapacity(int minCapacity) { 91 // overflow-conscious code 92 if (minCapacity - buf.length > 0) 93 grow(minCapacity); 94 } 95 96 /** 97 * The maximum size of array to allocate. 98 * Some VMs reserve some header words in an array. 99 * Attempts to allocate larger arrays may result in 100 * OutOfMemoryError: Requested array size exceeds VM limit 101 */ 102 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; 103 104 /** 105 * Increases the capacity to ensure that it can hold at least the 106 * number of elements specified by the minimum capacity argument. 107 * 108 * @param minCapacity the desired minimum capacity 109 */ 110 private void grow(int minCapacity) { 111 // overflow-conscious code 112 int oldCapacity = buf.length; 113 int newCapacity = oldCapacity << 1; 114 if (newCapacity - minCapacity < 0) 115 newCapacity = minCapacity; 116 if (newCapacity - MAX_ARRAY_SIZE > 0) 117 newCapacity = hugeCapacity(minCapacity); 118 buf = Arrays.copyOf(buf, newCapacity); 119 } 120 121 private static int hugeCapacity(int minCapacity) { 122 if (minCapacity < 0) // overflow 123 throw new OutOfMemoryError(); 124 return (minCapacity > MAX_ARRAY_SIZE) ? 125 Integer.MAX_VALUE : 126 MAX_ARRAY_SIZE; 127 } 128 129 /** 130 * Writes the specified byte to this byte array output stream. 131 * 132 * @param b the byte to be written. 133 */ 134 public synchronized void write(int b) { 135 ensureCapacity(count + 1); 136 buf[count] = (byte) b; 137 count += 1; 138 } 139 140 /** 141 * Writes <code>len</code> bytes from the specified byte array 142 * starting at offset <code>off</code> to this byte array output stream. 143 * 144 * @param b the data. 145 * @param off the start offset in the data. 146 * @param len the number of bytes to write. 147 */ 148 public synchronized void write(byte b[], int off, int len) { 149 if ((off < 0) || (off > b.length) || (len < 0) || 150 ((off + len) - b.length > 0)) { 151 throw new IndexOutOfBoundsException(); 152 } 153 ensureCapacity(count + len); 154 System.arraycopy(b, off, buf, count, len); 155 count += len; 156 } 157 158 /** 159 * Writes the complete contents of this byte array output stream to 160 * the specified output stream argument, as if by calling the output 161 * stream's write method using <code>out.write(buf, 0, count)</code>. 162 * 163 * @param out the output stream to which to write the data. 164 * @exception IOException if an I/O error occurs. 165 */ 166 public synchronized void writeTo(OutputStream out) throws IOException { 167 out.write(buf, 0, count); 168 } 169 170 /** 171 * Resets the <code>count</code> field of this byte array output 172 * stream to zero, so that all currently accumulated output in the 173 * output stream is discarded. The output stream can be used again, 174 * reusing the already allocated buffer space. 175 * 176 * @see java.io.ByteArrayInputStream#count 177 */ 178 public synchronized void reset() { 179 count = 0; 180 } 181 182 /** 183 * Creates a newly allocated byte array. Its size is the current 184 * size of this output stream and the valid contents of the buffer 185 * have been copied into it. 186 * 187 * @return the current contents of this output stream, as a byte array. 188 * @see java.io.ByteArrayOutputStream#size() 189 */ 190 public synchronized byte toByteArray()[] { 191 return Arrays.copyOf(buf, count); 192 } 193 194 /** 195 * Returns the current size of the buffer. 196 * 197 * @return the value of the <code>count</code> field, which is the number 198 * of valid bytes in this output stream. 199 * @see java.io.ByteArrayOutputStream#count 200 */ 201 public synchronized int size() { 202 return count; 203 } 204 205 /** 206 * Converts the buffer's contents into a string decoding bytes using the 207 * platform's default character set. The length of the new <tt>String</tt> 208 * is a function of the character set, and hence may not be equal to the 209 * size of the buffer. 210 * 211 * <p> This method always replaces malformed-input and unmappable-character 212 * sequences with the default replacement string for the platform's 213 * default character set. The {@linkplain java.nio.charset.CharsetDecoder} 214 * class should be used when more control over the decoding process is 215 * required. 216 * 217 * @return String decoded from the buffer's contents. 218 * @since JDK1.1 219 */ 220 public synchronized String toString() { 221 return new String(buf, 0, count); 222 } 223 224 /** 225 * Converts the buffer's contents into a string by decoding the bytes using 226 * the named {@link java.nio.charset.Charset charset}. The length of the new 227 * <tt>String</tt> is a function of the charset, and hence may not be equal 228 * to the length of the byte array. 229 * 230 * <p> This method always replaces malformed-input and unmappable-character 231 * sequences with this charset's default replacement string. The {@link 232 * java.nio.charset.CharsetDecoder} class should be used when more control 233 * over the decoding process is required. 234 * 235 * @param charsetName the name of a supported 236 * {@link java.nio.charset.Charset charset} 237 * @return String decoded from the buffer's contents. 238 * @exception UnsupportedEncodingException 239 * If the named charset is not supported 240 * @since JDK1.1 241 */ 242 public synchronized String toString(String charsetName) 243 throws UnsupportedEncodingException 244 { 245 return new String(buf, 0, count, charsetName); 246 } 247 248 /** 249 * Creates a newly allocated string. Its size is the current size of 250 * the output stream and the valid contents of the buffer have been 251 * copied into it. Each character <i>c</i> in the resulting string is 252 * constructed from the corresponding element <i>b</i> in the byte 253 * array such that: 254 * <blockquote><pre> 255 * c == (char)(((hibyte & 0xff) << 8) | (b & 0xff)) 256 * </pre></blockquote> 257 * 258 * @deprecated This method does not properly convert bytes into characters. 259 * As of JDK 1.1, the preferred way to do this is via the 260 * <code>toString(String enc)</code> method, which takes an encoding-name 261 * argument, or the <code>toString()</code> method, which uses the 262 * platform's default character encoding. 263 * 264 * @param hibyte the high byte of each resulting Unicode character. 265 * @return the current contents of the output stream, as a string. 266 * @see java.io.ByteArrayOutputStream#size() 267 * @see java.io.ByteArrayOutputStream#toString(String) 268 * @see java.io.ByteArrayOutputStream#toString() 269 */ 270 @Deprecated 271 public synchronized String toString(int hibyte) { 272 return new String(buf, hibyte, 0, count); 273 } 274 275 /** 276 * Closing a <tt>ByteArrayOutputStream</tt> has no effect. The methods in 277 * this class can be called after the stream has been closed without 278 * generating an <tt>IOException</tt>. 279 */ 280 public void close() throws IOException { 281 } 282 283 }