Java中的stream流的概念解析及實(shí)際運(yùn)用總結(jié)
流是字節(jié)序列的抽象概念。
文件是數(shù)據(jù)的靜態(tài)存儲(chǔ)形式,而流是指數(shù)據(jù)傳輸時(shí)的形態(tài)。
流類(lèi)分為兩個(gè)大類(lèi):節(jié)點(diǎn)流類(lèi)和過(guò)濾流類(lèi)(也叫處理流類(lèi))。
程序用于直接操作目標(biāo)設(shè)備所對(duì)應(yīng)的類(lèi)叫節(jié)點(diǎn)流類(lèi),程序也可以通過(guò)一個(gè)間接流類(lèi)去調(diào)用節(jié)點(diǎn)流類(lèi),以達(dá)到更加靈活方便地讀取各種類(lèi)型的數(shù)據(jù),這個(gè)間接流類(lèi)就是過(guò)濾流類(lèi)(也叫處理流類(lèi)),或者稱為包裝類(lèi)。
包裝類(lèi)的調(diào)用過(guò)程如下圖:

流分類(lèi)的關(guān)系
不管流的分類(lèi)是多么的豐富和復(fù)雜,其根源來(lái)自于四個(gè)基本的類(lèi)。這個(gè)四個(gè)類(lèi)的關(guān)系如下:
| 字節(jié)流 | 字符流 | |
| 輸入流 | InputStream | Reader |
| 輸出流 | OutputStream | Writer |
Java內(nèi)用 Unicode 編碼存儲(chǔ)字符,字符流處理類(lèi)負(fù)責(zé)將外部的其他編碼的字符流和java內(nèi) Unicode 字符流之間的轉(zhuǎn)換。而類(lèi)InputStreamReader 和OutputStreamWriter處理字符流和字節(jié)流的轉(zhuǎn)換。字符流(一次可以處理一個(gè)緩沖區(qū))一次操作比字節(jié)流(一次一個(gè)字節(jié))效率高。
InputStream

由于InputStream和OutputStream是abstact類(lèi),所以它們還不能表明具體對(duì)應(yīng)哪種IO設(shè)備。它們下面有許多子類(lèi),包括網(wǎng)絡(luò)、管道、內(nèi)存、文件等具體的IO設(shè)備,實(shí)際程序中使用的它們的各種子類(lèi)對(duì)象。
注:我們將節(jié)點(diǎn)流類(lèi)所對(duì)應(yīng)的IO源和目標(biāo)稱為流節(jié)點(diǎn)(Node)。
注意:將A文件的內(nèi)容寫(xiě)入B文件,程序?qū)文件的操作所用的是輸出類(lèi)還是輸入類(lèi)這個(gè)問(wèn)題。輸入輸出類(lèi)是相對(duì)程序而言的,而不是代表文件的,所以我們應(yīng)該創(chuàng)建一個(gè)輸入類(lèi)來(lái)完成對(duì)A文件的操作,創(chuàng)建一個(gè)輸出類(lèi)來(lái)完成對(duì)B文件的操作。
OutputStream

以字符為導(dǎo)向的 stream Reader/Writer
以 Unicode 字符為導(dǎo)向的 stream ,表示以 Unicode 字符為單位從 stream 中讀取或往 stream 中寫(xiě)入信息。同樣,Reader/Writer也為abstact類(lèi)。
Reader

Writer

IO程序代碼的復(fù)用:
平時(shí)寫(xiě)代碼用-1來(lái)作為鍵盤(pán)輸入的結(jié)束,在寫(xiě)的函數(shù)中不直接使用System.in,只是在調(diào)用該函數(shù)時(shí),將System.in作為參數(shù)傳遞進(jìn)去,這樣,我們以后要從某個(gè)文件中讀取數(shù)據(jù),來(lái)代替手工鍵盤(pán)輸入時(shí),我們可以直接使用這個(gè)函數(shù),程序就不用做太多的修改了,達(dá)到以不變應(yīng)萬(wàn)變的效果。
字節(jié)流和字符流的相互轉(zhuǎn)換
InputStreamReader和OutputStreamReader:把一個(gè)以字節(jié)為導(dǎo)向的stream轉(zhuǎn)換成一個(gè)以字符為導(dǎo)向的stream。
InputStreamReader類(lèi)是從字節(jié)流到字符流的橋梁:它讀入字節(jié),并根據(jù)指定的編碼方式,將之轉(zhuǎn)換為字符流。
使用的編碼方式可能由名稱指定,或平臺(tái)可接受的缺省編碼方式。
InputStreamReader的read()方法之一的每次調(diào)用,可能促使從基本字節(jié)輸入流中讀取一個(gè)或多個(gè)字節(jié)。
為了達(dá)到更高效率,考慮用BufferedReader封裝InputStreamReader,
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
Java流使用的一點(diǎn)總結(jié)
最經(jīng)工作中碰到不少Java流的使用,總結(jié)如下:
1. 生成Zip格式,遇到的是要在一個(gè)Servlet中生成Zip文件,輸出到web 客戶端,直接下載。
response.setContentType("application/zip");
response.addHeader("Content-Disposition", "attachment;filename=/"xxx.zip/"");
ZipOutputStream out = new ZipOutputStream(response.getOutputStream())
for()
{
ZipEntry entry = new ZipEntry("aa" + i ".dat");
out.putNewEntry(entry);
bytes[] bt = s.getBytes();
out.writeBytes(bt, 0, bt.length());
out.closeEntry();
}
out.flush();
out.close();
ZipOutputStream 繼承自 java.io.FilterOutputStream. 因此真正的寫(xiě)操作是通過(guò)參數(shù)OutputStream out去寫(xiě)的。
其 void write(byte[] b, int off, int len) 最終調(diào)用了 out.write(b, off, len);
如果要生成一個(gè)zip文件,構(gòu)造時(shí)就這樣寫(xiě) new ZipOutputStream(new FileOutputStream(path));
2. 類(lèi)似的寫(xiě)XML.
XMLWriter writer = new XMLWriter(new FileOutputStream(path), formater)
writer.write(doc).道理和上面類(lèi)似
3. 寫(xiě)文本文件,追加。
PrintStream ps = new PrintStream(new FileOutputStream(path, true), "utf-8") ps.println(s); // 能寫(xiě)boolean、int等各種類(lèi)型。
其內(nèi)部使用一個(gè)OutputStreamWriter的對(duì)象textOut來(lái)寫(xiě)。例如write(String s)最終調(diào)用到textOut.write(s);
這里涉及到編碼的問(wèn)題。其參數(shù)里的"utf-8"最終傳遞到OutputStream。
OutputStream是一個(gè)字符流和字節(jié)流之間的橋梁。
因此其提供了write(char[] cbuf, int off, int len) 和 write(String str, int off, int len) 用來(lái)寫(xiě)字符和字符串。
OutputStream內(nèi)部又通過(guò)一個(gè)StreamEncoder對(duì)象來(lái)序列化字符和字符串。
4. 寫(xiě)出到socket。
DataOutputStream out = new DataOutputStream(socket.getOutputStream()); out.writeBytes(bt); out.writeBoolean(boolean v) ;
DataOutputStream同樣是一個(gè)自FilterOutputStream.
5. 從文本中讀取
BufferedReader reader = new BufferedReader(new FileReader(path)); reader.readLine();
BufferedReader的模式和上面的Filter模式一樣,其內(nèi)部存儲(chǔ)一個(gè)Reader對(duì)象為參數(shù)傳進(jìn)來(lái)并用來(lái)實(shí)際讀取的對(duì)象。
BufferedReader對(duì)應(yīng)java 1.0的類(lèi)就是BufferedInputStream,是一個(gè)FilterInputStream。
6. 從Socket中讀取
BufferedInputStream is = new BufferedInputStream(socket.getInputStream()); is.read(bt, 0, bt.length());
總結(jié):
基類(lèi)Stream系列是InputStream和OutputStream,他們是抽象類(lèi),要求的方法只有(以O(shè)utput為例)
void write(int b) throws IOException; void write(byte b[]) throws IOException void write(byte b[], int off, int len)
其最基本的只是字節(jié)操作。第1個(gè)方法看似寫(xiě)一個(gè)整數(shù),其實(shí)只寫(xiě)一個(gè)字節(jié)(最低八個(gè)bit)。其子類(lèi)分兩個(gè)系列,一個(gè)是直接操作輸出設(shè)備的,我們上面碰到的有文件(FileOutputStream)和Servlet輸出(ServletOutputStream)。其他常用的還有一個(gè) ByteArrayOutputStream,是直接在內(nèi)存里操作的。
再就是FilterOutputStream系列了,都是接收一個(gè)OutputStream對(duì)象座位參數(shù),真正的寫(xiě)操作通過(guò)該對(duì)象去完成。例如ZipOutputStream,其本身只負(fù)責(zé)生成壓縮格式的數(shù)據(jù),至于這些數(shù)據(jù)是寫(xiě)到文件、內(nèi)存、還是servletResponse,由輸入的參數(shù)確定。這就是裝飾器模式。
Filter系列常用的有PrintStream(提供了print,println,write(boolean[int, char, string])各種操作,最終利用out.write方法以寫(xiě)字節(jié)的方式寫(xiě)進(jìn)去。
還有DataOutputStream,其提供了writeByte/writeBoolean/writeDouble/writeLong/wiretUTF等方法。
還有就是socket/zip等不常用的。
Java的流很方便也很復(fù)雜。復(fù)雜就復(fù)雜在實(shí)現(xiàn)一個(gè)功能往往需要多個(gè)類(lèi),而且有多種組合的辦法。尚需繼續(xù)在實(shí)踐中總結(jié)。
相關(guān)文章
RabbitMQ中的Connection和Channel信道詳解
這篇文章主要介紹了RabbitMQ中的Connection和Channel信道詳解,信道是建立在 Connection 之上的虛擬連接,RabbitMQ 處理的每條 AMQP 指令都是通過(guò)信道完成的,需要的朋友可以參考下2023-08-08
SpringBoot數(shù)據(jù)層測(cè)試事務(wù)回滾的實(shí)現(xiàn)流程
這篇文章主要介紹了SpringBoot數(shù)據(jù)層測(cè)試事務(wù)回滾的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2022-10-10
使用Java第三方實(shí)現(xiàn)發(fā)送短信功能
這篇文章主要介紹了使用Java第三方實(shí)現(xiàn)發(fā)送短信功能,在一些開(kāi)發(fā)中,經(jīng)常需要有給用戶發(fā)送短信接收驗(yàn)證碼的功能,那么在Java中該如何實(shí)現(xiàn)呢,今天我們就一起來(lái)看一看2023-03-03
在?Spring?Boot?中使用?Quartz?調(diào)度作業(yè)的示例詳解
這篇文章主要介紹了在?Spring?Boot?中使用?Quartz?調(diào)度作業(yè)的示例詳解,在本文中,我們將看看如何使用Quartz框架來(lái)調(diào)度任務(wù),Quartz支持在特定時(shí)間運(yùn)行作業(yè)、重復(fù)作業(yè)執(zhí)行、將作業(yè)存儲(chǔ)在數(shù)據(jù)庫(kù)中以及Spring集成,需要的朋友可以參考下2022-07-07
Java利用數(shù)組隨機(jī)抽取幸運(yùn)觀眾如何實(shí)現(xiàn)
這篇文章主要介紹了Java利用數(shù)組隨機(jī)抽取幸運(yùn)觀眾如何實(shí)現(xiàn),需要的朋友可以參考下2014-02-02
idea左側(cè)的commit框設(shè)置顯示出來(lái)方式
在IDEA中顯示左側(cè)的commit框,首先通過(guò)File-Settings-Version Control-Commit進(jìn)行設(shè)置,然后勾選Use non-modal commit interface完成2025-01-01
springboot整合redis過(guò)期key監(jiān)聽(tīng)實(shí)現(xiàn)訂單過(guò)期的項(xiàng)目實(shí)踐
現(xiàn)在各種電商平臺(tái)都有自己的訂單過(guò)期時(shí)間設(shè)置,那么如何設(shè)置訂單時(shí)間過(guò)期呢,本文主要介紹了springboot整合redis過(guò)期key監(jiān)聽(tīng)實(shí)現(xiàn)訂單過(guò)期的項(xiàng)目實(shí)踐,感興趣的可以了解一下2023-12-12
在Spring?Boot使用Undertow服務(wù)的方法
Undertow是RedHAT紅帽公司開(kāi)源的產(chǎn)品,采用JAVA開(kāi)發(fā),是一款靈活,高性能的web服務(wù)器,提供了NIO的阻塞/非阻塞API,也是Wildfly的默認(rèn)Web容器,這篇文章給大家介紹了在Spring?Boot使用Undertow服務(wù)的方法,感興趣的朋友跟隨小編一起看看吧2023-05-05
Spring?Security權(quán)限管理實(shí)現(xiàn)接口動(dòng)態(tài)權(quán)限控制
這篇文章主要為大家介紹了Spring?Security權(quán)限管理實(shí)現(xiàn)接口動(dòng)態(tài)權(quán)限控制,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
hibernate-validator如何使用校驗(yàn)框架
高效、合理的使用hibernate-validator校驗(yàn)框架可以提高程序的可讀性,以及減少不必要的代碼邏輯,本文主要介紹了hibernate-validator如何使用校驗(yàn)框架,感興趣的可以了解一下2022-04-04

