今天讲的是ByteArrayInputStream流和ByteArrayOutputStream流。
首先让我们来看看ByteArrayOutputStream的源码:
ByteArrayOutputStream.java
package java.io;import java.util.Arrays;public class ByteArrayOutputStream extends OutputStream { protected byte buf[]; protected int count; 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; } 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 { }}
从源码可以看出,该类继承了OutputStream输出流,其中封装了一个byte类型的数组buf作为数据的缓存区,当进行数据写人的时候,数据会不断地写入buf缓存区中,该缓存区默认大小为32字节。每一次写人数据后,都要执行ensureCapacity方法来确定缓存区容量十分能够容纳写入数据,如果不够的时候会执行grow方法,将容量翻倍以适应写入的数据。当写入完成后,我们可以通过toByteArray方法,来获得缓存区数组的一个副本。因为这个特性,我们可以使用ByteArrayOutputStream进行一次性的数据写入。
下面举例说明ByteArrayOutputStream的使用方法:
package ByteArrayIO;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;public class ByteArrayIOTest { public static void main(String[] args) { try { File file = new File("./src/file/test.txt"); FileInputStream fis = new FileInputStream(file); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int len; while ((len = fis.read()) != -1) { baos.write(len); } String messages = baos.toString(); System.out.println(messages); fis.close(); baos.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }}
执行上述代码,可以在控制台看到以下打印:
从打印可以看文件中的所有数据都被存放在了ByteOutputStream中的缓存区中。值得一提的是,ByteArrayOutputStream还介绍到,是否关闭流并不会影响到方法的执行,也就是说,即使流关闭了,数据依然存在。
下面来说说ByteArrayInputStream,首先贴出其源码:
package java.io;publicclass 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() { 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) { k = n < 0 ? 0 : n; } pos += k; return k; } public synchronized int available() { return count - pos; } public boolean markSupported() { return true; } public void mark(int readAheadLimit) { mark = pos; } public synchronized void reset() { pos = mark; } public void close() throws IOException { }}
从源码中可以看出,ByteArrayInputStream完全是基于字节数组来实现的,下面我们来详细分析一下。
ByteArrayInputStream中封装了几个属性:
1.buf数组用于存储从流中读取的数据
2.pos表示下一个即将被读取的字节索引
3.mark表示被标记的索引位置,当调用reset方法时,pos的值重置为mark值。mark方法中的参数无实际意义(也许有,但我没看明白T_T)
4.count表示字节流的总长度。
下面写一个小例子来说明这些方法的使用:
package ByteArrayIO;import java.io.ByteArrayInputStream;public class ByteArrayIOTest2 { public static final int LENGTH = 3; public static final byte[] datas = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A }; public static void main(String[] args) { System.out.println("源数据为:"+new String(datas, 0, datas.length)); new ByteArrayIOTest2().test(); } private void test() { ByteArrayInputStream bais = new ByteArrayInputStream(datas); int len; //将数据源中的数据全部读出,并打印,此时pos应该与count想等 while((len = bais.read()) != -1){ System.out.print((char)len); } //将pos重置为0 bais.reset(); //将pos向后跳跃5个字节 bais.skip(5); //将此处做上标记,下次reset时,pos定位在标记处,这里的参数0无实际意义 bais.mark(0); System.out.println("\r\n"+(char)bais.read()); bais.skip(10); System.out.println((char)bais.read()); bais.reset(); System.out.println((char)bais.read()); }}
同样,ByteAarrayInputStream在流关闭时,也不会影响方法的执行,不会抛出异常,并且数据仍然存在。