這篇來說說位元組流.
兩個頂級父類
位元組流有兩個頂級父類:輸入流InputStream與輸出流OutputStream,兩者均為抽象類.
InputStream
這是位元組輸入流的頂級父類,下面來分析一下它的源碼.
構造器
沒有顯式地寫出構造器,證明隻有一個預設構造器.
方法
一共有三個read():
有一個無參的抽象方法read(),是留給子類去實作的.
public abstract int read() throws IOException;
第二個read()傳入一個位元組數組,是利用第三個read()實作的,這裡直接看看第三read()的代碼:
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
//沒有byte[]就抛出空指針異常.
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
//偏移量或要讀取的長度小于0,或要數組的長度減偏移量大于要讀取的長度就抛出下标越界異常.
} else if (len == 0) {
return 0;
//要讀取的長度為0就傳回0.
}
int c = read(); //調用子類實作的read().
if (c == -1) {
return -1; //子類read()傳回-1(即沒有讀取到資料),這裡也傳回-1.
}
b[off] = (byte)c;
//把讀取到的第一個位元組存入byte[]的下标為off(偏移量)處.
int i = 1;
try {
for (; i < len ; i++) {
c = read(); //讀取
if (c == -1) {
break; //傳回-1就停止.
}
b[off + i] = (byte)c; //存入.
}
} catch (IOException ee) {
}
return i; //傳回讀取了幾個位元組.
}
三個read()的共同特點是,每次都是讀取一個位元組,利用for循環進行連續讀取.當發現沒有讀取到位元組的時候,就傳回-1,裡面for循環中的i,每讀取一個位元組就循環一次,自己也就+1,是以最終傳回的是讀取到的位元組數.
然後是skip(),補上方法内涉及的那個常量,作用是跳過位元組數(n)進行讀取檔案,傳回值根據測試就是傳入的參數.
private static final int MAX_SKIP_BUFFER_SIZE = 2048;
public long skip(long n) throws IOException {
long remaining = n;
int nr;
if (n <= 0) {
return 0; //傳入的數值小于0就傳回0.
}
int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining); //傳入的參數與常量取最小值,最大值是2048.
byte[] skipBuffer = new byte[size]; //建立size大小的位元組數組,最大就是常量大小.
while (remaining > 0) {
nr = read(skipBuffer, 0, (int)Math.min(size, remaining)); //read()
if (nr < 0) {
break; //傳回值小于0(也許隻有-1?)就跳出循環.
}
remaining -= nr;
}
return n - remaining; //最後傳回的實際上就是傳入的位元組數
}
寫了下面的方法測試skip()的傳回值:,檔案大小32767位元組:
public static void main(String[] args) {
InputStream in = null;
int nums = 0;
try {
in = new FileInputStream("1.txt");
System.out.println("skip()傳回值:" + in.skip(16384));
for (int i = 0; i < 1000; i++) {
in.read();
nums ++;
}
System.out.println("read實際讀取的位元組數:" + nums);
nums = 0;
System.out.println("skip()傳回值:" + in.skip(-16384));
while (in.read() > 0) {
nums ++;
}
System.out.println("read實際讀取的位元組數:" + nums);
} catch (IOException e) {
e.getStackTrace();
}
}
運作結果:
skip()傳回值:16384
read實際讀取的位元組數:1000
skip()傳回值:-16384
read實際讀取的位元組數:31767
最開始調用skip()并傳入16384,代表跳過了16384位元組,for循環1000次,就讀取到了1000位元組.然後調用skip()并傳入負數,發現實際的讀取結果是32768-16384-1000+16384 = 31767.
就這些代碼來說,skip()相當于輸入流中的一個"指針",傳入正數就往前跳正數個位元組,然後開始讀取1000個位元組,讀取的時候指針向後移動1000,,然後skip()傳入負值,指針往回跳負數的絕對值個位元組,繼續讀取到最後,就得到了31767這個傳回值.
剩下的方法都沒有實際的邏輯或邏輯很簡單,下面簡單介紹一下它們的含義:
//這個位元組流中還剩多少個位元組.
public int available() throws IOException {
return 0;
}
//關閉這個流,實作Closeable接口的方法.
public void close() throws IOException {}
//在流中标記readLimit位置
public synchronized void mark(int readlimit) {}
//抛出IO異常:不支援mark或reset操作
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
//測試這個輸入流是否支援 mark()和 reset().
public boolean markSupported() {
return false;
}
OutputStream
位元組輸出流的頂級父類,這個類裡面的方法比InputStream中的少.
構造器
依舊是沒有顯式提供構造器.
方法
三個write(),其中有一個抽象方法,依舊是留給子類重寫的,不過相比輸入流參數多變成了一個int值:
public abstract void write(int b) throws IOException;
第二個是調用本類中給出具體實作的write(),直接來看給出具體實作的那個write():
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
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();
//偏移量小于0,或大于位元組數組容量,要寫入的長度小于0,
//偏移量加要寫入的長度大于位元組數組容量或小于0,就抛出下标越界異常.
} else if (len == 0) {
return; //資料全部寫完,退出方法.
}
for (int i = 0 ; i < len ; i++) {
write(b[off + i]); //利用子類實作的write()開始寫入.
}
}
可以看到,與read()傳回int不同,write()沒有傳回值,這點需要注意.
還剩下兩個沒有具體實作的方法:
//關閉這個流,實作Closeable接口的方法.
public void flush() throws IOException {}
//将剩餘的資料沖刷出去,實作Flushable的方法.
public void close() throws IOException {}