日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

正確使用Java I/O輸出和讀入數(shù)據(jù)

 dddTTLee 2010-11-30
前言
Java的I/O系統(tǒng)使用“流”來處理各種類型的輸入、輸出數(shù)據(jù)的任務(wù)。
在傳輸數(shù)據(jù)的過程中,我們需要判斷流中傳輸?shù)臄?shù)據(jù)何時(shí)結(jié)束這樣的問題。這對于我們正確地發(fā)送和接收數(shù)據(jù)是非常關(guān)鍵的。
如何判斷流的末尾和批數(shù)據(jù)的末尾,是解決這個(gè)問題的關(guān)鍵。本文就是要深入地分析Java I/O輸入輸出的工作原理,保證我們能夠正確地執(zhí)行數(shù)據(jù)的發(fā)送和接收!
Java I/O任務(wù)
一個(gè)Java的I/O任務(wù),創(chuàng)建了一個(gè)連接兩個(gè)系統(tǒng)的數(shù)據(jù)傳輸管道。它分為兩個(gè)部分:輸入流和輸出流。
輸入流,指的是通過流向本系統(tǒng)的內(nèi)存?zhèn)鬏敂?shù)據(jù)的單向數(shù)據(jù)傳輸通道。
輸出流,指的是通過流向外部系統(tǒng)傳輸數(shù)據(jù)的單向數(shù)據(jù)傳輸通道。

輸入流

InputStream類是表示字節(jié)輸入流的所有類的超類。這是一個(gè)抽象類。
我們看它提供的讀入數(shù)據(jù)的方法:
read
public abstract int read()
                  throws IOException
從輸入流讀取下一個(gè)數(shù)據(jù)字節(jié)。返回 0 到 255 范圍內(nèi)的 int 字節(jié)值。如果因已到達(dá)流末尾而沒有可用的字節(jié),則返回值 -1。在輸入數(shù)據(jù)可用、檢測到流的末尾或者拋出異常前,此方法一直阻塞。
子類必須提供此方法的一個(gè)實(shí)現(xiàn)。
返回:
下一個(gè)數(shù)據(jù)字節(jié),如果到達(dá)流的末尾,則返回 -1。
拋出:
IOException - 如果發(fā)生 I/O 錯(cuò)誤。

--------------------------------------------------------------------------------

read
public int read(byte[] b)
         throws IOException
從輸入流中讀取一定數(shù)量的字節(jié)并將其存儲(chǔ)在緩沖區(qū)數(shù)組 b 中。以整數(shù)形式返回實(shí)際讀取的字節(jié)數(shù)。在輸入數(shù)據(jù)可用、檢測到文件末尾或者拋出異常前,此方法一直阻塞。
如果 b 為 null,將拋出 NullPointerException。如果 b 的長度為 0,則無字節(jié)可讀且返回 0;否則,要嘗試讀取至少一個(gè)字節(jié)。如果因?yàn)榱魑挥谖募┪捕鴽]有可用的字節(jié),則返回值 -1;否則,至少可以讀取一個(gè)字節(jié)并將其存儲(chǔ)在 b 中。
將讀取的第一個(gè)字節(jié)存儲(chǔ)在元素 b[0] 中,下一個(gè)存儲(chǔ)在 b[1] 中,依次類推。讀取的字節(jié)數(shù)最多等于 b 的長度。讓 k 為實(shí)際讀取的字節(jié)數(shù);這些字節(jié)將存儲(chǔ)在元素 b[0] 至 b[k-1] 之間,不影響元素 b[k] 至 b[b.length-1]。
如果不是因?yàn)榱魑挥谖募┪捕鵁o法讀取讀取第一個(gè)字節(jié),則拋出 IOException。特別是,如果輸入流已關(guān)閉,則拋出 IOException。
類 InputStream 的 read(b) 方法的效果等同于:
read(b, 0, b.length)
參數(shù):
b - 讀入數(shù)據(jù)的緩沖區(qū)。
返回:
讀入緩沖區(qū)的總字節(jié)數(shù),如果由于流末尾已到達(dá)而不再有數(shù)據(jù),則返回 -1。
拋出:
IOException - 如果發(fā)生 I/O 錯(cuò)誤。
NullPointerException - 如果 b 為 null。
另請參見:
read(byte[], int, int)

--------------------------------------------------------------------------------

read
public int read(byte[] b,
                int off,
                int len)
         throws IOException
將輸入流中最多 len 個(gè)數(shù)據(jù)字節(jié)讀入字節(jié)數(shù)組。嘗試讀取多達(dá) len 字節(jié),但可能讀取較少數(shù)量。以整數(shù)形式返回實(shí)際讀取的字節(jié)數(shù)。
在輸入數(shù)據(jù)可用、檢測到流的末尾或者拋出異常前,此方法一直阻塞。
如果 b 為 null,則拋出 NullPointerException。
如果 off 為負(fù),或 len 為負(fù),或 off+len 大于數(shù)組 b 的長度,則拋出 IndexOutOfBoundsException。
如果 len 為 0,則沒有字節(jié)可讀且返回 0;否則,要嘗試讀取至少一個(gè)字節(jié)。如果因?yàn)榱魑挥谖募┪捕鴽]有可用的字節(jié),則返回值 -1;否則,至少可以讀取一個(gè)字節(jié)并將其存儲(chǔ)在 b 中。
將讀取的第一個(gè)字節(jié)存儲(chǔ)在元素 b[off] 中,下一個(gè)存儲(chǔ)在 b[off+1] 中,依次類推。讀取的字節(jié)數(shù)最多等于 len。讓 k 為實(shí)際讀取的字節(jié)數(shù);這些字節(jié)將存儲(chǔ)在元素 b[off] 至 b[off+k-1] 之間,其余元素 b[off+k] 至 b[off+len-1] 不受影響。
在任何情況下,元素 b[0] 至 b[off] 和元素 b[off+len] 至 b[b.length-1] 都不會(huì)受到影響。
如果不是因?yàn)榱魑挥谖募┪捕鵁o法讀取第一個(gè)字節(jié),則拋出 IOException。特別是,如果輸入流已關(guān)閉,則拋出 IOException。
類 InputStream 的 read(b, off, len) 方法只重復(fù)調(diào)用方法 read()。如果第一個(gè)這樣的調(diào)用導(dǎo)致 IOException,則從對 read(b, off, len) 方法的調(diào)用中返回該異常。如果對 read() 的任何后續(xù)調(diào)用導(dǎo)致 IOException,則該異常會(huì)被捕獲并將發(fā)生異常時(shí)的位置視為文件的末尾;到達(dá)該點(diǎn)時(shí)讀取的字節(jié)存儲(chǔ)在 b 中并返回發(fā)生異常之前讀取的字節(jié)數(shù)。建議讓子類提供此方法的更有效的實(shí)現(xiàn)。
參數(shù):
b - 讀入數(shù)據(jù)的緩沖區(qū)。
off - 在其處寫入數(shù)據(jù)的數(shù)組 b 的初始偏移量。
len - 要讀取的最大字節(jié)數(shù)。
返回:
讀入緩沖區(qū)的總字節(jié)數(shù),如果由于已到達(dá)流末尾而不再有數(shù)據(jù),則返回 -1。
拋出:
IOException - 如果發(fā)生 I/O 錯(cuò)誤。
NullPointerException - 如果 b 為 null。
另請參見:
read()


read方法解讀
這些方法的核心。實(shí)際上就是read()方法。
這個(gè)方法的JavaDoc中指出:
“如果因已到達(dá)流末尾而沒有可用的字節(jié),則返回值 -1。如果因已到達(dá)流末尾而沒有可用的字節(jié),則返回值 -1。在輸入數(shù)據(jù)可用、檢測到流的末尾或者拋出異常前,此方法一直阻塞。”

1,read方法會(huì)在能夠返回流中的字節(jié)之前,一直阻塞線程。這就是說,read方法是一個(gè)低消耗的監(jiān)聽和讀取I/O傳輸?shù)暮梅椒ā?
這個(gè)方法的實(shí)現(xiàn),具有非常高的性能。

2,如果輸入流的I/O系統(tǒng)在執(zhí)行這個(gè)read方法時(shí)拋出異常,那么顯然這個(gè)方法會(huì)非正常結(jié)束,這個(gè)也是毫無疑問的。

3,“如果因已到達(dá)流末尾而沒有可用的字節(jié),則返回值 -1。”
這句話看似沒有問題,但實(shí)際上有非常大的歧義!什么是流的末尾?只有流的末尾才能返回-1嗎?

InputStream類和SocketInputStream類的源碼解讀
通過查看InputStream類的源碼,我發(fā)現(xiàn)實(shí)際上,流,就好比是雙向2車道的高速公路。它傳輸數(shù)據(jù)是一批一批的。我把它叫做“批數(shù)據(jù)”。
假設(shè)A=======B兩個(gè)系統(tǒng)通過一個(gè)I/O流來連接。
那么,從B端通向A端的車道,就叫作A的“輸入流”。同一條車道,在B這邊,叫作B的“輸出流”。
同理,從A端通向B端的車道,就叫作A的“輸出流”。同一條車道,在B這邊,就叫作B的“輸入流”。

數(shù)據(jù)在這條高速公路上,不是一條一條跑的,而是一批一批跑。

OutputStream類,此抽象類是表示輸出字節(jié)流的所有類的超類。輸出流接受輸出字節(jié)并將這些字節(jié)發(fā)送到某個(gè)接收器。

OutputStream類的write方法,每執(zhí)行一次,就向這條高速公路上發(fā)送了一批數(shù)據(jù)。OutputStream類的一些子類,它們并不是在每次write()方法執(zhí)行之后立刻把這批數(shù)據(jù)發(fā)送到數(shù)據(jù)高速公路上的。而是只有在執(zhí)行flush()方法之后,才把之前write的多批數(shù)據(jù)真正地發(fā)送到數(shù)據(jù)通道中。
這樣,多個(gè)write()方法發(fā)送的數(shù)據(jù)就變?yōu)榱艘慌鷶?shù)據(jù)了!

通過read()方法讀入時(shí),當(dāng)讀完該批數(shù)據(jù)之后,如果再一次執(zhí)行read()方法,就會(huì)立刻返回-1。
實(shí)際上,這是并沒有到達(dá)流的末尾!僅僅是讀完了一批發(fā)送的數(shù)據(jù)而已!

如果我們又一次執(zhí)行read()方法,那么,如果:
1,流沒有結(jié)束。也就是說,對面的發(fā)送端可能還會(huì)發(fā)送下一批數(shù)據(jù)時(shí),就會(huì)進(jìn)入阻塞狀態(tài)。當(dāng)前線程暫停,直到讀取到輸入流中下一批數(shù)據(jù)的第一個(gè)字節(jié)。
2,流結(jié)束了。也就是說,對面的發(fā)送端不再發(fā)送任何數(shù)據(jù),也即:這條數(shù)據(jù)通道已經(jīng)沒有用了,這時(shí),可以說“到達(dá)流的末尾”了!返回-1。
   
所以,InputStream及其子類的read()方法的注釋是不完整的!
Read()方法的注釋,應(yīng)該這么說:
read
public abstract int read()
                  throws IOException
從輸入流讀取下一個(gè)數(shù)據(jù)字節(jié)。返回 0 到 255 范圍內(nèi)的 int 字節(jié)值。
如果在讀完一批數(shù)據(jù)后首次調(diào)用read()方法,那么返回-1。表示這批數(shù)據(jù)已經(jīng)讀完了!
如果因已到達(dá)流末尾而沒有可用的字節(jié),則返回值 -1。在輸入數(shù)據(jù)可用、檢測到流的末尾或者拋出異常前,此方法一直阻塞。
子類必須提供此方法的一個(gè)實(shí)現(xiàn)。
返回:
下一個(gè)數(shù)據(jù)字節(jié);
如果剛讀完一批數(shù)據(jù),則返回-1;
如果到達(dá)流的末尾,則返回 -1。
拋出:
IOException - 如果發(fā)生 I/O 錯(cuò)誤。


如何正確使用Java I/O輸出和讀入數(shù)據(jù)
明白了Java的I/O流的工作機(jī)理和read方法的執(zhí)行結(jié)果,我們就能夠正確地使用Java I/O系統(tǒng)輸出和讀入數(shù)據(jù)了。

如何分批輸出數(shù)據(jù)
由于read(…)方法是分批讀取數(shù)據(jù)的,所以,我們應(yīng)該在輸出端正確地分批輸出數(shù)據(jù)。
Write(…)方法,然后執(zhí)行flush()方法能夠?qū)⒍嗯鷶?shù)據(jù)合并成一批數(shù)據(jù)輸出。
盡管OutputStream這個(gè)基類的flush()方法是無用的,但是由于我們得到的OutputStream類型的輸出對象都是這個(gè)類的子類的對象,所以,我們還是應(yīng)該盡量使用flush()方法強(qiáng)制向輸出流中物理輸出數(shù)據(jù),以避免錯(cuò)誤。

如何分批讀取數(shù)據(jù)
    我們常常使用public int read(byte[] b,
                int off,
                int len)
         throws IOException
這個(gè)方法來讀取一批數(shù)據(jù)。
查看這個(gè)方法的源代碼,我們可以發(fā)現(xiàn),它在讀取完一批數(shù)據(jù)時(shí),又執(zhí)行了一次read()方法,由于前面論述的原因,這個(gè)方法立刻返回-1,然后這個(gè)方法退出,返回這次讀取的字節(jié)數(shù)。
因此,如果我們要讀取一批數(shù)據(jù),可以采用如下幾種方法:
使用read()判斷-1來讀完一批數(shù)據(jù):
    代碼示例:
Int byte;
While((byte=inputStream.read())!=-1 ){
    Byte就是返回的字節(jié)。

}
如果讀完一批數(shù)據(jù)后再一次執(zhí)行read方法,將會(huì)立刻返回-1,表示這批數(shù)據(jù)已經(jīng)讀完。

使用read(byte[] buffer)判斷是否返回小于buffer.length或者-1來判斷是否讀完一批數(shù)據(jù)
上面那樣一個(gè)一個(gè)字節(jié)讀取數(shù)據(jù)比較麻煩,我們通常使用一個(gè)字節(jié)數(shù)組來讀取數(shù)據(jù)。
read(byte[] buffer)方法返回:
1,buffer.length,表示從輸入流中讀取到的數(shù)據(jù)塞滿了這個(gè)字節(jié)數(shù)組。此時(shí),可能已經(jīng)讀完了這批數(shù)據(jù),也可能沒有讀完這批數(shù)據(jù)。
    如果剛好讀完,那么再執(zhí)行一次read()方法,就會(huì)返回-1,表示這批數(shù)據(jù)已經(jīng)讀完,而不是表示流已經(jīng)結(jié)束。
2,小于buffer.length。表示讀完了該批數(shù)據(jù)。并且執(zhí)行了一次read()方法,返回-1,表示這批數(shù)據(jù)已經(jīng)讀完。
3,-1。這批數(shù)據(jù)已經(jīng)讀完了。 不可能是表示流已經(jīng)結(jié)束。因?yàn)橹熬蜁?huì)退出while循環(huán)。
   
代碼示例:
Byte[] buffer=new byte[1024];
int size=buffer.length;
While(size!=-1 || size>=buffer.length){
Size=inputStream.read(buffer));
將數(shù)據(jù)讀到數(shù)組buffer中。
如果讀到了-1,或者 讀出的數(shù)據(jù)少于buffer的尺寸,表示已經(jīng)讀完該批數(shù)據(jù),不再循環(huán)讀取數(shù)據(jù)了!


}


讀入一批數(shù)據(jù)的操作必須對應(yīng)輸出一批數(shù)據(jù)的操作
讀入一批數(shù)據(jù)的操作必須對應(yīng)輸出一批數(shù)據(jù)的操作。否則,讀入數(shù)據(jù)的線程會(huì)一直阻塞,等待輸出端輸出下一批數(shù)據(jù)。
如果對方也需要我們提供輸出數(shù)據(jù),那么就可能會(huì)使整個(gè)流的兩端的線程互相等待,死鎖住。并且這個(gè)I/O流也會(huì)永遠(yuǎn)不釋放,這樣就會(huì)使系統(tǒng)的資源耗盡。

后記:
在前兩天的工作中,我使用Socket在服務(wù)器和客戶端執(zhí)行操作時(shí),出現(xiàn)了死鎖的現(xiàn)象。為了找出問題的根源,我仔細(xì)查看了InputStream類和SocketInputStream類的源代碼。找出了造成輸入、輸出流誤用的原因。
希望這篇文章能夠幫助飽受I/O輸入、輸出流問題困擾的Java程序員!

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多