這是一段對輸入流進行讀取和輸出的代碼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(); } }