一個屌絲程式猿的人生(37)

左瀟龍發表於2016-09-06

本系列:第 1 篇 、第 2 篇第 3 篇(4)(5)(6)(7)(8)(9)10)(11)(12)(13)(14)(15)(16)(17)(18)(19)(20)(21)(22)(23)(24)(25)(26)(27)(28)(29)(30)(31)(32)(33)(34)(35)(36)


對於林蕭來說,學習Java基礎的過程,比想象當中的要順利許多。但儘管如此,在這個過程當中,還是偶爾會碰到比較難搞的奇葩知識點。

這不,林蕭看了沒多久視訊,就碰到了一個把他搞得暈頭轉向的傢伙——檔案IO。

說到這個檔案IO,第一個讓林蕭覺得彆扭的地方,就是這個輸入輸出流的叫法。

明明是讀取一個檔案,卻叫做檔案輸入流,也就是FileInputStream。而明明是寫入一個檔案,卻叫做檔案輸出流,即FileOutputStream。

好在林蕭一直都知道類比的學習方式,拿標準的輸入輸出流,和檔案的輸入輸出流一對比,就基本上能轉過來彎兒了。

標準的輸入流,是為了接受控制檯標準輸入流輸入的字元,因此便稱作輸入流。那麼檔案這裡也是類似,只不過,與標準輸入流不同的是,這裡的輸入是一個檔案。

同樣的,輸出流也是一樣,標準輸出流是將字元,輸出到控制檯的標準輸出流上面去,而檔案的輸出流,則是將字元輸出到檔案中去。

而一開始林蕭之所以覺得彆扭,其實是斷詞斷錯了地方的緣故。

就拿檔案輸入流FileInputStream來說吧,林蕭在一開始,很自然的便將這個類,按照中文的翻譯進行了斷詞,分成了“檔案輸入”和“流”兩個單詞。這麼分下來,檔案輸入流傳遞給林蕭的字面意思,就變成了用於給檔案輸入內容的流,也就是寫檔案的意思,與檔案輸入流的實際作用恰恰相反。

但其實人家正確的斷詞方式,是應該分為“檔案”和“輸入流”兩個詞。這樣一分下來,檔案輸入流的字面意思,就變成了一個檔案作為輸入的流,這就和檔案輸入流的作用一致了。

實際上,這裡如果按照類名的英文去看的話,則更容易理解。也就是說,FileInputStream應該看作是File和InputStream的結合,而不是FileInput和Stream的結合。

不過叫法這個事,也只是讓林蕭一開始覺得彆扭而已,真正讓人抓狂的,是檔案讀取的那段程式碼。

逐行讀取一個檔案的內容,最終都要用到一個叫做BufferedReader的類。而得到這個類的例項有兩種不同的方式,一種是先new一個FileInputStream,然後再new一個InputStreamReader,最後再new一個BufferedReader。還有一種方式是,先new一個FileReader,然後再new一個BufferedReader。

這麼一大堆完全不知道幹啥的類突然出現,林蕭直接就被整懵逼了。

FileInputStream、FileReader、InputStreamReader和BufferedReader到底有啥區別?

為毛每次都要new出來好幾個物件才能讀取?而且這些物件還特麼一個套一個的,搞基呢?

諸如此類,各種得不到答案的疑問,開始在林蕭腦海中盤旋,那種感覺,別提多痛苦了,就好像腦袋要爆炸了一般。

雖然關於這些疑問,在度娘上也可以搜到一些解釋,然而那並沒有什麼卵用。

當初的林蕭,水平差到以為只有得到了BufferedReader,才能逐行進行檔案內容的讀取,就這種初級的水準,怎麼可能消化的了度娘上的那些解釋?

要知道,哪怕使用最原始的FileInputStream,也是完全可以逐行讀取出檔案內容的,只不過比較麻煩而已。不僅需要自己去對位元組進行轉碼,而且要自己去解析轉碼後的內容,才能分析出每行的內容。

但就是這麼基礎的用法,林蕭都還絲毫不知,就更別提搞清楚FileInputStream、FileReader、InputStreamReader和BufferedReader這四個類的關係了。

實際上,這四個類的關係,要解釋起來倒算不上多麼複雜,但卻需要一定的基礎,才能真正的看懂這四個類到底什麼關係。

首先,在解釋這四個類的關係之前,要先解釋一下InputStream和Reader這兩個類,它們都是Java整個IO類庫中,最頂級的抽象類。

什麼叫最頂級?

這裡的最頂級並不是指得傳統意義上的,高階大氣上檔次的意思,而是指它們都處於繼承鏈的最上層。

而這兩個最頂級的抽象類,則分別代表了兩種流的讀取方式,InputStream代表的是位元組流的讀取,而Reader代表的,則是字元流的讀取。

通俗的解釋,就是InputStream讀出來的都是位元組,而Reader讀出來的都是字元。至於FileInputStream、FileReader、InputStreamReader和BufferedReader這四個類,其實都是這兩個類的子類而已。

其中FileInputStream是InputStream的子類,而其餘三個,則都是Reader的子類。但是,在Reader的這三個子類中,有一個子類是非常特殊的一個,那就是InputStreamReader。

這個類為什麼會這麼特殊呢?它有什麼作用呢?

其實它的作用很簡單,InputStreamReader這個類,就是一個連線InputStream和Reader的橋樑,它可以將一個InputStream,轉化為Reader型別。

那為什麼需要這種轉化呢?難道不可以直接new一個Reader去使用嗎?

答案當然是肯定的,在有些時候,你完全可以直接new一個Reader直接去使用,根本不需要先new一個InputStream,然後再使用InputStreamReader去做轉化。

但是,這種直接new一個Reader去使用的方式,並不是萬能的,在一些特定的情況下,這種方式是不適用的。而很不巧,檔案的讀取和寫入操作,就恰巧屬於這種特定的情況。

這其實是因為,對於檔案IO的底層操作,只能通過native方法來進行,而這些方法,則都是封裝在了FileInputStream和FileOutputStream這兩個位元組流的類當中。

因此,在讀取一個檔案的時候,只能先new一個FileInputStream,然後才能通過InputStreamReader,來產生一個Reader。

雖然FileReader這個類,看似是可以直接通過檔案或者檔案路徑,就new出來了一個Reader,但它本質上,依然是通過InputStreamReader對FileInputStream進行了一次轉換,才產生了一個Reader。

因為逐行讀取檔案內容這個需求,最終希望得到的是字元而不是位元組,因此在得到Reader物件以後,整個事情就好辦多了,只需要用Reader物件再new出來一個BufferedReader,就可以非常方便的逐行讀取檔案內容了。

至於為什麼有了Reader物件以後,還要再new出來一個BufferedReader,這就是設計模式思想的體現了。而在這裡所體現的設計模式,則是被稱為裝飾器模式。

簡單的說,裝飾器模式就是為了可以方便的,對某些類進行裝飾,從而增強它的功能。而在逐行讀取檔案內容這個具體的例子當中,就使用到了BufferedReader,來對Reader的功能進行增強。

這些增強的功能,主要就包括快取的功能,以及一些更加方便讀取的方法。就比如逐行讀取時所用到的readLine方法,這個方法在Reader物件當中就是沒有的,需要對Reader增強以後,獲取到BufferedReader物件才有。

其實分析到這裡,上面四個類的關係,可以說已經基本搞清楚了。但從這個分析的過程也可以看出來,如果你對Java的IO包沒有一個全貌的瞭解,或者對設計模式沒有做到深入理解的話,是不可能徹底搞明白這些關係的。

由此也可以看出,學習必須是遞進式的,如果你在底子還不夠的時候,就打算一口吃個胖子的話,是很容易碰一鼻子灰的。

好在林蕭一直對於自己學習的食量,把握的都還比較到位。該搞明白的,哪怕花再多時間,林蕭也會弄清楚,但是不該搞明白的,或者說當前無法搞明白的,林蕭也會很識趣的放棄。

舉一個不太恰當的例子,學習這件事,有時候特別像在皇上身邊兒當太監,有些事你應該知道的,你一定要不擇手段的探聽到,但是你不該知道的,那就千萬不要碰那條線,否則就很可能會掉腦袋。

上位者身邊的這條線,通常會被人們簡稱為高壓線,但是大部分人都忽略了,其實學習也是有高壓線的。

只是這條線到底在哪,就需要自己去衡量和把握了。

……

對於林蕭來說,Java基礎的學習,雖然也給他造成了一定的困擾,但總的來說,還是非常順利的。

檔案IO的難關過去以後,林蕭看了下接下來要學習的目錄,終於忍不住長舒一口氣,聲音中帶著一絲興奮的自言自語道:“終於要學完Java基礎了……”

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

任選一種支付方式

一個屌絲程式猿的人生(37) 一個屌絲程式猿的人生(37)

相關文章