“給你第二次機會”——小議PushbackInputStream

來源:互聯網
上載者:User
stream jungleford如是說
    PushbackInputStream和PushbackReader是Java I/O系統裡兩個比較讓人迷惑的類,我以前對它(們)就不太瞭解,直到某一天看了以前水母Java版的牛人zms的評論和一些資料以後才有所獲益。這是幾個月以前的事情了,這幾天寫有關序列化的總結時才想到這也不失為一個好的話題。

一個允許你反悔的hook

    Java I/O系統是一個典型的Decorator模式的實現,它以InputStream/OutputStream為基本核心,通過繼承關係,不斷為該核心添加新的功能,如檔案流、緩衝、加解密等。對I/O系統設計模式感興趣的話,可以參考developerWorks上的一篇文章:從Java類庫看設計模式。Java I/O預設是不緩衝流的,所謂“緩衝”就是先把從流中得到的一塊位元組序列暫存在一個被稱為buffer的內部位元組數組裡,然後你可以一下子取到這一整塊的位元組資料,沒有緩衝的流只能一個位元組一個位元組讀,效率孰高孰低一目瞭然。有兩個特殊的輸入資料流實現了緩衝功能,一個是我們常用的BufferedInputStream,像讀檔案我們常用
BufferedInputStream in = new BufferedInputStream(new FileInputStream("datafile"));while ((b = in.read()) != -1){  ...}in.close(); 
    這是我們幾乎不用查什麼JDK文檔就能信手拈來的程式碼片段,寫的時候也應該思考一下套一個BufferedInputStream的意義何在。另一個就是我們不怎麼看到的PushbackInputStream(其對應的字元流模式為PushbackReader)。    在通常狀態下,“流”意味著“一次性”,就是說你進行了一次操作後它的狀態就變了,譬如讀,無論是檔案還是socket,你讀的過程中一個潛在的“讀指標”一樣的東東就在移動,你無法在讀以後再重新置放(當然RandomAccessFile是另一種情況),如果你以前奇怪為什麼資料庫操作中ResultSet裡get某個欄位以後就不能再第二次get它了,這裡或許是個解釋。但好在PushbackInputStream給了我們第二次讀的機會。我們先來區別一下“監聽”和“截獲”的概念,“監聽”就是把得到的訊息copy一份,原始訊息並不作任何改變地傳遞到目的地;而“截獲”則是先把訊息“扣押”下來,不讓其自動轉給目標,而是先進行一些處理以後在轉寄給目標(如果是網路安全專業的背景知識,大概知道“監聽”是對“機密性”的攻擊,而“截獲”不僅是對“機密性”還是對“完整性”的攻擊)。有的朋友大概對hook這個名詞有些瞭解,它是一種Windows的一種訊息處理機制,似乎就是一種訊息截獲手段,但我對Windows編程一竅不通//shy;此外,如果你熟悉Servlet的話,也能找到像Filter這樣的處理機制,在對每個HTTP請求/應答進行轉寄之前,先在裡頭耍一點花招,確定哪些予以轉寄,哪些屏蔽掉,這也算是“截獲”吧。通過上面的介紹,我們不妨把PushbackInputStream看成是對輸入資料流的一種“截獲”手段,其中最重要的方法是unread:
public void unread(int b) throws IOExceptionpublic void unread(byte[] b) throws IOExceptionpublic void unread(byte[] b, int off, int len) throws IOException
    我們可以想象一下,PushbackInputStream內建一個緩衝區(事實上,你可以從它的原始碼裡找到這個protected的位元組數組),當低層流進來時先流進這個buffer,在你把流“物歸原主”之前還有機會對它耍花招,然後再用unread方法“反悔”一下,把緩衝區裡已經讀過的內容(一般是沒有被改動的,當然你也可以改動它,那就失去“歸趙”的意義了,因為已經不是“完璧”了)再插入到流的頭部,下次讀的時候是流剩餘的部分再加上從緩衝區“歸還”的部分。上面三個unread方法分別代表從緩衝區“歸還”一個位元組、一個位元組數組以及一個位元組數組中指定的部分。    PushbackInputStream是對二進位流的處理,字元流下相對應的就是PushbackReader。

有什麼用?

    學過編譯的話就容易理解了,比如從左向右掃描字元流“for(int i=0;i<10;i++)”,掃描到“for”是不是就可以說是個關鍵字了呢?不行,說不定後面是“for1”,那就是個變數而不是關鍵字了,知道看到“(”才恍然大悟,哦,我可以安全地說“看到for關鍵字”了,但“(”還得歸還給輸入資料流,因為需要後面繼續掃描。在上下文相關語言裡,就更需要這種補償機制。又如,在解析HTML文檔的時候,我需要根據它的“meta”標籤的“charset”屬性來決定使用哪種字元集進行解析,但HTML可不是“charset”而是“<html>”開頭的哦!所以需要通過PushbackInputStream緩衝前面一段內容,等取到字元集名稱後在把讀到的流全部歸還,再用指定的字元集進行解析。

參考資料
Java Network Programming. by Elliotte R. Harold zms兄在水母的文章. by zms(無奈的是,水木清華已經不能對校外開放了) JDK 1.4.2 Documentation. by java.sun.com

相關文章

Beyond APAC's No.1 Cloud

19.6% IaaS Market Share in Asia Pacific - Gartner IT Service report, 2018

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。