Java IO
庫中的流表明有能力產出數據的數據源對象或者是有能力接收數據的接收端對象,咱們通常把它分紅輸入和輸出兩部分:設計模式
InputStream
或Reader
派生的類都含有名爲read
的方法,用於讀取單個字節或字節數組。OuputStream
或Writer
派生的類都含有名爲write
的方法,用於寫入單個字節或字節數組。咱們一般經過疊合多個對象來提供所指望的功能,這實際上是一種裝飾器設計模式。數組
InputStream
的做用它的做用是用來表示那些從不一樣數據源產生輸入的類,而最終結果就是經過read
方法得到數據源的內容,從數據源中讀出的內容用int
或byte[]
來表示:bash
String
對象InputStream
源碼InputStream
是一個抽象類,全部表示字節輸入的流都是繼承於它,它實現瞭如下接口: 網絡
public abstract int read() throws IOException
返回輸入流的下一個字節(next byte
),若是已經到達輸入流的末尾,那麼返回-1
public int read(byte b[]) throws IOException
嘗試從輸入流中讀取b.length
長度的字節,存入到b
中,若是已經到達末尾返回-1
,不然返回成功寫入到b
中的字節數。public int read(byte b[], int off, int len) throws IOException
嘗試從輸入流中讀取下len
長度的字節,若是len
爲0
,那麼返回0
,不然返回實際讀入的字節數,讀入的第一個字節存放在數據b[off]
中,若是沒有可讀的,那麼返回-1
。public long skip(long n) throws IOException
跳過,並丟棄掉n
個字節,其最大值爲2048
。InputStream
的具體實現類ByteArrayInputStream
它接收byte[]
做爲構造函數的參數,咱們調用read
方法時,就是從byte[]
數組裏,讀取字節。public ByteArrayInputStream(byte buf[], int offset, int length) {
this.buf = buf;
this.pos = offset;
this.count = Math.min(offset + length, buf.length);
this.mark = offset;
}
public synchronized int read() {
return (pos < count) ? (buf[pos++] & 0xff) : -1;
}
複製代碼
StringBufferInputStream
已通過時,推薦使用StringReader
。FileInputStream
FileInputStream
支持提供文件名、File
和FileDescription
做爲構造函數的參數,它的read
調用的是底層的native
方法。public FileInputStream(File file) throws FileNotFoundException {
String name = (file != null ? file.getPath() : null);
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(name);
}
if (name == null) {
throw new NullPointerException();
}
if (file.isInvalid()) {
throw new FileNotFoundException("Invalid file path");
}
fd = new FileDescriptor();
fd.attach(this);
path = name;
open(name);
}
public int read() throws IOException {
return read0();
}
private native int read0() throws IOException;
複製代碼
PipedInputStream
經過通訊管道來交換數據,若是兩個線程但願進行數據的傳輸,那麼它們一個建立管道輸出流,另外一個建立管道輸入流,它必需要和一個PipedOutputStream
相鏈接。public PipedInputStream(int pipeSize) {
if (pipeSize <= 0) {
throw new IllegalArgumentException("pipe size " + pipeSize + " too small");
}
buffer = new byte[pipeSize];
}
public PipedInputStream(PipedOutputStream out, int pipeSize) throws IOException {
this(pipeSize);
connect(out);
}
@Override
public synchronized int read() throws IOException {
if (!isConnected) {
throw new IOException("Not connected");
}
if (buffer == null) {
throw new IOException("InputStream is closed");
}
lastReader = Thread.currentThread();
try {
int attempts = 3;
while (in == -1) {
// Are we at end of stream?
if (isClosed) {
return -1;
}
if ((attempts-- <= 0) && lastWriter != null && !lastWriter.isAlive()) {
throw new IOException("Pipe broken");
}
notifyAll();
wait(1000);
}
} catch (InterruptedException e) {
IoUtils.throwInterruptedIoException();
}
int result = buffer[out++] & 0xff;
if (out == buffer.length) {
out = 0;
}
if (out == in) {
in = -1;
out = 0;
}
notifyAll();
return result;
}
複製代碼
SequenceInputStream
將多個InputStream
鏈接在一塊兒,一個讀完後就完畢,並讀下一個,它接收兩個InputStream
對象或者一個容納InputStream
對象的容器Enumeration
。public int read() throws IOException {
while (in != null) {
int c = in.read();
if (c != -1) {
return c;
}
nextStream();
}
return -1;
}
複製代碼
ObjectInputStream
它和一個InputStream
相關聯,源數據都是來自於這個InputStream
,它繼承於InputStream
,而且它和傳入的InputStream
並非直接關聯的,中間經過了BlockDataInputStream
進行中轉,要關注的就是它的readObject
方法,它會把一個以前序列化過的對象進行反序列化,而後獲得一個Object
對象,它的目的在於將(把二進制流轉換成爲對象)和(從某個數據源中讀出字節流)這兩個操做獨立開來,讓它們能夠隨意地組合。public ObjectInputStream(InputStream in) throws IOException {
verifySubclass();
bin = new BlockDataInputStream(in);
handles = new HandleTable(10);
vlist = new ValidationList();
serialFilter = ObjectInputFilter.Config.getSerialFilter();
enableOverride = false;
readStreamHeader();
bin.setBlockDataMode(true);
}
public final Object readObject() throws IOException, ClassNotFoundException {
if (enableOverride) {
return readObjectOverride();
}
// if nested read, passHandle contains handle of enclosing object
int outerHandle = passHandle;
try {
Object obj = readObject0(false);
handles.markDependency(outerHandle, passHandle);
ClassNotFoundException ex = handles.lookupException(passHandle);
if (ex != null) {
throw ex;
}
if (depth == 0) {
vlist.doCallbacks();
}
return obj;
} finally {
passHandle = outerHandle;
if (closed && depth == 0) {
clear();
}
}
}
複製代碼
FilterInputStream
它的構造函數參數就是一個InputStream
:protected FilterInputStream(InputStream in) {
this.in = in;
}
public int read() throws IOException {
return in.read();
}
複製代碼
這個類很特殊,前面的InputStream
子類都是傳入一個數據源(Pipe/byte[]/File
)等等,而後經過重寫read
方法從數據源中讀取數據,而FilterInputStream
則是將InputStream
組合在內部,它調用in
去執行InputStream
定義的抽象方法,也就是說它不會改變組合在內部的InputStream
所對應的數據源。 另外,它還新增了一些方法,這些方法底層仍是調用了read
方法,可是它封裝了一些別的操做,好比DataInputStream
中的readInt
,它調用in
連續讀取了四次,而後拼成一個int
型返回給調用者,之因此採用組合,而不是繼承,目的是將(把二進制流轉換成別的格式)和(從某個數據源中讀出字節流)這兩個操做獨立開來,讓它們能夠隨意地組合。ide
public final int readInt() throws IOException {
int ch1 = in.read();
int ch2 = in.read();
int ch3 = in.read();
int ch4 = in.read();
if ((ch1 | ch2 | ch3 | ch4) < 0)
throw new EOFException();
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
}
複製代碼
OuputStream
的做用OuputStream
決定了輸出要去往的目標:函數
OutputStream
源碼和InputStream
相似,也是一個抽象類,它的子類表明了輸出所要去往的目標,它的關鍵方法以下: ui
write
方法,前兩個
write
方法最終都是調用了抽象的
write(int oneByte)
方法,最終怎麼寫入是由子類實現的。
OutputStream
的具體實現類ByteArrayOutputStream
在ByteArrayOutputStream
的內部,有一個可變長的byte[]
數組,當咱們調用write
方法時,就是向這個數組中寫入數據,它還提供了toByteArray/toString
方法,來得到當前內部byte[]
數組中的內容。FileOutputStream
它和上面相似,只不過寫入的終點換成了所打開的文件。PipedOutputStream
和PipedInputStream
相關聯。ObjectOutputStream
和ObjectInputStream
相似,只不過它內部組合的是一個OutputStream
,當調用writeObject(Object object)
方法時,實際上是先將Object
進行反序列化轉換爲byte
,再輸出到OuputStream
所指向的目的地。FilterOutputStream
它的思想和FilterInputStream
相似,都是在內部組合了一個OuputStream
,FilterOutputStream
提供了寫入int/long/short
的write
重載函數,當咱們調用這些函數以後,FilterOutputStream
最終會經過內部的OuputStream
向它所指向的目的地寫入字節。