引言
使用java io 包時,InputStream 類中的有好幾個read()方法,并且返回值也都是int類型,這樣就使得初學者很容易搞混,其實雖然返回值都是int類型,但所表示的意義確是不一樣的
有參 read(byte b[], int off, int len)
先看有參的read方法。
有參的read比較好理解,將讀取到的數(shù)據(jù)寫入字節(jié)數(shù)組(從字節(jié)數(shù)組的指定位置開始寫入)
- 三個入?yún)?
-
b[]存儲讀取到的數(shù)據(jù)的字節(jié)數(shù)組 -
off從目標數(shù)組b[]的off位開始寫入,一般都是從0開始 -
len要讀取的字節(jié)碼長度,一般會是存儲數(shù)組b[]的長度
-
這里的int類型的返回值是實際讀取的字節(jié)數(shù),如果檢測到無數(shù)據(jù)可讀時會返回 -1
無參 read()
/**
* 從輸入流讀取下一個字節(jié)的數(shù)據(jù)。返回值為`int類型`,值范圍在`0-255`之間。如果由于到達流
* 的末尾而沒有可用的字節(jié),則返回`-1`。此方法將一直阻塞,直到輸入數(shù)據(jù)可用、檢測到流的結(jié)
* 尾或引發(fā)異常為止
*/
public abstract int read() throws IOException;
從輸入流讀取下一個字節(jié)的數(shù)據(jù)。返回值為int類型,值范圍在0-255之間。如果由于到達流的末尾而沒有可用的字節(jié),則返回-1。此方法將一直阻塞,直到輸入數(shù)據(jù)可用、檢測到流的結(jié)尾或引發(fā)異常為止
我們再來看一下這個方法的具體實現(xiàn)
public int read() throws IOException {
if (eof) {
return -1;
}
temp = new byte[1];
int n = read(temp, 0, 1);
if (n <= 0) {
return -1;
}
return temp[0] & 0xff;
}
到這里就很奇怪了,這個方法是返回下一個字節(jié)的數(shù)據(jù),可是為什么要返回一個int類型,而不直接返回 byte 類型?并且返回int類型時還有一個& 0xff操作,為什么還要執(zhí)行這個與操作呢?下面就讓我們好好分析一下,為何要返回int類型,而不直接返回byte類型,以及為何會先執(zhí)行一個& 0xff操作
為何要返回int類型
在讀取字節(jié)時,我們肯定需要一個標識來表示已經(jīng)讀到了字節(jié)流末尾,一般會返回-1來標識,但是如果是返回byte類型,就無法標識是否到了文件末尾。所以單憑這點,這里就不能返回byte類
為何要執(zhí)行與操作& 0xff再返回int類型
上面解釋了為何要返回int類型,可是貌似返回int類型也并沒有解決問題..
看這么一個情況,萬一返回的單個字節(jié)以二進制表示是1111 1111,轉(zhuǎn)換成int類型會高位補符號位1,也就是1111 1111 1111 1111 1111 1111 1111 1111,剛好是-1(Java中數(shù)字是以補碼形式存儲的),所以這里就會產(chǎn)生混亂,我們無法區(qū)分返回-1是不是到了字節(jié)流的末尾
那么這個問題該怎么解決呢?
這里要解決的問題其實就是當還沒有讀到字節(jié)流末尾時不能返回-1,并且二進制字節(jié)流也不能改變
所以這里就加入了& 0xff操作,我們再來看前面返回-1的例子
1111 1111高位自動補1后與上0xff(0000 0000 0000 0000 0000 0000 1111 1111)
1111 1111 1111 1111 1111 1111 1111 1111 & 0000 0000 0000 0000 0000 0000 1111 1111 = 0000 0000 0000 0000 0000 0000 1111 1111
可知執(zhí)行了& 0xff操作后,相當于永遠不會返回負數(shù)了,也就不會存在返回-1和到字節(jié)流末尾返回-1沖突的情況,并且實際的二進制結(jié)構(gòu)也沒有改變,可以說是完美的解決了這個問題。
上面的
1111 1111會自動高位補符號位的原因是,當Java檢測到byte要轉(zhuǎn)化或?qū)⒁D(zhuǎn)換成高位類型時,會自動補高位符號位
補符號位
這里再解釋一下補符號位
我們知道byte占一個字節(jié)8位,而int類型占4個字節(jié)32位,所以byte類型向上轉(zhuǎn)換成int類型時需要補符號位,正數(shù)補0,負數(shù)補1。我們補符號位的目的是為了類型轉(zhuǎn)換后大小和符號位都保持不變。
總結(jié)
本篇文章雖然是從IO的read方法接入,其實還是跟Java內(nèi)部的編碼格式有關(guān),如數(shù)字在Java內(nèi)存中是以補碼存儲的,數(shù)字類型轉(zhuǎn)換會高位補符號位等等,要徹底搞懂還是要花些時間的。