Android開發中你所須要掌握的Java IO框架,看這一篇就夠了

做者:享學Alvin老師

1、IO框架

Java IO 的學習是一件很是艱鉅的任務。java

它的挑戰是來自於要覆蓋全部的可能性。不只存在各類I/O源端還有想要和他通訊的接收端(文件/控制檯/網絡連接),並且還須要以不一樣的方式與他們進行通訊(順序/隨機存取/緩衝/二進制/字符/行/字 等等)這些狀況綜合起來就給咱們帶來了大量的學習任務,大量的類須要學習。json

咱們要學會全部的這些java 的IO是很難的,由於咱們沒有構建一個關於IO的體系,要構建這個體系又須要深刻理解IO庫的演進過程,因此,咱們若是缺少歷史的眼光,很快咱們會對何時應該使用IO中的哪些類,以及何時不應使用它們而困惑。設計模式

因此,在開發者的眼中,IO很亂,不少類,不少方法,很迷茫。數組

2、IO簡介

數據流是一組有序,有起點和終點的字節的數據序列。包括輸入流和輸出流。緩存

流序列中的數據既能夠是未經加工的原始二進制數據,也能夠是經必定編碼處理後符合某種格式規定的特定數據。所以Java中的流分爲兩種:安全

  1. 字節流:數據流中最小的數據單元是字節bash

  2. 字符流:數據流中最小的數據單元是字符, Java中的字符是Unicode編碼,一個字符佔用兩個字節。網絡

Java.io包中最重要的就是5個類和一個接口。5個類指的是File、OutputStream、InputStream、Writer、Reader;一個接口指的是Serializable。掌握了這些就掌握了Java I/O的精髓了。框架

Java I/O主要包括以下3層次:dom

  1. 流式部分——最主要的部分。如:OutputStream、InputStream、Writer、Reader等

  2. 非流式部分——如:File類、RandomAccessFile類和FileDescriptor等類

  3. 其餘——文件讀取部分的與安全相關的類,如:SerializablePermission類,以及與本地操做系統相關的文件系統的類,如:FileSystem類和Win32FileSystem類和WinNTFileSystem類。


在Android 平臺,從應用的角度出發,咱們最須要關注和研究的就是 字節流(Stream)字符流(Reader/Writer)和 File/ RandomAccessFile。當咱們須要的時候再深刻研究也何嘗不是一件好事。關於字符和字節,例如文本文件,XML這些都是用字符流來讀取和寫入。而如RAR,EXE文件,圖片等非文本,則用字節流來讀取和寫入。面對如此複雜的類關係,有一個點是咱們必需要首先掌握的,那就是設計模式中的修飾模式,學會並理解修飾模式是搞懂流必備的前提條件哦。

字節流的學習

在具體的學習流以前,咱們必需要學的一個設計模式是裝飾模式。由於從流的整個發展歷史,出現的各類類之間的關係看,都是沿用了修飾模式,都是一個類的功能能夠用來修飾其餘類,而後組合成爲一個比較複雜的流。好比說:

DataOutputStream out = new DataOutputStream(
                    new BufferedOutputStream(
                    new FileOutputStream(file)))
複製代碼

從上面的代碼塊中你們不難看出這些類的關係:爲了向文件中寫入數據,首先須要建立一個FileOutputStream,而後爲了提高訪問的效率,因此將它發送給具有緩存功能的BufferedOutput-Stream,而爲了實現與機器類型無關的java基本類型數據的輸出,因此,咱們將緩存的流傳遞給了DataOutputStream。

從上面的關係,咱們能夠看到,其根本目的都是爲outputSteam添加額外的功能。而這種額外功能的添加就是採用了裝飾模式來構建的代碼。所以,學習流,必需要學好裝飾模式。

下面的圖是一個關於字節流的圖譜,這張圖譜比較全面的概況了咱們字節流中間的各個類以及他們之間的關係。


字節流的學習過程:

爲何要按照一個學習路線來呢?緣由是他們的功能決定的。

OutputStream>FileOutputStream/FilterOutputStream>DataOutputStream->bufferedOutputStream

相應的學習InputStream方法就行了。

從學習的角度來,咱們應該先掌握FilterOutputStream, 以及FileOutputStream,這兩個類是基本的類,從繼承關係能夠不難發現他們都是對 abstract 類 OutputStream的拓展,是它的子類。然而,伴隨着 對 Stream流的功能的拓展,因此就出現了 DataOutputStream,(將java中的基礎數據類型寫入數據字節輸出流中、保存在存儲介質中、而後能夠用DataOutputStream從存儲介質中讀取到程序中還原成java基礎類型)。

這裏多提一句FileOutputStream、DataOutputStream、FilterOutputStream三個類的關係的這種設計既使用了裝飾器模式 避免了類的爆炸式增加。

爲了提高Stream的執行效率,因此出現了bufferedOutputStream。bufferedOutputStream就是將本地添加了一個緩存的數組。在使用bufferedOutputStream以前每次從磁盤讀入數據的時候都是須要訪問多少byte數據就向磁盤中讀多少個byte的數據,而出現bufferedOutputSteam以後,策略就改了,會先讀取整個緩存空間相應大小的數據,這樣就是從磁盤讀取了一塊比較大的數據,而後緩存起來,從而減小了對磁盤的訪問的次數以達到提高性能的目的。

另一方面,咱們知道了outputStream(輸出流)的發展歷史後,咱們即可以知道如何使用outpuSteam了,一樣的方法,咱們能夠運用到inputStream中來,這樣對稱的解釋就出現到了inputStream相關的中來了,因而,咱們對整個字節流就有了全方位的理解,因此這樣子咱們就不會感受到流的複雜了。這個時候對於其餘的一些字節流的使用(byteArrayOutputStream/PipeOutputStream/ObjectOutputStream)的學習就自須要在使用的時候看看API便可。

字符流的學習

下圖則是一個關於字符流的圖譜,這張圖譜比較全面的概況了咱們字符流中間的各個類以及他們之間的關係。


字符流的學習和字節流的學習是同樣的,它和字節流有着一樣的發展過程,只是,字節流面向的是咱們未知或者即便知道了他們的編碼格式也意義不大的文件(png,exe, zip)的時候是採用字節,而面對一些咱們知道文件構造咱們就可以搞懂它的意義的文件(json,xml)等文件的時候咱們仍是須要以字符的形式來讀取,因此就出現了字符流。reader 和 Stream最大的區別我認爲是它包含了一個readline()接口,這個接口標明瞭,一行數據的意義,這也是能夠理解的,由於自有字符才具有行的概念,相反字節流中的行也就是一個字節符號。

字符流的學習歷程:

Writer>FilterWriter>BufferedWriter>OutputStreamWriter>FileWriter>其餘

同時類比着學習Reader相關的類。

FilterWriter/FilterReader

字符過濾輸出流、與FilterOutputStream功能同樣、只是簡單重寫了父類的方法、目的是爲全部裝飾類提供標準和基本的方法、要求子類必須實現核心方法、和擁有本身的特點。這裏FilterWriter沒有子類、可能其意義只是提供一個接口、留着之後的擴展。。。自己是一個抽象類。

BufferedWriter/BufferedReader

BufferedWriter是 Writer類的一個子類。他的功能是爲傳入的底層字符輸出流提供緩存功能、一樣當使用底層字符輸出流向目的地中寫入字符或者字符數組時、每寫入一次就要打開一次到目的地的鏈接、這樣頻繁的訪問不斷效率底下、也有可能會對存儲介質形成必定的破壞、好比當咱們向磁盤中不斷的寫入字節時、誇張一點、將一個很是大單位是G的字節數據寫入到磁盤的指定文件中的、沒寫入一個字節就要打開一次到這個磁盤的通道、這個結果無疑是恐怖的、而當咱們使用BufferedWriter將底層字符輸出流、好比FileReader包裝一下以後、咱們能夠在程序中先將要寫入到文件中的字符寫入到BufferedWriter的內置緩存空間中、而後當達到必定數量時、一次性寫入FileReader流中、此時、FileReader就能夠打開一次通道、將這個數據塊寫入到文件中、這樣作雖然不可能達到一次訪問就將全部數據寫入磁盤中的效果、但也大大提升了效率和減小了磁盤的訪問量!

OutputStreamWriter/InputStreamReader

輸入字符轉換流、是輸入字節流轉向輸入字符流的橋樑、用於將輸入字節流轉換成輸入字符流、經過指定的或者默認的編碼將從底層讀取的字節轉換成字符返回到程序中、與OutputStreamWriter同樣、本質也是使用其內部的一個類來完成全部工做:StreamDecoder、使用默認或者指定的編碼將字節轉換成字符;OutputStreamWriter/ InputStreamReader只是對StreamDecoder進行了封裝、isr內部全部方法核心都是調用StreamDecoder來完成的、InputStreamReader只是對StreamDecoder進行了封裝、使得咱們能夠直接使用讀取方法、而不用關心內部實現。

OutputStreamWriter、InputStreamReader分別爲InputStream、OutputStream的低級輸入輸出流提供將字節轉換成字符的橋樑、他們只是外邊的一個門面、真正的核心:

OutputStreamWriter中的StreamEncoder:

一、使用指定的或者默認的編碼集將字符轉碼爲字節
二、調用StreamEncoder自身實現的寫入方法將轉碼後的字節寫入到底層字節輸出流中。

InputStreamReader中的StreamDecoder:

一、使用指定的或者默認的編碼集將字節解碼爲字符
二、調用StreamDecoder自身實現的讀取方法將解碼後的字符讀取到程序中。

InputStreamReader中的StreamDecoder:

一、使用指定的或者默認的編碼集將字節解碼爲字符
二、調用StreamDecoder自身實現的讀取方法將解碼後的字符讀取到程序中。

在理解這兩個流的時候要注意:java——io中只有將字節轉換成字符的類、沒有將字符轉換成字節的類、緣由很簡單——字符流的存在原本就像對字節流進行了裝飾、加工處理以便更方便的去使用、在使用這兩個流的時候要注意:因爲這兩個流要頻繁的對讀取或者寫入的字節或者字符進行轉碼、解碼和與底層流的源和目的地進行交互、因此使用的時候要使用BufferedWriter、BufferedReader進行包裝、以達到最高效率、和保護存儲介質。

FileReader/FileWriter

FileReader和FileWriter繼承於InputStreamReader/OutputStreamWriter。

從源碼能夠發現FileWriter 文件字符輸出流、主要用於將字符寫入到指定的打開的文件中、其本質是經過傳入的文件名、文件、或者文件描述符來建立FileOutputStream、而後使用OutputStreamWriter使用默認編碼將FileOutputStream轉換成Writer(這個Writer就是FileWriter)。若是使用這個類的話、最好使用BufferedWriter包裝一下、高端大氣上檔次、低調奢華有內涵!

FileReader 文件字符輸入流、用於將文件內容以字符形式讀取出來、通常用於讀取字符形式的文件內容、也能夠讀取字節形式、可是由於FileReader內部也是經過傳入的參數構造InputStreamReader、而且只能使用默認編碼、因此咱們沒法控制編碼問題、這樣的話就很容易形成亂碼。因此讀取字節形式的文件仍是使用字節流來操做的好、一樣在使用此流的時候用BufferedReader包裝一下、就算衝着BufferedReader的readLine()方法去的也要使用這個包裝類、不說他還能提升效率、保護存儲介質。

字節流與字符流的關係

那麼字節輸入流和字符輸入流之間的關係是怎樣的呢?請看下圖


一樣的字節與字符輸出流字節的關係也以下圖所示


字節流與字符流的區別

字節流和字符流使用是很是類似的,那麼除了操做代碼的不一樣以外,還有哪些不一樣呢?

字節流在操做的時候自己是不會用到緩衝區(內存)的,是與文件自己直接操做的,而字符流在操做的時候是使用到緩衝區的字節流在操做文件時,即便不關閉資源(close方法),文件也能輸出,可是若是字符流不使用close方法的話,則不會輸出任何內容,說明字符流用的是緩衝區,而且可使用flush方法強制進行刷新緩衝區,這時才能在不close的狀況下輸出內容

那開發中究竟用字節流好仍是用字符流好呢?

在全部的硬盤上保存文件或進行傳輸的時候都是以字節的方法進行的,包括圖片也是按字節完成,而字符是隻有在內存中才會造成的,因此使用字節的操做是最多的。

若是要java程序實現一個拷貝功能,應該選用字節流進行操做(可能拷貝的是圖片),而且採用邊讀邊寫的方式(節省內存)。

字節流與字符流的轉換

雖然Java支持字節流和字符流,但有時須要在字節流和字符流二者之間轉換。InputStreamReader和OutputStreamWriter,這兩個爲類是字節流和字符流之間相互轉換的類。

InputSreamReader用於將一個字節流中的字節解碼成字符:

有兩個構造方法:

InputStreamReader(InputStream in);
複製代碼
  • 功能:用默認字符集建立一個InputStreamReader對象
InputStreamReader(InputStream in,String CharsetName);
複製代碼
  • 功能:接收已指定字符集名的字符串,並用該字符建立對象
OutputStream用於將寫入的字符編碼成字節後寫入一個字節流。

一樣有兩個構造方法:

OutputStreamWriter(OutputStream out);
複製代碼
  • 功能:用默認字符集建立一個OutputStreamWriter對象;
OutputStreamWriter(OutputStream out,String  CharSetName);
複製代碼
  • 功能:接收已指定字符集名的字符串,並用該字符集建立OutputStreamWrite對象,爲了不頻繁的轉換字節流和字符流,對以上兩個類進行了封裝。

BufferedWriter類封裝了OutputStreamWriter類;
BufferedReader類封裝了InputStreamReader類;

封裝格式:

BufferedWriter out=new BufferedWriter(new OutputStreamWriter(System.out));
  BufferedReader in= new BufferedReader(new InputStreamReader(System.in);
複製代碼

利用下面的語句,能夠從控制檯讀取一行字符串:

BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
  String line=in.readLine();複製代碼

最後

後續還會更新更多精選文章,能夠點贊關注下,歡迎你們在下面留言!

相關文章
相關標籤/搜索