Java OutputStream 淺析

  • Java InputStream 淺析 一文中,介紹了 InputStream 相關的知識
  • 本文將簡要介紹 Java 中的 OutputStream 類體系結構

1 OutputStream 子類結構

  • 如 InputStream 一樣,OutputStream 也是一個典型的裝飾者模式的示例
  • 它的體系結構和 InputStream 很是相似
  • OutputStream 比 InputStream 多繼承一個 Flushable 接口,用於將緩衝輸出寫入 underlying stream
    在這裏插入圖片描述

1.1 OutputStream 源碼分析

public abstract class OutputStream implements Closeable, Flushable {
    //提供模板方法,供子類實現
    public abstract void write(int b) throws IOException;
    //內部使用,抽象類,自動擴展子類的實現功能
    public void write(byte b[]) throws IOException {
        write(b, 0, b.length);
    }
    //同上
    public void write(byte b[], int off, int len) throws IOException {...}
    //空實現,交給子類實現,用於實現緩存的輸出流
    public void flush() throws IOException {}
    //空實現,交給子類實現
    public void close() throws IOException {}
}

2 OutputStream 具體實現類源碼分析

2.1 ByteArrayOutputStream —— 字節輸出流

  • 核心參數:
    • buf[]:存儲輸出字節的地方,可以自動擴容
    • count:buf[] 數組,有效內容的最大下標
  • 核心方法:
    • write(int b):將 int 類型 b 的 低 8 位 存入字節數組中
public class ByteArrayOutputStream extends OutputStream {

    protected byte buf[];
	//緩存有效數量
    protected int count;
    
    public synchronized void write(int b) {
        ensureCapacity(count + 1);
        buf[count] = (byte) b;
        count += 1;
    }
    //將 b[] 字節數組,放入字節流中
    public synchronized void write(byte b[], int off, int len) {
        if ((off < 0) || (off > b.length) || (len < 0) ||
            ((off + len) - b.length > 0)) {
            throw new IndexOutOfBoundsException();
        }
        ensureCapacity(count + len);
        System.arraycopy(b, off, buf, count, len);
        count += len;
    }
    //內部調用 out 的 write 方法
    public synchronized void writeTo(OutputStream out) throws IOException {
        out.write(buf, 0, count);
    }
    //通過 count = 0,實現 reset 方法
    public synchronized void reset() {count = 0;}
    //通過本輸出流,創建一個 字節數組
    public synchronized byte toByteArray()[] {
        return Arrays.copyOf(buf, count);
    }
    //即是將字節數組,創建爲字符串,通過平臺默認的編碼方式
    public synchronized String toString() {
        return new String(buf, 0, count);
    }
    //即是將字節數組,創建爲字符串,通過指定的編碼方式
    public synchronized String toString(String charsetName) {
        return new String(buf, 0, count, charsetName);
    }
    //字節數組流,不需要 close
    public void close() throws IOException {
    }
}

2.2 FileOutputStream —— 文件輸出流

  • 核心參數:
    • append:是否爲 append 模式
    • path:文件路徑
  • 核心方法:
    • write(int b, boolean append):寫入一個字節到輸出流
  • 沒有實現 flush 方法
public class FileOutputStream extends OutputStream {
    //平臺相關 fd
    private final FileDescriptor fd;
    //如果 append 爲 true,則輸出流將以 append 方式加入 file 中,否則從 file 的開頭開始寫
    private final boolean append;
    //適配 java nio
    private FileChannel channel;
    //同 inputstream
    private final String path;
    private final Object closeLock = new Object();
    private volatile boolean closed = false;
    
    //
    public FileOutputStream(File file, boolean append) {
        String name = (file != null ? file.getPath() : null);
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkWrite(name);
        }
        if (name == null) {
            throw new NullPointerException();
        }
        if (file.isInvalid()) {
            throw new FileNotFoundException("Invalid file path");
        }
        this.fd = new FileDescriptor();
        fd.attach(this);
        this.append = append;
        this.path = name;
        open(name, append);
    }
    //通過文件名打開文件
    private native void open0(String name, boolean append) throws FileNotFoundException;
    //往文件輸出流中寫入 一個 字節
    private native void write(int b, boolean append) throws IOException;
    //將 b[] 數組的 off-len 寫入輸出流
    private native void writeBytes(byte b[], int off, int len, boolean append) throws IOException;
    //關閉輸出流
    public void close() throws IOException {
        synchronized (closeLock) {
            if (closed) {
                return;
            }
            closed = true;
        }

        if (channel != null) {
            channel.close();
        }

        fd.closeAll(new Closeable() {
            public void close() throws IOException {
               close0();
           }
        });
    }
}

2.3 PipedOutputStream —— 管道輸出流

  • 核心參數:sink,內部依賴輸入流
  • 核心方法:connect(PipedInputStream snk),傳入一個輸入流
public class PipedOutputStream extends OutputStream {
    private PipedInputStream sink;
    //一個 PipedOutputStream,必須對應一個 PipedInputStream
    public synchronized void connect(PipedInputStream snk) throws IOException {
        if (snk == null) {
            throw new NullPointerException();
        } else if (sink != null || snk.connected) {
            throw new IOException("Already connected");
        }
        sink = snk;
        snk.in = -1;
        snk.out = 0;
        snk.connected = true;
    }
    //內部之間將輸出流,輸出到輸入流中
    public void write(int b)  throws IOException {
        if (sink == null) {
            throw new IOException("Pipe not connected");
        }
        sink.receive(b);
    }
    public synchronized void flush() throws IOException {
        if (sink != null) {
            synchronized (sink) {
                sink.notifyAll();//開啓所有輸入流讀取
            }
        }
    }
    public void close()  throws IOException {
        if (sink != null) {
            sink.receivedLast();
        }
    }
}

2.4 SocketOutputStream —— 網絡輸出流

class SocketOutputStream extends FileOutputStream {
    private AbstractPlainSocketImpl impl = null;
    private byte temp[] = new byte[1];
    private Socket socket = null;
    //從 impl 中得到 socket,並通過 impl 打開一個 fd
    SocketOutputStream(AbstractPlainSocketImpl impl) throws IOException {
        super(impl.getFileDescriptor());
        this.impl = impl;
        socket = impl.getSocket();
    }
    //寫字節入 socket 
    private native void socketWrite0(FileDescriptor fd, byte[] b, int off,
                                     int len) throws IOException;
    //關閉 socket
    public void close() throws IOException {
        // Prevent recursion. See BugId 4484411
        if (closing)
            return;
        closing = true;
        if (socket != null) {
            if (!socket.isClosed())
                socket.close();
        } else
            impl.close();
        closing = false;
    }
}

2.5 ObjectOutputStream —— 序列化輸出流

  • 留待以後分析

3 OutputStream 裝飾類源碼分析

3.1 BufferedOutputStream —— 緩衝輸出流

  • 核心參數:
    • buf[]:最爲緩衝區
    • count:緩衝區的已緩存下標
  • 核心方法:
    • flushBuffer:刷空緩衝區
    • write:向緩衝區寫字節
public class BufferedOutputStream extends FilterOutputStream {
    protected byte buf[];
    protected int count;//目前輸出緩衝數組的下標
    public BufferedOutputStream(OutputStream out, int size) {
        super(out);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }
    //將 buf[] 中的字節全部刷入 輸出流
    private void flushBuffer() throws IOException {
        if (count > 0) {
            out.write(buf, 0, count);
            count = 0;
        }
    }
    //如果 buf[] 滿了,將 buf[] 中的字節全部刷入輸出流,並將新的字節加入 buf[]
    public synchronized void write(int b) throws IOException {
        if (count >= buf.length) {
            flushBuffer();
        }
        buf[count++] = (byte)b;
    }
    //先刷空 buf,再刷 實際輸出流
    public synchronized void flush() throws IOException {
        flushBuffer();
        out.flush();
    }
}

3.2 DataOutputStream —— 基本類型序列化

public class DataOutputStream extends FilterOutputStream implements DataOutput {
    //寫入字節的個數,達到Integer.MAX_VALUE 時,將一直保持這個值
    protected int written;
    
    //主要就是序列化這些基本類型爲 byte
    public final void writeBytes(String s) throws IOException {
        int len = s.length();
        for (int i = 0 ; i < len ; i++) {
            out.write((byte)s.charAt(i));
        }
        incCount(len);
    }
}

3.3 GZIPOutputStream

  • 留待以後分析

3.4 ZIPOutputStream

  • 留待以後分析
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章