基本概念
InpuStream/OuputStream(位元組輸入流/位元組輸出流),它們都是抽象類,并且都實作了 Closeable 接口,表示所有位元組(byte)輸入流(輸出流)的類。
源碼分析
1.InputStream
類結構如下
成員變量
// 成員常量,表示 skip(跳躍、丢棄)操作時能丢棄的最大數量
private static final int MAX_SKIP_BUFFER_SIZE = ;
構造函數,它有一個預設的無參造函數。
read 方法,定義了三個 read 方法,代表不同的讀取方式。
- ① 是一個抽象方法,留給子類實作。
- ② 通過調用 ③ 實作。
- ③ 的實際操作依靠 ① 來完成。
// ①從輸入流中讀取資料的下一個位元組
public abstract int read() throws IOException;
// ②從輸入流中讀取一定數量的位元組,并将其存儲在緩沖區數組 b 中
public int read(byte b[]) throws IOException {
return read(b, , b.length);
}
// ③将輸入流中最多 len 個資料位元組讀入 byte 數組
public int read(byte b[], int off, int len) throws IOException {
// 校驗參數的合法性
if (b == null) {
throw new NullPointerException();
} else if (off < || len < || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == ) {
return ;
}
// 先讀取一個位元組,用來判斷是否到流的末尾。若是,傳回 -1
int c = read();
if (c == -) {
return -;
}
// 将上面讀取的一個位元組的值添加進數組
b[off] = (byte) c;
int i = ;
// 繼續讀取流,直到 len 或 流末尾
try {
for (; i < len; i++) {
// 實際通過 read 來進行操作
c = read();
if (c == -) {
break;
}
// 位元組存放的位置從 off 開始
b[off + i] = (byte) c;
}
} catch (IOException ee) {
}
return i;
}
skip 方法,它表示跳躍,丢棄操作。
它可以跳過(丢棄)指定數量的位元組,然後再繼續讀取流。
它實際上通過 read 方法來進行跳躍操作,然後建立了一個位元組數組用來儲存被丢棄的位元組。
public long skip(long n) throws IOException {
long remaining = n;
int nr;
// 如果 n 為 0,表示不進行跳躍(丢棄)操作。
if (n <= ) {
// 關鍵 -->傳回值為 0 ,說明有 0 個位元組被丢棄
return ;
}
// 最多隻能丢棄 2048 個位元組
int size = (int) Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
// 建立數組用來儲存被丢棄的位元組
byte[] skipBuffer = new byte[size];
while (remaining > ) {
// 關鍵 --> 通過 read 方法完成該操作
nr = read(skipBuffer, , (int) Math.min(size, remaining));
// 如果進行該操作時,提前到達流末尾,則不再繼續
if (nr < ) {
break;
}
// 表示剩餘需要丢棄位元組的數量
remaining -= nr;
}
return n - remaining;
}
剩餘方法,大多沒有具體的實作,都留給子類做擴充,這裡先不細說。
// 傳回流中剩下可讀取的位元組數量,預設傳回 0
public int available() throws IOException {
return ;
}
// 關閉流操作,空方法,留給子類實作
public void close() throws IOException {
}
// 标記操作,空方法,留給子類實作
public synchronized void mark(int readlimit) {
}
// 釋放标記,與 mark 配套使用
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
// 判斷是否支援标記,預設不支援
public boolean markSupported() {
return false;
}
2.OutputStream
類結構如下
構造函數,它有一個無參的構造函數
write 方法,同樣有三種寫入方式。
- ① 是一個抽象方法,留給子類實作。
- ② 通過調用 ③ 實作。
- ③ 的實際操作依靠 ① 來完成。
// 将指定的位元組寫入此輸出流(抽象方法)
public abstract void write(int b) throws IOException;
// 将 b.length 個位元組從指定的 byte 數組寫入此輸出流
public void write(byte b[]) throws IOException {
write(b, , b.length);
}
// 将指定 byte 數組中從偏移量 off 開始的 len 個位元組寫入此輸出流
public void write(byte b[], int off, int len) throws IOException {
// 校驗參數的合法性
if (b == null) {
throw new NullPointerException();
} else if ((off < ) || (off > b.length) || (len < ) || ((off + len) > b.length) || ((off + len) < )) {
throw new IndexOutOfBoundsException();
} else if (len == ) {
return;
}
// 将數組中的所有位元組内容寫入流
for (int i = ; i < len; i++) {
write(b[off + i]);
}
}
剩餘方法
//将緩存中流的内容強制輸出到目的地
public void flush() throws IOException {
}
public void close() throws IOException {
}
原了解釋
1. 按位元組讀取
假設下面代碼一段位元組流
當位元組輸入流按位元組讀取時,即 read():
- 讀取第一個位元組,傳回值1。
- 讀取第二個位元組,傳回值 2.
- 讀取到最後,發現已經到達流末尾了,傳回 -1。
2.按位元組數組讀取
假設位元組數組的大小為 3,即 read(buffer,0,3):
- 它會先讀取 1 個位元組用于判斷流是否達到末尾,并将值放入數組的第一個位置(n =0)。
- 然後按照循環(n<3)來讀取,并将值添加數組
3.跳躍操作
假設現在跳躍的個數為 3。
将丢棄的位元組添加進緩沖數組。