这是一段对输入流进行读取和输出的代码java
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());
输出数据以下:数组
之前一直对这样的while循环理解不足,今天忽然再度写到这样的代码,就想着深刻研究一下。ide
这个方法的官方定义以下工具
/** * 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的知识。this
bit:位。位是电子计算机中最小的数据单位。每一位的状态只能是0或1。比尔盖茨曾经说过,计算机世界都是由0和1组成的。计算机也只能读懂0和1这种二进制数据。编码
byte:字节。8个二进制位构成1个"字节(Byte)",它是存储空间的基本计量单位。即:1byte=8bit。spa
编码:编码的知识量太大,这里只是简单提一下开发中经常使用到的utf-8编码。在utf-8编码中,一个英文字符等于一个字节,一个中文(含繁体)等于三个字节。不管是中文仍是英文,都叫字符。中文标点占三个字节,英文标点占一个字节。也就是说一个“千”字,它要被计算机识别,首先转化成对应它的三个byte字节(对应规则在相关的映射表中),再转化成3*8=24(bit)。最终结果是11100101 10001101 10000011 ,这个二进制数字才能被计算机读懂。而英文字母“Q”的转化简单些,它只须要一个字节去表示 10000001。code
这里分享个在线转换utf-8编码的地址http://www.mytju.com/classcode/tools/encode_utf8.asp对象
我在开发过程当中,遇到的流传输、数据传输,基本上都是用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()方法将缓冲区的数据以特定编码再次输出。
讲一下源码中几个关键的点:
这样来看,这个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(); } }