Jungleford's Home BlogJava分舵

          Java技術(shù)研究,兼探討歷史話題

          BlogJava 首頁(yè) 新隨筆 聯(lián)系 聚合 管理
            24 Posts :: 0 Stories :: 53 Comments :: 0 Trackbacks
          jungleford如是說

              PushbackInputStreamPushbackReaderJava I/O系統(tǒng)里兩個(gè)比較讓人迷惑的類,我以前對(duì)它(們)就不太了解,直到某一天看了以前水母Java版的牛人zms的評(píng)論和一些資料以后才有所獲益。這是幾個(gè)月以前的事情了,這幾天寫有關(guān)序列化的總結(jié)時(shí)才想到這也不失為一個(gè)好的話題。

          一個(gè)允許你反悔的hook

              Java I/O系統(tǒng)是一個(gè)典型的Decorator模式的實(shí)現(xiàn),它以InputStream/OutputStream為基本核心,通過繼承關(guān)系,不斷為該核心添加新的功能,如文件流、緩沖、加解密等。對(duì)I/O系統(tǒng)設(shè)計(jì)模式感興趣的話,可以參考developerWorks上的一篇文章:從Java類庫(kù)看設(shè)計(jì)模式。Java I/O默認(rèn)是不緩沖流的,所謂“緩沖”就是先把從流中得到的一塊字節(jié)序列暫存在一個(gè)被稱為buffer的內(nèi)部字節(jié)數(shù)組里,然后你可以一下子取到這一整塊的字節(jié)數(shù)據(jù),沒有緩沖的流只能一個(gè)字節(jié)一個(gè)字節(jié)讀,效率孰高孰低一目了然。有兩個(gè)特殊的輸入流實(shí)現(xiàn)了緩沖功能,一個(gè)是我們常用的BufferedInputStream,像讀文件我們常用

          BufferedInputStream in = new BufferedInputStream(new FileInputStream("datafile"));
          while ((b = in.
          read()) != -1)
          {
            ...
          }
          in.
          close();
           

              這是我們幾乎不用查什么JDK文檔就能信手拈來的代碼段,寫的時(shí)候也應(yīng)該思考一下套一個(gè)BufferedInputStream的意義何在。另一個(gè)就是我們不怎么看到的PushbackInputStream(其對(duì)應(yīng)的字符流模式為PushbackReader)。
              在通常狀態(tài)下,“流”意味著“一次性”,就是說你進(jìn)行了一次操作后它的狀態(tài)就變了,譬如讀,無論是文件還是socket,你讀的過程中一個(gè)潛在的“讀指針”一樣的東東就在移動(dòng),你無法在讀以后再重新定位(當(dāng)然RandomAccessFile是另一種情況),如果你以前奇怪為什么數(shù)據(jù)庫(kù)操作中ResultSet里get某個(gè)字段以后就不能再第二次get它了,這里或許是個(gè)解釋。但好在PushbackInputStream給了我們第二次讀的機(jī)會(huì)。我們先來區(qū)別一下“監(jiān)聽”和“截獲”的概念,“監(jiān)聽”就是把得到的消息copy一份,原始消息并不作任何改變地傳遞到目的地;而“截獲”則是先把消息“扣押”下來,不讓其自動(dòng)轉(zhuǎn)給目標(biāo),而是先進(jìn)行一些處理以后在轉(zhuǎn)發(fā)給目標(biāo)(如果是網(wǎng)絡(luò)安全專業(yè)的背景知識(shí),大概知道“監(jiān)聽”是對(duì)“機(jī)密性”的攻擊,而“截獲”不僅是對(duì)“機(jī)密性”還是對(duì)“完整性”的攻擊)。有的朋友大概對(duì)hook這個(gè)名詞有些了解,它是一種Windows的一種消息處理機(jī)制,似乎就是一種消息截獲手段,但我對(duì)Windows編程幾乎一竅不通;此外,如果你熟悉Servlet的話,也能找到像Filter這樣的處理機(jī)制,在對(duì)每個(gè)HTTP請(qǐng)求/應(yīng)答進(jìn)行轉(zhuǎn)發(fā)之前,先在里頭耍一點(diǎn)花招,確定哪些予以轉(zhuǎn)發(fā),哪些屏蔽掉,這也算是“截獲”吧。通過上面的介紹,我們不妨把PushbackInputStream看成是對(duì)輸入流的一種“截獲”手段,其中最重要的方法是unread:

          public void unread(int b) throws IOException
          public void
          unread(byte[] b) throws IOException
          public void
          unread(byte[] b, int off, int len) throws IOException

              我們可以想象一下,PushbackInputStream內(nèi)置一個(gè)緩沖區(qū)(事實(shí)上,你可以從它的源代碼里找到這個(gè)protected的字節(jié)數(shù)組),當(dāng)?shù)蛯恿鬟M(jìn)來時(shí)先流進(jìn)這個(gè)buffer,在你把流“物歸原主”之前還有機(jī)會(huì)對(duì)它耍花招,然后再用unread方法“反悔”一下,把緩沖區(qū)里已經(jīng)讀過的內(nèi)容(一般是沒有被改動(dòng)的,當(dāng)然你也可以改動(dòng)它,那就失去“歸趙”的意義了,因?yàn)橐呀?jīng)不是“完璧”了)再插入到流的頭部,下次讀的時(shí)候是流剩余的部分再加上從緩沖區(qū)“歸還”的部分。上面三個(gè)unread方法分別代表從緩沖區(qū)“歸還”一個(gè)字節(jié)、一個(gè)字節(jié)數(shù)組以及一個(gè)字節(jié)數(shù)組中指定的部分。
              PushbackInputStream是對(duì)二進(jìn)制流的處理,字符流下相對(duì)應(yīng)的就是PushbackReader。

          有什么用?

              學(xué)過編譯的話就容易理解了,比如從左向右掃描字符流“for(int i=0;i<10;i++)”,掃描到“for”是不是就可以說是個(gè)關(guān)鍵字了呢?不行,說不定后面是“for1”,那就是個(gè)變量而不是關(guān)鍵字了,知道看到“(”才恍然大悟,哦,我可以安全地說“看到for關(guān)鍵字”了,但“(”還得歸還給輸入流,因?yàn)樾枰竺胬^續(xù)掃描。在上下文相關(guān)語言里,就更需要這種補(bǔ)償機(jī)制。又如,在解析HTML文檔的時(shí)候,我需要根據(jù)它的“meta”標(biāo)簽的“charset”屬性來決定使用哪種字符集進(jìn)行解析,但HTML可不是“charset”而是“<html>”開頭的哦!所以需要通過PushbackInputStream緩沖前面一段內(nèi)容,等取到字符集名稱后在把讀到的流全部歸還,再用指定的字符集進(jìn)行解析。

          參考資料

          posted on 2005-04-02 22:03 jungleford 閱讀(702) 評(píng)論(0)  編輯  收藏 所屬分類: 咖啡屋 - Java 技術(shù)研究
          主站蜘蛛池模板: 徐水县| 雅安市| 咸阳市| 罗源县| 张北县| 宝山区| 拉萨市| 禄劝| 漾濞| 陇川县| 滨州市| 枝江市| 福州市| 德惠市| 八宿县| 武陟县| 鄂托克旗| 北安市| 江华| 新郑市| 静海县| 武鸣县| 林口县| 隆子县| 婺源县| 南川市| 安远县| 武鸣县| 盐池县| 资溪县| 菏泽市| 莱阳市| 万源市| 板桥市| 铜陵市| 璧山县| 泗水县| 莱阳市| 南和县| 阿克陶县| 纳雍县|