為什麼我覺得 Java 的 IO 很複雜?

凌碩士發表於2020-06-04

初學者覺得複雜是很正常的,歸根結底是因為沒有理解JavaIO框架的設計思想:

可以沿著這條路想一想:

1,學IO流之前,我們寫的程式,都是在記憶體裡自己跟自己玩。比如,你宣告個變數,建立個陣列,建立個集合,寫一個排序演算法,模擬一個連結串列,使用一些常用API,現在回想一下,是不是在只是自己在記憶體裡玩一玩?計算機組成包括運算器,控制器,儲存器,輸入裝置,輸出裝置。那麼你前面的工作,僅僅夠你的程式和記憶體以及CPU打打交道,如果你需要操作外部裝置呢?比如鍵盤,顯示器,再比如,最常見的外設:硬碟?甚至未來世界裡的每家每戶都有的機器人,“如何讓你的程式和機器人進行互動呢?”

2,所以程式設計語言必須要提供程式與外部裝置互動的方式,這就是IO框架的由來。我們需要和外部裝置進行資料的互動。那麼,計算機是通過什麼和外部進行互動的呢?很簡單就能想到:資料線。資料線裡傳播的是什麼呢?一個詞:位元流。位元就是bit的諧音,計算機中“位”的意思,代表0或1。1位或者1bit,就是一個0或一個1。但是,畢竟0或1不能表示什麼,所以計算機更常見的基本單位是位元組,也就是用8位0或1組成的一段資料。以上是對位元流的由來做一個簡單地解釋。(位元流一詞來自於計算機網路原理中,對物理層傳輸內容的描述:物理層(網線)中傳輸的是“位元流”,在這裡借用這個名詞代指資料的表示形式,幫助理解)上面兩段話的意思,其實是為了下文做鋪墊,幫助理解輸入輸出最重要的概念:方向性。輸入還是輸出,是相對於程式或者說相對於記憶體而言的。資料從外流到記憶體,就是輸入(讀),資料從記憶體出去,就是輸出(寫)。

3,既然計算機和外界進行資訊的輸入和輸出互動,用的是位元流,那麼很容易就能想到IO流名字的由來了。就是比喻輸入輸出的資料像流一樣。我們可以這麼認為,任何外部裝置與記憶體之間輸入輸出的操作,都是需要輸入輸出流(IO流)來完成的,這裡的IO流,指的就是位元流(或者稱位元組流)。這些外部裝置,包括,鍵盤(標準輸入裝置),顯示器(標準輸出裝置),音響,網路上另一臺主機,甚至你玩遊戲用的遊戲手柄,以及各種各樣的訊號感測器,都可以叫做外部裝置,和這些裝置之間進行資料互動,顯然不可能靠之前學習的那些陣列,集合,常用類,String等等來完成。而是要靠和外界資料交換的類來完成。靠什麼來進行資料交換,就是前面說的,位元流,或者說IO流類。

4,那麼,既然要學習IO流,就得針對某一個輸入輸出裝置來學習。哪種輸入輸出裝置最重要同時也最常見?當然是硬碟。硬碟在這裡的含義也可以理解為檔案系統。(Java程式是執行在某作業系統平臺上的應用軟體JVM上的,實際上Java程式可見的並不是硬碟,而是作業系統提供的檔案系統,因此此處可直接理解為檔案系統)。因此,我們學習IO流的時候,基本上是學習的Java如何操作檔案系統,除了檔案系統,我們還能夠了解Java操作標準輸入輸出裝置,如http://System.in和System.out。

5,知道了學習的方向,是要使用Java操作檔案系統,那麼首先要學習的就是檔案的表示,即File類。然後,我們要操作做檔案,雖然我們大部分操作都是操作檔案系統,但是要明白IO流的概念不僅僅侷限在操作檔案上,前面我已經提到了,我們的程式語言是要能操作所有的輸入輸出,因此,API提供了兩個頂層抽象類,用來表示操作所有的輸出輸出:InputStream,OutputStream。並且,這兩個類表示位元組的輸入輸出,因為輸入輸出的本質是位元組流。這裡注意體會一句話“位元組流是最最基本的流”,這句話的由來就是因為計算機底層傳遞的就是位元組。那麼,當我們要操作檔案的時候,就需要具體的對檔案系統操作的IO實現類,於是我們需要學習FileInputStream和FileOutputStream,它們是檔案輸入輸出位元組流。這裡之所以FileInputStream/OutputStream作為子類出現,按照物件導向思想理解就是,將來還有別的位元組流來操作別的裝置(比如將來需要通過操作網路裝置獲取網路資料,再比如需要操作機器人,那麼或許就會再來個RobotInputStream和RobotOutputStream,這些新的需求也就都可以繼承這個體系)(這裡順便提一句架構設計思想,其中有一種設計原則叫“開閉原則”,其核心是:一個物件對擴充套件開放,對修改關閉。就是說,一旦寫好了某個類,就不要去輕易改動他,而是要保證它一直能執行下去,而面對新的功能需求時,只要在原有程式碼上增加即可,而不是修改原有程式碼。要做到開閉原則,就需要分清需求中未來哪些部分是穩定的,哪些是很可能變化的,而往往抽象的部分是最穩定的,把穩定的內容分離出來,就能滿足開閉原則。這就是為什麼Java的類設計的如此之瑣碎,為什麼我們要從繼承關係角度去理解JavaIO流的設計)

6,學了檔案IO位元組流之後,我們會發現原始的位元組流物件用起來沒那麼高效,因為每個讀或寫請求都由底層作業系統處理,這些請求往往會觸發磁碟訪問、網路活動或其他一些相對昂貴的操作。不帶緩衝區的流物件,只能一個位元組一個位元組的讀,每次都呼叫底層的作業系統API,非常低效,而帶緩衝區的流物件,可以一次讀一個緩衝區,緩衝區空了才去呼叫一次底層API,這就能大大提高效率。所以又有了BufferedInputStream和BufferedOutputSteam,他們的用法是把位元組流物件傳入後再使用,也相當於把它倆套在了位元組流的外面,給位元組流裝了個“外掛”,讓基本位元組流如虎添翼。

7,說到操作檔案,就不得不提到檔案的分類和編碼格式。檔案分為二進位制檔案和文字檔案,二進位制檔案是用記事本開啟後看不懂的,他們的編碼格式是特殊的,比如pdf檔案,exe檔案。記事本開啟後人能看懂的只有純文字檔案,我們處理檔案(或者說處理任何的位元組流),就免不了處理一些文字檔案(或文字位元組流)。如果是英語國家的人還好說,因為他們是用的常用字元用一張ASCII碼錶就能表示得出來,用一個位元組就能表示一個字母。但是顯然,對非英語國家的人來說,一個位元組的大小無法表示他們所有的文字。因此,人們需要有能夠處理字元的類,或者說這個類提供一個功能:就是把輸入的位元組轉成字元,把要輸出的字元轉成計算機可以識別的位元組。所以,你需要兩個轉換流:InputStreamReader和OutputStreamWriter。這兩個類的作用分別是把位元組流轉成字元流,把字元流轉成位元組流。但是這兩個流需要套在現成的位元組流上才能使用,當中用到的設計模式也就是常說的裝飾模式。當位元組流被轉成字元流之後,恭喜你,你可以不必操作位元組流了,而是可以用人類的方式read和write各種“文字”。

8,(那麼,我們為什麼還要學習位元組流?因為位元組流依然有它的作用範圍。首先,所有的流都是建立在位元組流之上的,比如字元流。位元組流或許可以讀任何位元組,但是他處理不了Unicode(萬國碼),他處理不了Data流,Object流,也就是說,它做不了高階的事情,只能讀寫最原始的東西。位元組流好比動物,能看,能聽,能汪汪叫,但是他不能讀書,不能寫字,不能理解更高階的知識。其次要注意的是,字元流只能用來處理文字檔案,也就是隻能來處理字元,如果出來用來處理二進位制檔案,會帶來錯誤,所以處理二進位制檔案只能用位元組流)

,9,還是回到檔案系統,我們最常見的是和檔案系統打交道,那麼針對如此常見的用途,讀取文字檔案能不能用一種方便的方式呢?當然,大牛們替你想到並提供了。FileReader和FileWriter這兩個流物件可以直接把檔案轉成讀取、寫入流。讓你省去了建立位元組流,再套上轉換流的步驟。看看這類名起的,實際上很形象,xxxReader和xxxWriter,明擺著告訴你“閱讀和書寫”都是“人可以做的”也就是他們表示的是字元流。同理上面的InputStreamReader和OutputStreamWriter,表示的是把位元組流轉成人可讀的,把位元組流轉成人可寫的。因此他們的頂層抽象類:Reader和Writer,表示的是所有人類可讀可寫的字元流統稱。

10,同上面說的緩衝區的作用,再把Reader和Writer做成高效的,就需要BufferedReader和BufferedWriter,把它們套在Reader和Writer上,就能實現高效的字元流。

11,講到這裡,IO流的大概思想已經說的的差不多了,是不是覺得之前混亂的那些類,現在知道他們的作用和設計思想以後,稍稍清晰了許多呢?可以簡單的記,位元組流是基礎,理論上可用於所有的輸入輸出場景,內容是文字的位元組流可以通過轉換流轉成字元流,轉換流是位元組流和字元流之間相互轉換的橋樑,把位元組流轉成字元流,離不開轉換流,字元流是對於字元功能的增強可用來處理“文字”,操作檔案系統應用範圍最廣,所以JDK提供了現成的FileXXX類,用來方便程式設計使用。

另外,還有許多類是“在記憶體裡自己和自己玩的”比如ByteArrayReader/Writer,PipedWriter/Reader,它們雖然也稱為“流物件”但是他們的資料不出記憶體,所以它們的close()方法可有可無。以及其他帶有某些功能的類,比如序列化流,比如資料輸入輸出流,等等。

IO流物件的用法和作用大同小異,其使用環境和意義取決於具體需要,用到了再具體分析即可。

這裡主要介紹了JavaIO框架的設計思想,但具體底層實現細節,還需要學習JVM相關知識,以及微機原理和介面技術等等底層的課程。

 

手寫不易,覺得文章不錯可以關注公眾號「 凌晨四點的程式設計師 」一起學習


你的關注是我分享,創作的最大動力

掃碼關注送面試資料,包含技術,筆試,SQL,人事,面試指導等一大批資料,關注公眾號回覆"面試"即可領取
掃碼關注:

相關文章