我們到底能走多遠系列(3)
扯淡:
現在項目里的java代碼,都是封裝,封裝,再封裝。在沒有空閑的趕工編碼,幾年后會感覺學不動,畢竟少了很多思考的時間。也基本不會去研究代碼的底層實現。時間一長就會覺得自己什么也不會。
一個大型的項目,一般不可能只用java實現,可能會用到C,C++,shell,python等等。單單一個web就要學很多,jsp,jquery,javascript,html,css,各種開源的不開源的框架,各種web服務器,數據庫等等等。java程序員的確容易迷茫,但精通一樣吧,怕找不到工作,全學吧,怕一直是碼農。
其實,我覺得大多數的人都只是希望能做到“工程師”,解決問題的人。我看到現在接近40的程序員也還是蠻吃香的,他們的能力也是這樣慢慢碼過來的。和他們交流的時候感覺他們什么都知道點,也有自己及其精通的一面。這個方向似乎也是一種不錯的職業規划。
-----------------------------------------------------------------------------
總之,不要停止學習,不要停止進步。
-----------------------------------------------------------------------------
BufferedInputStream應該比較實用吧。讀讀源碼,學習下。
繼承結構:
BufferedInputStream ----> FilterInputStream -----> InputStream
概述:
FilterInputStream繼承自InputStream屬於輸入流中的鏈接流,同時引用了InputStream,將 InputStream封裝成一個內部變量,同時構造方法上需要傳入一個InputStream。這是一個典型的裝飾器模式,他的任何子類都可以對一個繼 承自InputStream的原始流或其他鏈接流進行裝飾,如我們常用的使用BufferedInputStream對FileInputStream進 行裝飾,使普通的文件輸入流具備了內存緩存的功能,通過內存緩沖減少磁盤io次數。
BufferedInputStream方法一覽:
private void fill() private byte[] getBufIfOpen() private InputStream getInIfOpen() private int read1(byte[] b, int off, int len) ------------------------------------------------- public BufferedInputStream(InputStream in) public BufferedInputStream(InputStream in, int size) -------------------------------------------------- public synchronized int available() 返回此輸入流方法的下一個調用方可以不受阻塞地從此輸入流讀取(或跳過)的字節數。 public void close() 關閉此輸入流並釋放與該流關聯的所有系統資源。 public synchronized void mark(int readlimit) 在此輸入流中標記當前的位置。對 reset 方法的后續調用會在最后標記的位置重新定位此流,以便后續讀取重新讀取相同的字節。 readlimit - 在標記位置失效前可以讀取字節的最大限制。 public boolean markSupported() 測試此輸入流是否支持 mark 和 reset 方法。 public synchronized int read() 從輸入流讀取下一個數據字節。返回 0 到 255 范圍內的 int 字節值。如果因已到達流末尾而沒有可用的字節,則返回值 -1。 public synchronized int read(byte b[], int off, int len) 將輸入流中最多 len 個數據字節讀入字節數組。嘗試讀取多達 len 字節,但可能讀取較少數量。以整數形式返回實際讀取的字節數。 public synchronized void reset() 將此流重新定位到對此輸入流最后調用 mark 方法時的位置。 public synchronized long skip(long n) 跳過和放棄此輸入流中的 n 個數據字節。
源碼:
package java.io; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; //繼承FilterInputStream,FilterInputStream繼承InputStream //該類主要完成對被包裝流,加上一個緩存的功能,所謂的緩存不就是new一個byte數組 public class BufferedInputStream extends FilterInputStream { private static int defaultBufferSize = 8192; //默認緩存的大小 protected volatile byte buf[]; //內部的緩存(數組)volatile修飾,保證不同的線程總是看到某個成員變量的同一個值 protected int count; //buffer的大小,表示當前緩沖區內總共有多少有效數據 protected int pos; //buffer中cursor的位置,即byte數組的當前下標,下次讀取從該位置讀取 protected int markpos = -1; //mark的位置 protected int marklimit; //mark的范圍,最多能mark的字節長度,也就是從mark位置到當前pos的最大長度,作為參數傳入 //原子性更新。和一致性編程相關 private static final AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater = AtomicReferenceFieldUpdater.newUpdater (BufferedInputStream.class, byte[].class, "buf"); private InputStream getInIfOpen() throws IOException { //檢查輸入流是否關閉,同時返回被包裝流 InputStream input = in; if (input == null) throw new IOException("Stream closed"); return input; } private byte[] getBufIfOpen() throws IOException { //檢查buffer的狀態,同時返回緩存 byte[] buffer = buf; if (buffer == null) throw new IOException("Stream closed"); //不太可能發生的狀態 return buffer; } public BufferedInputStream(InputStream in) { //構造器 this(in, defaultBufferSize); //指定默認長度(defaultBufferSize)為buffer的長度 } public BufferedInputStream(InputStream in, int size) { //構造器 super(in); if (size <= 0) { //檢查size參數, throw new IllegalArgumentException("Buffer size <= 0"); } buf = new byte[size]; //創建指定長度的buffer,這就緩存! } //從流中讀取數據,填充如緩存中。構造函數開辟了緩存,都是空值,需要方法來填充它們。 private void fill() throws IOException { byte[] buffer = getBufIfOpen(); //得到buffer //buffer沒有被mark,也就是說沒有被調用mark方法 if (markpos < 0) pos = 0; //mark位置小於0,此時pos為0 // buffer被mark的情況 else if (pos >= buffer.length) //pos大於buffer的長度,讀到buffer的最后 if (markpos > 0) { //有mark,將mark-pos段的數組保留 int sz = pos - markpos; System.arraycopy(buffer, markpos, buffer, 0, sz); pos = sz; markpos = 0; } else if (buffer.length >= marklimit) { //buffer的長度大於marklimit時,mark失效 markpos = -1; pos = 0; //丟棄buffer中的內容 } else {//buffer的長度小於marklimit時對buffer擴容 int nsz = pos * 2; if (nsz > marklimit) nsz = marklimit;//擴容為原來的2倍,太大則為marklimit大小 byte nbuf[] = new byte[nsz]; System.arraycopy(buffer, 0, nbuf, 0, pos); //將buffer中的字節拷貝如擴容后的buf中 if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {//在buffer在被操作時,不能取代此buffer throw new IOException("Stream closed"); } buffer = nbuf; //將新buf賦值給buffer } count = pos; int n = getInIfOpen().read(buffer, pos, buffer.length - pos); if (n > 0) count = n + pos; } public synchronized int read() throws IOException { //讀取下一個字節 if (pos >= count) { //到達buffer的末端 fill();//就從流中讀取數據,填充buffer if (pos >= count) return -1; //讀過一次,沒有數據則返回-1 } return getBufIfOpen()[pos++] & 0xff; //返回buffer中下一個位置的字節 } private int read1(byte[] b, int off, int len) throws IOException { //將數據從流中讀入buffer中 int avail = count - pos; //buffer中還剩的可讀字符 if (avail <= 0) {//buffer中沒有可以讀取的數據時 if (len >= getBufIfOpen().length && markpos < 0) { return getInIfOpen().read(b, off, len); //將輸入流中的字節讀入b中 } fill();//填充 avail = count - pos; if (avail <= 0) return -1; } int cnt = (avail < len) ? avail : len; //從流中讀取后,檢查可以讀取的數目 System.arraycopy(getBufIfOpen(), pos, b, off, cnt); //將當前buffer中的字節放入b的末端 pos += cnt; return cnt; } // byte b[]調用者提供,調用者要使用的也是它。 public synchronized int read(byte b[], int off, int len)throws IOException { getBufIfOpen(); // 檢查buffer是否open if ((off | len | (off + len) | (b.length - (off + len))) < 0) {//檢查輸入參數是否正確 throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int n = 0; for (;;) { int nread = read1(b, off + n, len - n); if (nread <= 0) return (n == 0) ? nread : n; n += nread; if (n >= len) return n; // if not closed but no bytes available, return InputStream input = in; if (input != null && input.available() <= 0) return n; } } public synchronized long skip(long n) throws IOException { getBufIfOpen(); // 檢查buffer是否關閉 if (n <= 0) { return 0; } //檢查輸入參數是否正確 long avail = count - pos; //buffered中可以讀取字節的數目 if (avail <= 0) { //可以讀取的小於0,則從流中讀取 if (markpos <0) return getInIfOpen().skip(n); //mark小於0,則mark在流中 fill(); // 從流中讀取數據,填充緩沖區。 avail = count - pos; //可以讀的取字節為buffer的容量減當前位置 if (avail <= 0) return 0; } long skipped = (avail < n) ? avail : n; pos += skipped; //當前位置改變 return skipped; } public synchronized int available() throws IOException { return getInIfOpen().available() + (count - pos); } //該方法不會block!返回流中可以讀取的字節的數目。 //該方法的返回值為緩存中的可讀字節數目加流中可讀字節數目的和 public synchronized void mark(int readlimit) { //當前位置處為mark位置 marklimit = readlimit; markpos = pos; } public synchronized void reset() throws IOException { getBufIfOpen(); // 緩沖去關閉了,肯定就拋出異常!程序設計中經常的手段 if (markpos < 0) throw new IOException("Resetting to invalid mark"); pos = markpos; } public boolean markSupported() { //該流和ByteArrayInputStream一樣都支持mark return true; } //關閉當前流同時釋放相應的系統資源。 public void close() throws IOException { byte[] buffer; while ( (buffer = buf) != null) { if (bufUpdater.compareAndSet(this, buffer, null)) { InputStream input = in; in = null; if (input != null) input.close(); return; } // Else retry in case a new buf was CASed in fill() } } }
fill()---> 從字面理解就是填充的意思,實際上是從真正的輸入流中讀取一些新數據放入緩沖內存中,之后直到緩沖內存中的數據讀完前都不會再從真正的流中讀取數據。
網上對這個方法的分析:
普通的情況:
有mark的情況:
普通的練習,復制文件:
first:
private void copyFile(String fromPath, String toPath) throws IOException{ // input File fromFile = new File(fromPath); InputStream is = new FileInputStream(fromFile); BufferedInputStream bis = new BufferedInputStream(is); // output File toFile = new File(toPath); OutputStream os = new FileOutputStream(toFile); BufferedOutputStream bos = new BufferedOutputStream(os); // transfer station byte b[] = new byte[(int)fromFile.length()] ; while(bis.read(b, 0, b.length) != -1){ bos.write(b, 0, b.length); } bis.close(); bos.close(); }
second:
public static void copy() throws IOException { BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("d:/我的文檔/123.txt")); BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("d:/我的文檔/txt.txt")); int by = 0; while ((by=bufis.read()) != -1) { bufos.write(by); } bufos.close(); bufis.close(); }
總結:
理解還不是很深刻,對它的實現的理解還有很多不理解的地方,還需要繼續學習。
加油吧!
----------------------------------------------------------------------
努力不一定成功,但不努力肯定不會成功。
共勉。