? 一直以來對IO這塊的東西認識都不是很清楚。每次涉及這塊的東西,一般都找點現成的代碼復制粘貼一下。這可不好,于是靜下心來好好弄一下。
?
?
什么是IO?
??? 一提起IO給人的感覺就比較復雜(不過確實也挺復雜的),可能是學C的時候嚇怕了,呵呵。不過,理一下思路還是比較清晰的。在Java里,IO說白了就是對讀、寫的一種抽象。沒有什么其他的復雜的東西。至于讀什么、寫什么,那就看你想讀什么、想寫什么,由自己控制了。
?
?
讀什么?寫什么?
看看下面這個圖會清楚很多(只用看InputStream就行,OutputStream一樣):
?
?

?
???? 大家可以看一下直接繼承InputStream的幾個類ByteArrayInputStream、FileInputStream、StringBufferInputStream(PipedInputSteram和SequenceInputStream一般不用)。呵呵,看看這幾個類的構造函數就明了了。第一個是把byte數字作為IO源讀取byte數組里的東西,第二個是把文件作為IO源讀取文件里的東西,第三個是把String作為IO源讀取String里面的東西。這么看這幾個IO實在簡單。說白了就是從byte數組、文件、String按字節讀取罷了.....
??? 一句簡單的話說:IO體系第一級繼承的幾個類解決了從哪兒讀或者寫到哪兒的問題。
?
?
怎么讀?怎么寫?
??? 試想一下,現在要讀一個幾百兆的文件,怎么讀呢?是一下子把整個文件都讀到內存慢慢分析呢,還是每次讀只讀一個字節逐個分析呢?顯然兩個都不好,于是我們想到了緩沖。所以IO體系上有了BufferedInputStream。再想一下,現在你想寫一個“3.1415926”到文件里怎么寫?總不能自己把這個數的二進制形式寫出來,轉成byte數組寫到文件里吧。即便寫進去了讀的時候怎么讀啊?誰知道你寫進去的是數字而不是其他字符呢?
??? 于是針對怎么讀Java IO體系上出現了一個分支FilerInoutStream(FilterOutputStream)。繼承了這個類的幾個類各自對怎么讀、寫提供了不同的支持。Buffered提供了緩沖,LineNumber提供了對不同類型數據的讀寫。
??? 還是一句話:IO體系的第二級繼承解決了怎么讀、怎么寫。
?
?
以不變應萬變
??? 說IO不能不說他的Decorator(裝飾)模式。此模式在這里的應用確實很經典!從上面兩段的分析看來Java的IO就是分成兩部分從哪兒讀(寫)和怎么讀(寫)。想想看,一個數據源可能需要不同的讀寫方式、而不同的數據源都可能需要同一直讀寫方式。于是這里的組合方式是一種乘積。n個數據源×n中處理方式×(n-1)處理方式(有時候可能涉及多種處理方式的疊加)=n^3。想想看,要是一個一個寫還不寫死......
??? 從IO體系的根看,InputStream(OutputStream)提供了最根本的read(write)方法。各子類提供各自實現。而得益于Decorator,各子類還可以把自己的處理方式加到read(write)的前后,形成一種處理方式的疊加。
??? JDK這部分的代碼比較簡單。看一下就大概知道Decorator怎么實現的了。
?
?
追根溯源
???? 我們見到的IO最大的應用一個應該是數據的持久化,FileinputStream(FileOutputStream)已經實現了。而另一個則是網絡傳輸。之前我就很想知道IO是怎么在網絡上實現傳輸的?還有上面說的幾個IO都不存在“阻塞”問題,那么IO的阻塞實在哪兒產生的呢?
???? 找了半天對于找到了下面一句:
???? class SocketInputStream extends FileInputStream
???? 再追下去就找到了一些native方法,這個就超出我能力之外了。不過可以猜想,底層的東西應該就是C的一些文件讀寫了。這個就不是Java的問題:)目前暫時這么認為,以后或許能發現一些新的東西。
?
?
根深蒂固的痛——國際化
???? 編碼問題不僅是Java頭痛的問題,也是每一個用Java的人都頭痛的問題。IO也不例外。InputStream(OutputStream)體系都是針對字節(byte)進行讀寫的。這樣的問題是字節可能是沒有意義的,只有幾個字節聯合轉換才能得到有意義的東西。所以如果是字節還要再次進行編解碼才能得到我們想要的東西。
??? 為了避免這個麻煩,字符體系(Read、Write)產生了。他們可以對流進行編解碼,這樣可以直接得到用戶想要的東西。兩個體系大大體上都是對應的。再多的東西我也沒有深入了解了:)
??? 還有一個要提的是,什么時候用字符、什么時候用字節呢?TIJ里有交代:大部分時候用字符,字符不行的時候再考慮字節。呵呵,挺簡練的。
?
?
日新月異
??? nio應該是老掉牙的東西了。不過對于我這個初始IO的人來說還有待進一步了解。目前知道兩點:上面的IO體系的底層都已經用nio重寫了,所以速度上應該差不多。這樣看來,一般情況下用上面的IO是沒問題的。再一個是,nio最大的優勢在于他的“非阻塞IO”,不過這個東東是用在網絡編程的。一般也用不上。
??? 再新的東西就是前兩天看到的一個blog,關于aio(異步IO)的大家有興趣可以自己看看。
這是我以前的Blog,現在也還在用。因為技術方面的東西沒人關注,呵呵,所以還是把技術的東西放到技術的地方,或許更能找到共同的語音:)順便貼一篇,歡迎討論~
重拾聚集(Collection)
大家先來看段代碼吧:
List list = new ArrayList();
list.add("exception");
for (Iterator iter = list.iterator(); iter.hasNext();) {
???? String element = (String) iter.next();
???? list.remove(element);
}
這段代碼運行時會拋出異常,呵呵,不知道大家發現問題沒?
?
這段代碼會拋出:java.util.ConcurrentModificationException。也就是所謂的“Fail Fast”。用了很久Java了,才發現這么一個異常,著實郁悶了一把......啥都不說了,拿起書來,重新來過。
?
為什么要Iterator?
下面是一個Collection的總體圖:
?
?
??? 上面代碼涉及了一個經典的涉及模式-Iterator模式。既然涉及了這個模式,就要問問為什么這里要用這個模式。查了一下書,有這么幾句描述Iterator的:迭代邏輯沒有改變,但是需要將一種聚集變換成另一種聚集。因為不同的聚集遍歷接口不同,所以需要修改客戶端代碼。這幾句話沒錯,而且我們用Iterator都有如文章開頭示例的標準方式,然而仔細想來卻還有點問題。
??? 假設Iterator是為了實現遍歷方便。可是,我們幾個常用的聚集(如:ArrayList,LinkedList,Set等)都已經間接實現了Collection接口。而大家應該注意到Collection接口中有個size()方法。這樣的結果是,對所有實現了Collection的聚集而言,我們都可以通過for (int i = 0; i < list.size(); i++)這樣的形式實現遍歷。而且,JDK1.5還可以通過foreach的形式大大簡化代碼量。所以,這樣想來Iterator為了實現遍歷的方便而引入,顯然不成立。
??? 接著找找。我們可以注意到,Set和List兩個大類中一個很大的區別是:List提供get方法,而Set沒有提供get方法。這樣的結果是Set類只能往集合中放,卻不能從集合中取(從一般的常識來說,這樣做還是合理的)。呵呵,所以這里我只能猜想Iterator是不是是為了訪問Set中元素而設計的了。
??? 僅僅猜想,不知道大家有沒有什么更好的想法?
?
討論:
???? 跟朋友討論了一下,for(int i = 0; i < list.size(); i++)對應遍歷LinkedList來說是致命的。對ArrayList是O(n),而LinkedList則是O(n^2),這樣的效率是不能讓人接受的。
????如上所說,那么foreach的引入應該是在編譯層次對兩種不同數據結構進行了不同處理。而且成為了Iterator的替代。
??? 不過這里又引出了另一個問題——
???????????Iterator模式如何用在其他地方?作為一種設計模式Iterator應該有更廣的使用價值吧~
?
?
Iterator的缺點
- ??? 聚集中的元素有些是有順序的,而有些是沒有順序可言的。使用過度Iterator會產生一種元素順序的誤解。
- ??? Iterator給出的元素沒有類型。這點很致命啊。而且也是JDK1.5一大優勢。
- ??? Iterator使得某些同步的類在使用了Iterator后,成為不同步(不能說不同步,只是Iterator無法并發)。如Vector。
??? 這里沒有貶低Iterator的意思,只是實在看不出他有啥好處......
?
集合
能力有限,感覺了解還是太少,也沒太多好說的,只是有些區別應該注意一下。
- 繼承List的幾個類中,只有Vector是同步的。當然也包括Vector的子類Stack。
- 如果需要對不同步的List,Set等可以考慮使用Collections類中的方法。