(最近剛來到簡(jiǎn)書平臺(tái),以前在CSDN上寫的一些東西,也在逐漸的移到這兒來,有些篇幅是很早的時(shí)候?qū)懴碌?,因此可能?huì)看到一些內(nèi)容雜亂的文章,對(duì)此深感抱歉,以下為正文)
正文
本篇講述的是Java IO包中的PushbackInputStream類。我們知道,通常情況下我們從流中讀取數(shù)據(jù)時(shí)都是順序操作的,也許流中的數(shù)據(jù)并不都是我們需要的,按照平常的流,我們要做的是就是將流中的數(shù)據(jù)依讀取取出,并對(duì)取出的數(shù)據(jù)進(jìn)行篩選,不符合條件的數(shù)據(jù)就丟棄。PushbackInputStream流則稍有不同,它通過內(nèi)部的一個(gè)緩存從而支持了數(shù)據(jù)的回推,在上述場(chǎng)景下,當(dāng)遇到不需要的數(shù)據(jù)時(shí),PushbackInputStream還可以將數(shù)據(jù)重新推回到流中,下面先附上源碼進(jìn)行簡(jiǎn)單的分析。
PushbackInputStream.java
package java.io;
public class PushbackInputStream extends FilterInputStream {
/**
* 定義了一個(gè)byte型數(shù)組,作為流中讀取數(shù)據(jù)是的臨時(shí)緩存,PushbackInputStream中的push back回推功能的實(shí)現(xiàn),主要就是依賴這個(gè)緩存數(shù)組來實(shí)現(xiàn)的。
*/
protected byte[] buf;
/**
* 定義了一個(gè)int型數(shù)值,該值表示實(shí)現(xiàn)回退功能的緩存數(shù)組中下一個(gè)要讀取的字節(jié)數(shù)據(jù)的位置。
*/
protected int pos;
/**
* 該方法用于確定當(dāng)前流是否保持開啟狀態(tài),如果當(dāng)前流未開啟則拋出對(duì)應(yīng)異常。
*/
private void ensureOpen() throws IOException {
if (in == null)
throw new IOException("Stream closed");
}
/**
* 一個(gè)帶兩個(gè)參數(shù)的構(gòu)造方法,第一個(gè)參數(shù)為一個(gè)InputStream對(duì)象,第二個(gè)參數(shù)為一個(gè)int型變量,該變量決定了內(nèi)部緩存數(shù)組buf的容量大小。
*/
public PushbackInputStream(InputStream in, int size) {
super(in);
if (size <= 0) {
throw new IllegalArgumentException("size <= 0");
}
this.buf = new byte[size];
this.pos = size;
}
/**
* 帶有一個(gè)參數(shù)的構(gòu)造方法,內(nèi)部其實(shí)是調(diào)用上面帶兩個(gè)參數(shù)的構(gòu)造方法,內(nèi)部緩存數(shù)組容量默認(rèn)為1。
*/
public PushbackInputStream(InputStream in) {
this(in, 1);
}
/**
* 每次讀取一個(gè)字節(jié)的數(shù)據(jù)。從方法中可以看出,優(yōu)先讀取回推緩存中的數(shù)據(jù),如果回推緩存中沒有數(shù)據(jù),那么就正常調(diào)用InputStream對(duì)應(yīng)的read方法。
*/
public int read() throws IOException {
//在讀取數(shù)據(jù)前先確定當(dāng)前流處于開啟狀態(tài)
ensureOpen();
//pos在初始化時(shí)其值等于回推緩存的容量大小,如果pos小于buf.length,則表示回推緩存中已經(jīng)存在回推的數(shù)據(jù),那么讀取數(shù)據(jù)時(shí),先從回推緩存中取數(shù)據(jù)。
if (pos < buf.length) {
//這里有個(gè)小操作&0xfff,也許有人不知道為什么,一個(gè)byte型&0xff不就是其本身嗎,這里牽扯到了在計(jì)算機(jī)中數(shù)據(jù)存儲(chǔ)的問題,在計(jì)算機(jī)中,數(shù)據(jù)存儲(chǔ)都是使
//二進(jìn)制的補(bǔ)碼進(jìn)行存儲(chǔ)的,當(dāng)我們從流中取出一個(gè)字節(jié)的數(shù)據(jù)后,當(dāng)轉(zhuǎn)換成int型是,計(jì)算機(jī)會(huì)自動(dòng)將其高位用1補(bǔ)位,這時(shí),從二進(jìn)制數(shù)據(jù)本身來看,它已經(jīng)
//被改變了,&0xff這樣可以保證其低8位的數(shù)據(jù)位不發(fā)生變化,其高位都是0,這樣就保證了數(shù)據(jù)的一致性。
return buf[pos++] & 0xff;
}
return super.read();
}
/**
* 一次讀取多個(gè)字節(jié)的read方法,其包含三個(gè)參數(shù),第一個(gè)為承載讀取的數(shù)據(jù)的字節(jié)數(shù)組,第二個(gè)參數(shù)為從數(shù)組的哪個(gè)位置開始存儲(chǔ)數(shù)據(jù),第三個(gè)參數(shù)則是要進(jìn)行存儲(chǔ)的
* 數(shù)據(jù)長(zhǎng)度。
*/
public int read(byte[] b, int off, int len) throws IOException {
//在讀取數(shù)據(jù)前,先確定當(dāng)前流狀態(tài)是否保持開啟狀態(tài)。
ensureOpen();
//對(duì)傳入的參數(shù)進(jìn)行安全檢測(cè),如果不符要切的則進(jìn)行相應(yīng)的操作(拋出異常或者是直接返回)。
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è)int型變量avail,用來接收buf.length-pos的值,該值實(shí)際表示了在回推緩存中實(shí)際存在的數(shù)據(jù)數(shù)量。
int avail = buf.length - pos;
//avail>0,表示回推緩存中存在數(shù)據(jù)。
if (avail > 0) {
//len<avail,表示所要讀取的字節(jié)數(shù)量小于回推緩存中的數(shù)據(jù)數(shù)量,那么此時(shí)將avail的值置為len。
if (len < avail) {
avail = len;
}
//將回推緩存中指定長(zhǎng)度的數(shù)據(jù)拷貝到傳入的字節(jié)數(shù)組中去。
System.arraycopy(buf, pos, b, off, avail);
//pos+avail表示下一次從回推緩存中取數(shù)據(jù)的位置。
pos += avail;
//off+avail表示傳入的字節(jié)數(shù)組下一次存儲(chǔ)的位置。
off += avail;
//len-avail表示剩余還需要寫入的數(shù)據(jù)長(zhǎng)度。
len -= avail;
}
//接下來是正常情況下直接從流中讀取多個(gè)字節(jié)的數(shù)據(jù)。len大于0表示還需要向傳入的字節(jié)數(shù)組中傳入數(shù)據(jù)。
if (len > 0) {
//直接調(diào)用父類讀取多個(gè)字節(jié)的read方法,從流中直接讀取。
len = super.read(b, off, len);
//len=-1,表示流中已經(jīng)沒有數(shù)據(jù)可讀,此時(shí)判斷avail的是否為0,即判斷是否從回推緩存中讀取過數(shù)據(jù),如果沒有則直接返回-1,否則返回avail。
if (len == -1) {
return avail == 0 ? -1 : avail;
}
//該種情況是同時(shí)從流中和回推緩存中讀取了數(shù)據(jù),所以此時(shí)需要返回avial+len的值,從兩個(gè)地方讀取的數(shù)據(jù)總和才是真正讀取的數(shù)據(jù)總量
return avail + len;
}
//該種情況是回推緩存已經(jīng)能滿足讀取數(shù)據(jù)的要求,此時(shí)只需返回avail值即可。
return avail;
}
/**
* 一次可以回推一個(gè)字節(jié)數(shù)據(jù)的unread方法,帶一個(gè)int型參數(shù),改參數(shù)表示了要回推的數(shù)據(jù)。
*/
public void unread(int b) throws IOException {
//在進(jìn)行回推操作前,要確保當(dāng)前是處于開啟狀態(tài)。
ensureOpen();
//pos等于0,表示回推緩存中已經(jīng)沒有足夠的容量再放置回推的數(shù)據(jù)內(nèi)容,所以此時(shí)拋出對(duì)應(yīng)的異常。
if (pos == 0) {
throw new IOException("Push back buffer is full");
}
//將傳入的數(shù)據(jù)放置與回推緩存中的相應(yīng)位置。
buf[--pos] = (byte)b;
}
/**
* 一次可以回推多個(gè)字節(jié)數(shù)據(jù)的unread方法,帶有三個(gè)參數(shù),第一個(gè)參數(shù)為一個(gè)byte型數(shù)組,用于存放所要進(jìn)行回推操作的數(shù)據(jù),第二和第三個(gè)參數(shù)都是一個(gè)int型數(shù)值,
* 分別代表從傳入的數(shù)組中取出數(shù)據(jù)的起點(diǎn),以及其需要回推的數(shù)據(jù)長(zhǎng)度。
*/
public void unread(byte[] b, int off, int len) throws IOException {
//在進(jìn)行回推操作前,需要先確定當(dāng)前流是否依然處于開啟狀態(tài)。
ensureOpen();
//如果len>pos,則表示要進(jìn)行回推操作的數(shù)據(jù)長(zhǎng)度已經(jīng)超過了內(nèi)置的回推緩存數(shù)組的容量,那么此時(shí)將拋出對(duì)應(yīng)的異常。
if (len > pos) {
throw new IOException("Push back buffer is full");
}
//該種情況下表示數(shù)據(jù)可以正?;赝频絻?nèi)置的回推緩存區(qū)之中。
pos -= len;
//利用System.arraycopy方法將指定的數(shù)據(jù)拷貝到內(nèi)置的回推緩存區(qū)之中。
System.arraycopy(b, off, buf, pos, len);
}
/**
* 一次可以回推多個(gè)字節(jié)數(shù)據(jù)的unread方法,該方法帶有一個(gè)byte數(shù)組型參數(shù),其內(nèi)部實(shí)質(zhì)是調(diào)用上面帶3個(gè)參數(shù)的unread方法,將傳入的數(shù)組的所有數(shù)據(jù)都回推到內(nèi)置
* 回推緩存中去。
*/
public void unread(byte[] b) throws IOException {
unread(b, 0, b.length);
}
/**
* 該方法返回當(dāng)前流中可以讀取的數(shù)據(jù)總量。
*/
public int available() throws IOException {
//進(jìn)行操作前先確定當(dāng)前流是否處于開啟狀態(tài)。
ensureOpen();
//定義了一個(gè)int型變量n用來存方法buf.length-pos的值,該值表達(dá)了內(nèi)置回推緩存中已經(jīng)回推的數(shù)據(jù)數(shù)量。
int n = buf.length - pos;
//定義了一個(gè)int型變量avail用來存放調(diào)用父類方法available的返回值,該值表達(dá)了當(dāng)前流中可還可以進(jìn)行讀取操作的數(shù)據(jù)數(shù)量。
int avail = super.available();
//判斷回推的數(shù)據(jù)數(shù)量+流中可以讀取的數(shù)據(jù)數(shù)量是否大于Integer.MAX_VALUE,如果大于則返回Integer.MAX_VALUE,否則返回兩者之和。
return n > (Integer.MAX_VALUE - avail)
? Integer.MAX_VALUE
: n + avail;
}
/**
* 該方法用于跳過指定的字節(jié)數(shù),最后返回實(shí)際跳過的字節(jié)數(shù)。
*/
public long skip(long n) throws IOException {
//進(jìn)行操作前確定當(dāng)前流處于開啟狀態(tài)。
ensureOpen();
//如果傳入的參數(shù)小于等于0,直接返回0,表示
if (n <= 0) {
return 0;
}
//定義了一個(gè)long型數(shù)據(jù)pskip,初始時(shí)賦值為buf.length-pos,該值代表著內(nèi)置緩存區(qū)中實(shí)際推回的數(shù)據(jù)長(zhǎng)度。
long pskip = buf.length - pos;
//pskip>0,表示內(nèi)置的緩存區(qū)中實(shí)際存在著回推的數(shù)據(jù)。
if (pskip > 0) {
//如果需要跳過的字節(jié)數(shù)n小于回推緩存區(qū)中回推的字節(jié)數(shù),將pskip置為n。
if (n < pskip) {
pskip = n;
}
//將回推緩存區(qū)讀取位置后移pskip的長(zhǎng)度,并且將需要跳過的字節(jié)數(shù)減去pskip。
pos += pskip;
n -= pskip;
}
//如果回推緩存區(qū)的數(shù)據(jù)不足以滿足跳過的長(zhǎng)度,那么還需調(diào)用父類流中的skip方法。并且將pskip累加上跳過的字節(jié)數(shù)。
if (n > 0) {
pskip += super.skip(n);
}
return pskip;
}
/**
* 該方法用于判別當(dāng)前流是否支持流標(biāo)記功能,該方法返回false,所以表示當(dāng)前流不支持標(biāo)記功能。
*/
public boolean markSupported() {
return false;
}
/**
* 該方法用于在流中制定位置留下標(biāo)記,因?yàn)楫?dāng)前流中不支持流標(biāo)記功能,所以這里是空實(shí)現(xiàn)。
*/
public synchronized void mark(int readlimit) {
}
/**
* 該方法用于將讀取位置重置到流中標(biāo)記的位置,因?yàn)楫?dāng)前流不支持流標(biāo)記功能,所以此處直接拋出相應(yīng)異常。
*/
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
/**
* 該方法用于關(guān)閉當(dāng)前流,并將內(nèi)置的回推緩存置為null。
*/
public synchronized void close() throws IOException {
if (in == null)
return;
in.close();
in = null;
buf = null;
}
}
通過上面對(duì)源碼的簡(jiǎn)單分析,我們對(duì)PushbackInputStream有了簡(jiǎn)單的了解,下面用一個(gè)簡(jiǎn)單的例子來闡述該類的用法:
package PushBackIO;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PushbackInputStream;
public class PushBackIOTest {
public static void main(String[] args) {
int data;
try (PushbackInputStream pis = new PushbackInputStream(
new FileInputStream(new File("./src/file/testcopy.txt")));
PrintStream ps = new PrintStream(System.out);) {
while ((data = pis.read()) != -1) {
if (data =='1') {
pis.unread(data);
System.out.println("\r\nunread data :" + (char)data);
pis.read();
}
ps.write(data);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
執(zhí)行上述代碼后可以看到如下效果:

運(yùn)行結(jié)果
以上為本篇的全部?jī)?nèi)容。