摘要:通過多個裝飾類實現(xiàn)責(zé)任鏈模式,它將對一個輸入流的不同處理分散到不同的中去。
1、基本概念 1.1、InputStream
最基本的字節(jié)輸入流,抽象類,定義了讀取原始字節(jié)的所有基本方法
1.1.1、public abstract int read() throws IOException 讀取一個字節(jié)的方法,最基礎(chǔ)的方法
1.1.2、public int read(byte b[], int off, int len) 讀取指定長度的字節(jié)到字節(jié)數(shù)組,基于方法1.1.1
1.1.3、public int read(byte b[]) throws IOException 讀取一個數(shù)組那么多的字節(jié),基于 方法1.1.2
1.1.4、public long skip(long n) throws IOException 跳過一定字節(jié)數(shù),用到的比較少
1.1.5、public int available() throws IOException 返回可以讀取的最少字節(jié)數(shù),用到的比較少
1.1.6、mark(int readlimit)、void reset()和markSupported()這三個方法,并不是每個子類都支持,這里設(shè)計得不合理,完全可以把這三個方法遷移到一個新的接口中去。
1.1.7、public void close() throws IOException 關(guān)閉輸入流
最基本的字節(jié)輸出流,抽象類,定義了寫入原始字節(jié)的所有基本方法
1.2.1、public abstract void write(int b) throws IOException 寫入一個字節(jié),最基礎(chǔ)的方法
1.2.2、public void write(byte b[], int off, int len) throws IOException 將一個字節(jié)數(shù)組中的部分字節(jié)寫入,基于方法1.2.1
1.2.3、public void write(byte b[]) throws IOException 將一個字節(jié)數(shù)組寫入,基于方法1.2.2
1.2.4、public void close() throws IOException 關(guān)閉輸出流
1.2.5、public void flush() throws IOException 刷新輸出流
點評:InputStream和OutputStream定義了I/O領(lǐng)域最基礎(chǔ)的行為,也就是讀取和寫入一個字節(jié),同時使用了模板方法將讀取和寫入的行為進行了適當(dāng)擴展。
2、擴展點一:對I/O流的繼承有了抽象類,就一定會有子類。針對不同的數(shù)據(jù)來源,InputStream和OutputStream存在三種子類:一種是基于內(nèi)存的ByteArrayInputStream/ByteArrayOutputStream,一種是基于磁盤文件的FileInputStream/FileOutputStream,還有一種是基于網(wǎng)絡(luò)的SocketInputStream/SocketOutputStream。
讀取寫入的源是操作系統(tǒng)的文件
FileInputStream使用native方法進行底層文件的讀取private native int read0() throws IOException;所有其他的read方法最終都是基于這個本地方法實現(xiàn)。
FileOutputStream使用native方法進行底層文件的寫入private native void writeBytes(byte b[], int off, int len, boolean append)所有其他的write方法都是基于這個本地方法實現(xiàn)。
讀取寫入的源是內(nèi)存的一個數(shù)組,用的比較少。
2.3、SocketInputStream/SocketOutputStreamSocketInputStream使用 private native int socketRead0(FileDescriptor fd,byte b[], int off, int len,int timeout)這個native方法讀取遠程服務(wù)器的數(shù)據(jù)流。所有read方法都是基于這個本地方法實現(xiàn)的。
SocketOutputStream 使用private native void socketWrite0(FileDescriptor fd, byte[] b, int off,int len)這個native方法來進行遠程數(shù)據(jù)流的寫入,所有的write方法都是基于這個方法實現(xiàn)的。
點評:InputStream和OutputStream是對流的抽象,不同的具體流通過繼承去實現(xiàn),對于Java本地平臺,最基本的就是基于文件系統(tǒng)的流,當(dāng)涉及到遠程系統(tǒng),就會出現(xiàn)網(wǎng)絡(luò)流,基于內(nèi)存的流一般不會用到。
3、擴展點二:對IO流行為的擴展裝飾模式可以對一個類的行為進行擴展,并且不改變它的接口,Java通過FilterInputStream/FilterOutputStream實現(xiàn)了裝飾模式。
責(zé)任鏈模式則是定義統(tǒng)一的接口,然后通過多個實現(xiàn)該接口的子類串行協(xié)作完成一項復(fù)雜的功能。Java通過將多個FilterInputStream/FilterOutputStream的子類串聯(lián)起來實現(xiàn)了責(zé)任鏈模式。
3.1、FilterInputStream/FilterOutputStreamFilterInputStream本身不實現(xiàn)輸入流的功能,而是通過構(gòu)造函數(shù)傳入另一個InputStream的子類,把輸入流的功能交給它做。通過繼承FilterInputStream可以對輸入輸出流的行為進行擴展,這是裝飾模式的典型用法。通過多個裝飾類實現(xiàn)責(zé)任鏈模式,它將對一個輸入流的不同處理分散到不同的FilterInputStream中去。FilterOutputStream和FilterInputStream的原理一樣。
3.2、BufferedInputStream/BufferedOutputStream繼承了FilterInputStream,實現(xiàn)了輸入流處理中的緩沖的功能。底層的流會先被讀取到一個字節(jié)數(shù)組中,用戶使用BufferedInputStream讀取數(shù)據(jù)的時候,會先讀取字節(jié)數(shù)組中的數(shù)據(jù),讀完了才會調(diào)用底層的流進行進一步的讀取。這種方法可以提升讀取的性能。
繼承了FilterOutputStream,實現(xiàn)了輸出流處理中的緩沖功能。當(dāng)用戶寫入數(shù)據(jù)的時候,其實是先寫入到BufferedOutputStream的一個字節(jié)數(shù)組中,當(dāng)這個字節(jié)數(shù)組滿了,才會真正調(diào)用底層的輸出流執(zhí)行輸出動作。這種方法可以提升寫入的性能。在使用BufferedOutputStream的寫入功能時,一定要使用flush,因為緩沖數(shù)組不滿的時候是不會寫入底層流的,在寫入最后一點數(shù)據(jù)的時候,緩沖數(shù)據(jù)不一定被填滿,這時候就需要調(diào)用flush進行強制刷新。
繼承FilterOutputStream,這個類的print和println方法可以把java的一些基本類型數(shù)據(jù)轉(zhuǎn)換成字節(jié)寫入到底層輸出流,但是PrintStream對String的轉(zhuǎn)換是平臺相關(guān)的,不同的平臺會有不同的編碼,所以寫入到底層的字節(jié)也不同,因此PrintStream只適合于測試輸出,不適合于一般的I/O操作,特別是網(wǎng)絡(luò)流。
3.4、DataInputStream/DataOutputStream這兩個類繼承了FilterInputStream/FilterOutputStream,用來實現(xiàn)將java基本類型轉(zhuǎn)換成二進制來進行讀寫操作,這兩個類的readUTF和writeUTF方法使用了一種特殊的UTF編解碼方式,只能用于java程序,因此不建議在網(wǎng)絡(luò)流或者跨平臺的應(yīng)用中使用者兩個類
3.5、PushbackInputStream繼承了FilterInputStream,提供了一種回退的機制,可以實現(xiàn)unread,本質(zhì)是使用緩沖數(shù)組實現(xiàn)了,也就是說,回退的范圍是有限的。
4、Reader/Writer出現(xiàn)的原因InputStream和OutputStream是面向字節(jié)的,而人類的習(xí)慣是面向字符,因此InputStream和OutputStream對于程序猿的用戶體驗不是太好,于是就需要提供一些面向字符的流。由于DataInputStream/DataOutputStream在跨平臺的情況下存在問題,因此,java設(shè)計者干脆仿照InputStream和OutputStream重新設(shè)計了一套面向字符的I/O,也就是Reader/Writer
4.1、Reader基本的字符輸入流,是個抽象類
4.1.1、public abstract int read() throws IOException 讀取一個字符的方法,最基礎(chǔ)的方法
4.1.2、public int read(char b[], int off, int len) 讀取指定長度的字符到字節(jié)數(shù)組,基于方法4.1.1
4.1.3、public int read(char b[]) throws IOException 讀取一個數(shù)組那么多的字符,基于 方法4.1.2
4.1.4、public long skip(long n) throws IOException 跳過一定字符,用到的比較少
4.1.5、public int available() throws IOException 返回可以讀取的最少字符,用到的比較少
mark(int readlimit)、void reset()和markSupported()這三個方法,并不是每個子類都支持,這里設(shè)計得不合理,完全可以把這三個方法遷移到一個新的接口中去。
4.1.6、public void close() throws IOException 關(guān)閉輸入流
4.1.7、public boolean ready() throws IOException 是否已經(jīng)準(zhǔn)備好
基本的字符輸出流,是個抽象類
4.2.1、abstract public void write(char cbuf[], int off, int len) 抽象方法,用于寫入一個字符數(shù)組的一部分,需要子類實現(xiàn)
4.2.2、public void write(char cbuf[]) throws IOException 基于4.2.1、,寫入一個字符數(shù)據(jù)
4.2.3、public void write(int c) throws IOException 將一個int類型的堤16位作為一個字符寫入,基于4.2.1
4.2.4、public void write(String str) throws IOException 寫入一個字符串,基于4.2.1
4.2.5、public void write(String str, int off, int len) throws IOException 寫入一個字符串的一部分,基于4.2.1
由于計算機只識別字節(jié),所以Reader/Writer的數(shù)據(jù)來源最終還是字節(jié),而他們無法直接和字節(jié)打交道,這時候就需要一個中介者將Reader/Writer和InputStream和OutputStream進行打通,于是就有了InputStreamReader和OutputStreamWriter
不同源的Reader/Writer,他們都繼承InputStreamReader/OutputStreamWriter
6.1、FileReader/FileWriter繼承了InputStreamReader/OutputStreamWriter,傳入FileInputStream/FileOutputStream作為底層的字節(jié)I/O
6.2、CharArrayReader/CharArrayWriter繼承了InputStreamReader/OutputStreamWriter,使用char數(shù)組作為數(shù)據(jù)源,用的比較少
7、對Reader/Writer行為的擴展類似于字節(jié)流,也使用了裝飾模式和責(zé)任鏈模式
7.1、FilterReader/FilterWriter對Reader/Writer的代理,底層使用其他Reader/Writer作為真正的操作。
7.2、BufferedReader/BufferedWriter繼承了FilterReader/FilterWriter,BufferedReader使用char數(shù)組作為數(shù)據(jù)的緩沖區(qū),讀取數(shù)據(jù)先從緩存區(qū)讀,讀不到在從底層的Reader讀,Reader其實用到是更底層的InputStream,盡量不要用BufferedInputStream作為底層InputStream,兩層緩沖區(qū)沒有必要。BufferedWriter先寫入緩沖區(qū),待緩沖區(qū)寫滿了再使用底層真正的Writer寫,Writer其實用的是更底層的OutputStream。盡量不要用BufferedOutputStream作為底層OutputStream,兩層緩沖區(qū)沒必要。
7.3、PushbackReader繼承了FilterReader,實現(xiàn)了可退回的寫,本質(zhì)是使用了一個char數(shù)組,所以可退回是有界限。
7.4、PrintWriter用于取代PrintStream,它可以java基本類型轉(zhuǎn)換成字節(jié)輸出,而且可以正確處理不同字符集的國際化問題。
至此,我們對java.io包下的相關(guān)類都做了詳細的解讀,接下來,讓我們看看第三方開源框架都對java IO進行了哪些擴展。
8、開源庫對Java IO的擴展通過上面的解讀我們知道,java IO本身的擴展點有兩個,一個是通過繼承對數(shù)據(jù)來源的擴展,第二個是通過裝飾模式對行為進行擴展。下面介紹的commons-io選擇了對行為進行擴展,并提供一些IO操作的工具方法,簡化IO操作,而okio則不走尋常路,廢棄了java IO的體系,設(shè)計出了source/sink接口體系。
8.1、commons-io8.1.1、擴展行為
最新的commons-io 2.5提供了對input和output的各種擴展,通過繼承FilterInputStream/FilterOutputStream實現(xiàn)
input:
AutoCloseInputStream:當(dāng)IO流讀到EOF時,會進行自動關(guān)閉
BOMInputStream:用于處理含有BOM的輸入流,比如Windows下用記事本保存的文件
BoundedInputStream:含有讀取界限的輸入流,超過這個界限讀取將會停止
CountingInputStream:含有統(tǒng)計功能的輸入流
DemuxInputStream:這個輸入流會將真正的流保存在ThreadLocal中
ProxyInputStream:一個抽象類,提供了讀取一個字節(jié)之前后之后的回調(diào)方法
TaggedInputStream:這個類在拋異常的時候會給異常設(shè)置標(biāo)記,從而用于跟蹤異常
TeeInputStream:從一個源讀取數(shù)據(jù),同時會保存到一個指定的源,類似于unix的tee命令
UnixLineEndingInputStream:這個流在讀取換行符的時候會按照unix格式讀取
WindowsLineEndingInputStream:這個流在讀取換行符的時候會按照Windows格式讀取
output
ChunkedOutputStream:寫入流的時候按照chunk分批寫入
CountingOutputStream:具有統(tǒng)計功能的輸出流
DemuxOutputStream:這個輸出流會將真正的流保存在ThreadLocal中
ProxyOutputStream:一個抽象類,提供了寫入一個字節(jié)之前后之后的回調(diào)方法
TaggedOutputStream:這個類在拋異常的時候會給異常設(shè)置標(biāo)記,從而用于跟蹤異常
TeeOutputStream:寫數(shù)據(jù)到一個源,同時會保存到一個指定的源,類似于unix的tee命令
8.1.2、工具方法
IOUtils工具類,主要提供以下工具方法:
closeQuietly - 關(guān)閉一個流,忽略異常
toXxx/read - 從某個流讀取數(shù)據(jù)
write - 向某個流寫入數(shù)據(jù)
copy -從一個流復(fù)制到另一個流
contentEquals - 比較兩個流中的內(nèi)容
8.2、okio如果使用原生的Java IO進行基本類型的讀寫,我們需要使用DataInputStream/DataOutputStream以及BufferedReader/BufferedWriter這四個類,除此之外,我們還需要了解InputStreamReader/OutputStreamWriter以及Java IO之間的責(zé)任鏈,對于一般的Java開發(fā)者來說,這顯然太復(fù)雜了。于是okio重新設(shè)計了接口Source/Sink,提供了訪問基本類型的接口和緩沖功能,同時屏蔽了底層復(fù)雜的IO體系,開發(fā)者只要傳入InputStream和OutputStream就可以了。
具體的類關(guān)系如下:
使用Okio的Java代碼如下:
try {
BufferedSource bufferedSource = Okio.buffer(Okio.source(new FileInputStream("1.txt"))); int i = bufferedSource.readInt(); long l = bufferedSource.readLong(); String s = bufferedSource.readString(Charset.forName("UTF-8")); BufferedSink bufferedSink = Okio.buffer(Okio.sink(new FileOutputStream("2.txt"))); bufferedSink.writeInt(1); bufferedSink.writeLong(2L); bufferedSink.writeString("123", Charset.forName("UTF-8")); } catch (Exception e) { // process exception }
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/66511.html
摘要:在包下主要包括輸入輸出兩種流,每種輸入輸出流又可分為字節(jié)流和字符流兩大類。輸入輸出是從程序運行所在的內(nèi)存的角度而言的。的輸入流主要由和作為基類,而輸出流主要由和作為基類。 本章主要參考和摘自瘋狂java講義上面的(java編程思想的后面看過后有新的內(nèi)容再補充進去吧)。 輸入輸出是所有程序都必需的部分————使用輸入機制允許程序讀取外部數(shù)據(jù)(包括磁盤、光盤等存儲設(shè)備上的數(shù)據(jù)和用戶輸入的...
摘要:的是實現(xiàn)輸入輸出的基礎(chǔ)中把不同的輸入輸出源鍵盤文件網(wǎng)絡(luò)連接抽象的表述為流流的分類輸入流和輸出流按照流的流向來分輸入流只能從中讀數(shù)據(jù)而不能向其中寫數(shù)據(jù)輸出流只能向其中寫出數(shù)據(jù)而不能從中讀取數(shù)據(jù)此處的輸入輸出涉及到一個方向問題數(shù)據(jù)從內(nèi)存到硬盤被 Java的IO是實現(xiàn)輸入輸出的基礎(chǔ),Java中把不同的輸入/輸出源(鍵盤,文件,網(wǎng)絡(luò)連接)抽象的表述為流,stream. 流的分類 輸入流和輸...
摘要:我的是忙碌的一年,從年初備戰(zhàn)實習(xí)春招,年三十都在死磕源碼,三月份經(jīng)歷了阿里五次面試,四月順利收到實習(xí)。因為我心理很清楚,我的目標(biāo)是阿里。所以在收到阿里之后的那晚,我重新規(guī)劃了接下來的學(xué)習(xí)計劃,將我的短期目標(biāo)更新成拿下阿里轉(zhuǎn)正。 我的2017是忙碌的一年,從年初備戰(zhàn)實習(xí)春招,年三十都在死磕JDK源碼,三月份經(jīng)歷了阿里五次面試,四月順利收到實習(xí)offer。然后五月懷著忐忑的心情開始了螞蟻金...
摘要:流分類由此可見,流很龐大從不同角度進行分類數(shù)據(jù)分類按處理數(shù)據(jù)單位分為字節(jié)流和字符流。處理數(shù)據(jù)是音頻視頻文本等一切為字節(jié)流,僅能處理文本的為字符流。功能分類節(jié)點流和處理流。從向一個特定的設(shè)備磁盤網(wǎng)絡(luò)等讀寫數(shù)據(jù)的流稱為節(jié)點流,也常被稱為低級流。 嗨嘍,小樂又來了,今天要給大家送上的技術(shù)文章是Java重點知識-IO流。 先來看看IO流的思維導(dǎo)圖吧。showImg(https://segmen...
摘要:目錄介紹問題匯總具體問題好消息博客筆記大匯總年月到至今,包括基礎(chǔ)及深入知識點,技術(shù)博客,學(xué)習(xí)筆記等等,還包括平時開發(fā)中遇到的匯總,當(dāng)然也在工作之余收集了大量的面試題,長期更新維護并且修正,持續(xù)完善開源的文件是格式的同時也開源了生活博客,從年 目錄介紹 00.Java問題匯總 01.具體問題 好消息 博客筆記大匯總【16年3月到至今】,包括Java基礎(chǔ)及深入知識點,Android技...
閱讀 3304·2021-11-18 10:02
閱讀 2751·2019-08-30 13:56
閱讀 407·2019-08-29 12:36
閱讀 521·2019-08-28 18:07
閱讀 712·2019-08-27 10:51
閱讀 3449·2019-08-26 12:13
閱讀 3286·2019-08-26 11:46
閱讀 3315·2019-08-23 12:00