前言
流
:把數(shù)據(jù)比作水流,水流的源頭為水源棺聊,數(shù)據(jù)的源頭為數(shù)據(jù)源伞租。試想一下,數(shù)據(jù)從源頭而來(lái)限佩,我們?cè)谶@頭獲取到數(shù)據(jù)流葵诈,然后對(duì)數(shù)據(jù)流進(jìn)行處理。而處理方式就如同:我們?nèi)∮煤永锏乃钔缓鬅_(kāi)作喘,然后飲用一樣。在這里我們需要學(xué)習(xí)JAVA是怎么實(shí)現(xiàn)的這個(gè)處理過(guò)程晕城。
java的基礎(chǔ)中必學(xué)的一課就是IO流泞坦,同時(shí)要學(xué)好java也必須要掌握好IO流的相關(guān)基礎(chǔ)。
一广辰、流的劃分
二暇矫、Reader相關(guān)源碼
2.1 Reader抽象類
(1)通過(guò)構(gòu)造函數(shù)設(shè)置鎖對(duì)象
(2)read讀取流
抽象方法需要子類去實(shí)現(xiàn)
/**
* 將當(dāng)前的流讀到cbuf中
* @param cbuf 目標(biāo)數(shù)組
* @param off 拷貝偏移量,從cbuf 的off之后開(kāi)始存數(shù)據(jù)
* @param len 拷貝長(zhǎng)度
* @return
* @throws IOException
*/
public abstract int read(char cbuf[], int off, int len) throws IOException;
(3)skip 方法
// 輸入想要跳過(guò)的單位择吊,返回實(shí)際跳過(guò)的單位
public long skip(long n) throws IOException {
if (n < 0L)
//很明顯不能跳過(guò)負(fù)數(shù)個(gè)單位
throw new IllegalArgumentException("skip value is negative");
// 最大只能跳過(guò)8912個(gè)單位
int nn = (int) Math.min(n, maxSkipBufferSize);
// 這里有個(gè)同步鎖,說(shuō)明當(dāng)前線程在操作這個(gè)流的時(shí)候是不允許別的線程讀取的
synchronized (lock) {
if ((skipBuffer == null) || (skipBuffer.length < nn))
skipBuffer = new char[nn];
long r = n;
while (r > 0) {
int nc = read(skipBuffer, 0, (int)Math.min(r, nn));
if (nc == -1)
// 如果已經(jīng)后面已經(jīng)沒(méi)有數(shù)據(jù)槽奕,則退出循環(huán)几睛,這個(gè)r就意味著剩余沒(méi)有跳過(guò)的單位數(shù)量
break;
r -= nc;
}
//需要跳過(guò)的數(shù)量-還沒(méi)有跳過(guò)的數(shù)量=真實(shí)跳過(guò)的數(shù)量
return n - r;
}
}
(4)ready方法
是否流已經(jīng)就緒
(5)markSupported方法
是否允許標(biāo)記
(6)mark方法
標(biāo)記當(dāng)前在流中的位置
(7)reset方法
重置標(biāo)記
(8)close方法
關(guān)閉流
2.2 BufferedReader
可以看到在類內(nèi)沒(méi)有一個(gè)方法是直接加鎖的,但是在方法內(nèi)部基本上都加上了鎖粤攒,因?yàn)樵谶m當(dāng)?shù)牡胤郊渔i可以提高代碼效率所森。
public int read(char cbuf[], int off, int len) throws IOException {
// 對(duì)當(dāng)前對(duì)象加鎖
synchronized (lock) {
// 流是否還打開(kāi)著
ensureOpen();
// 移除數(shù)組越界的情況
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
// 嘗試讀取數(shù)據(jù)
int n = read1(cbuf, off, len);
if (n <= 0) return n;
// 循環(huán)讀取數(shù)據(jù)
while ((n < len) && in.ready()) {
int n1 = read1(cbuf, off + n, len - n);
if (n1 <= 0) break;
n += n1;
}
return n;
}
}
2.3 InputStreamReader
這個(gè)類差不多就是StreamDecoder包裝了一層囱持,而StreamDecoder又是依賴的InputStream
2.4 StringReader
字符串的流讀取類
public int read(char cbuf[], int off, int len) throws IOException {
synchronized (lock) {
ensureOpen();
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
if (next >= length)
return -1;
int n = Math.min(length - next, len);
// 以char為單位進(jìn)行讀取
str.getChars(next, next + n, cbuf, off);
next += n;
return n;
}
}
三、Writer相關(guān)源碼
3.1 Writer抽象類
(1)通過(guò)構(gòu)造函數(shù)設(shè)置鎖對(duì)象
(2)write方法
方法內(nèi)部通過(guò)構(gòu)造函數(shù)傳入的對(duì)象加鎖實(shí)現(xiàn)同步
/**
* 將流寫入到cbuf中.
* @param cbuf 需要寫入的源數(shù)組
* @param off 從cbuf第一個(gè)位置開(kāi)始的偏移量
* @param len 需要從cbuf拷貝的長(zhǎng)度
*/
public abstract void write(char cbuf[], int off, int len) throws IOException;
(3)append方法
在當(dāng)前流后面附加上寫入的內(nèi)容焕济,該方法是基于write方法的
(4)flush方法
將流刷入到目的地中纷妆,但是有個(gè)要注意的點(diǎn),如果寫入需要底層操作系統(tǒng)晴弃,如寫入到硬盤的文件中掩幢,flush并不能一定保證寫入到文件中。
(5)close方法
關(guān)閉流
3.2 BufferedWriter
public void write(char cbuf[], int off, int len) throws IOException {
synchronized (lock) {
// 寫流之前需要判斷是否流還打開(kāi)著
ensureOpen();
// 判斷是否數(shù)組越界
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
if (len >= nChars) {
// 當(dāng)超過(guò)一開(kāi)始定義的charbuffer長(zhǎng)度上鞠,那么直接刷到底層操作系統(tǒng)中际邻,避免內(nèi)存中數(shù)據(jù)量太大
// 這個(gè)flushBuffer調(diào)用的是流對(duì)象的write方法,所以實(shí)際還需要看一下實(shí)現(xiàn)對(duì)象是怎么操作的
flushBuffer();
out.write(cbuf, off, len);
return;
}
int b = off, t = off + len;
while (b < t) {
int d = min(nChars - nextChar, t - b);
System.arraycopy(cbuf, b, cb, nextChar, d);
b += d;
nextChar += d;
if (nextChar >= nChars)
flushBuffer();
}
}
}
3.3 StringWriter
它調(diào)用的StringBuffer芍阎,write方法都是依賴了StringBuffer的append
3.4 OutputStreamWriter
它大部分是依賴StreamEncoder實(shí)現(xiàn)的方法功能世曾,就像是包裝了StreamEncoder一下,StreamEncoder也是繼承了Writer谴咸,功能實(shí)現(xiàn)需要依賴OutputStream轮听。
3.5 PrintWriter
(1)支持選擇寫入的編碼格式
public PrintWriter(String fileName, String csn)
throws FileNotFoundException, UnsupportedEncodingException
{
this(toCharset(csn), new File(fileName));
}
(2)這個(gè)類中,每次拋出異常都會(huì)記錄岭佳,方便后面查詢是否流處理是出現(xiàn)過(guò)異常
catch (IOException x) {
trouble = true;
}
四蕊程、InputStream主要源碼
4.1 read()單字節(jié)讀取
public abstract int read() throws IOException;
4.2 read(byte b[])多字節(jié)讀取
將數(shù)據(jù)讀入到byte b[]中
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
4.3 read(byte b[], int off, int len) 帶偏移位置的讀取
// 將數(shù)據(jù)讀入到b[]中,從off偏移開(kāi)始驼唱,讀入len個(gè)長(zhǎng)度的數(shù)據(jù)
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
// 嘗試讀取一個(gè)字節(jié)
int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
// 循環(huán)讀取數(shù)據(jù)藻茂,單個(gè)字節(jié)單個(gè)字節(jié)的讀取,直到長(zhǎng)度夠
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
4.4 available數(shù)據(jù)長(zhǎng)度獲取
可見(jiàn)默認(rèn)都是返回的0玫恳,需要子類自己去實(shí)現(xiàn)長(zhǎng)度獲取
public int available() throws IOException {
return 0;
}
4.5 transferTo
將流轉(zhuǎn)移到OutputStream中辨赐,返回已經(jīng)完成轉(zhuǎn)移的長(zhǎng)度
4.6 FileInputStream
在代碼開(kāi)發(fā)中常用的是文件流讀取,它的打開(kāi)流和讀取流方法都是依賴native方法實(shí)現(xiàn)的京办,這里就不再深入了掀序,不過(guò)它還依賴native實(shí)現(xiàn)了available方法,換句話說(shuō)就是它能夠獲取到流的長(zhǎng)度惭婿。
五不恭、OutputStream主要源碼
5.1 write寫方法
// 將數(shù)據(jù)寫入到b[]中,從off位置開(kāi)始财饥,寫入長(zhǎng)度為len
public void write(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
for (int i = 0 ; i < len ; i++) {
write(b[off + i]);
}
}