Java&Android 基礎知識梳理(6) 字節輸入輸出流

1、概述

Java IO庫中的表明有能力產出數據的數據源對象或者是有能力接收數據的接收端對象,咱們通常把它分紅輸入和輸出兩部分:設計模式

  • 繼承自InputStreamReader派生的類都含有名爲read的方法,用於讀取單個字節或字節數組。
  • 繼承自OuputStreamWriter派生的類都含有名爲write的方法,用於寫入單個字節或字節數組。

咱們一般經過疊合多個對象來提供所指望的功能,這實際上是一種裝飾器設計模式。數組

2、字節輸入流

2.1 InputStream的做用

它的做用是用來表示那些從不一樣數據源產生輸入的類,而最終結果就是經過read方法得到數據源的內容,從數據源中讀出的內容intbyte[]來表示bash

  • 字節數組
  • String對象
  • 文件
  • 管道
  • 一個由其它種類的流組成的序列,以便咱們能夠將它們收集合併到一個流內
  • 其它數據源,如網絡等

2.2 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長度的字節,若是len0,那麼返回0,不然返回實際讀入的字節數,讀入的第一個字節存放在數據b[off]中,若是沒有可讀的,那麼返回-1
  • public long skip(long n) throws IOException 跳過,並丟棄掉n個字節,其最大值爲2048

2.3 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支持提供文件名、FileFileDescription做爲構造函數的參數,它的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));
    }
複製代碼

3、字節輸出流

3.1 OuputStream的做用

OuputStream決定了輸出要去往的目標:函數

  • 字節數組
  • 文件
  • 管道

3.2 OutputStream源碼

InputStream相似,也是一個抽象類,它的子類表明了輸出所要去往的目標,它的關鍵方法以下: ui

咱們主要關注的是 write方法,前兩個 write方法最終都是調用了抽象的 write(int oneByte)方法,最終怎麼寫入是由子類實現的。

3.3 OutputStream的具體實現類

  • ByteArrayOutputStreamByteArrayOutputStream的內部,有一個可變長的byte[]數組,當咱們調用write方法時,就是向這個數組中寫入數據,它還提供了toByteArray/toString方法,來得到當前內部byte[]數組中的內容。
  • FileOutputStream 它和上面相似,只不過寫入的終點換成了所打開的文件。
  • PipedOutputStreamPipedInputStream相關聯。
  • ObjectOutputStreamObjectInputStream相似,只不過它內部組合的是一個OutputStream,當調用writeObject(Object object)方法時,實際上是先將Object進行反序列化轉換爲byte,再輸出到OuputStream所指向的目的地。
  • FilterOutputStream 它的思想和FilterInputStream相似,都是在內部組合了一個OuputStreamFilterOutputStream提供了寫入int/long/shortwrite重載函數,當咱們調用這些函數以後,FilterOutputStream最終會經過內部的OuputStream向它所指向的目的地寫入字節。
相關文章
相關標籤/搜索