1. 程式人生 > >Java中IO框架——FileInputStream原始碼解析

Java中IO框架——FileInputStream原始碼解析

屬性

    // 檔案描述類,處理開啟的檔案
    private final FileDescriptor fd;
    // 檔案的路徑,如果該流是通過檔案描述類建立的,該屬性則為空
    private final String path;
    // 用於讀、寫、對映、操作檔案的通道
    private FileChannel channel = null;
    // 一個關閉鎖,只在close()方法中使用,確保多執行緒同步呼叫,同時和其他同步方法不衝突(因為這個關閉鎖是自己的物件鎖)
    private final Object closeLock = new Object();
    // 流是否是關閉的,volatile保證多執行緒的可見性
private volatile boolean closed = false;

建構函式

    // 檔案路徑建立File物件,並呼叫下邊的過載的建構函式
    public FileInputStream(String name) throws FileNotFoundException {
        this(name != null ? new File(name) : null);
    }

    // 根據File物件來構造檔案輸入流
    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) { // 檔案路徑為null,丟擲空指標異常
throw new NullPointerException(); } if (file.isInvalid()) { // 如果file物件無效,丟擲未找到檔案異常 throw new FileNotFoundException("Invalid file path"); } // 建立檔案描述符 fd = new FileDescriptor(); // 在檔案描述符中儲存該物件引用 fd.attach(this); // 路徑名賦值path path = name; // 開啟該路徑進行讀取 open(name); } // 呼叫open0(name)本地方法 private void open(String name) throws FileNotFoundException { open0(name); } // 本地方法,開啟指定路徑的檔案進行讀取 private native void open0(String name) throws FileNotFoundException; // 根據檔案描述符構造輸入流 public FileInputStream(FileDescriptor fdObj) { SecurityManager security = System.getSecurityManager(); if (fdObj == null) { throw new NullPointerException(); } if (security != null) { security.checkRead(fdObj); } // 檔案描述符物件屬性的賦值 fd = fdObj; path = null; // 在檔案描述符中儲存該物件引用 fd.attach(this); }

重要方法

read()

    // 從輸入流中讀取下一個位元組的資料,呼叫本地方法
    // 該方法一直阻塞直到有可用的資料
    public int read() throws IOException {
        return read0();
    }

    // 本地方法,讀取下一個位元組的資料
    private native int read0() throws IOException;

帶引數read

可以參考 InputStream 的類似的 read 方法

    // 讀取b.length數量的位元組,並從位元組陣列b的0下標開始填滿b陣列
    public int read(byte b[]) throws IOException {
        return readBytes(b, 0, b.length);
    }
    // 讀取b.length數量的位元組,並從位元組陣列b的off下標位置開始填滿b陣列
    public int read(byte b[], int off, int len) throws IOException {
        return readBytes(b, off, len);
    }
    // 本地方法
    private native int readBytes(byte b[], int off, int len) throws IOException;

其他本地方法

    // 本地方法,跳過n個位元組
    public native long skip(long n) throws IOException;
    // 本地方法,返回可讀的位元組數
    public native int available() throws IOException;

close()

    public void close() throws IOException {
        // 使用物件鎖
        synchronized (closeLock) {
            // 如果流已經關閉則返回
            if (closed) {
                return;
            }
            // 否則將關閉狀態設為true
            closed = true;
        }
        // 操作檔案的通道不為空,也要進行關閉
        if (channel != null) {
           channel.close();
        }
        // 檔案描述符相關資源的關閉,並呼叫本地關閉方法關閉流
        fd.closeAll(new Closeable() {
            public void close() throws IOException {
               close0();
           }
        });
    }

    private native void close0() throws IOException;

finalize()

    protected void finalize() throws IOException {
        if ((fd != null) &&  (fd != FileDescriptor.in)) {
            // 如果fd正在被其他流使用,就不能進行關閉,只有當所有流都不再引用這個檔案描述符才關閉
            close();
        }
    }

其他方法

    // 獲取流物件對應的檔案描述符
    public final FileDescriptor getFD() throws IOException {
        if (fd != null) {
            return fd;
        }
        throw new IOException();
    }
    // 獲取流物件對應的通道,如果為空就建立一個新的通道
    public FileChannel getChannel() {
        synchronized (this) {
            if (channel == null) {
                channel = FileChannelImpl.open(fd, path, true, false, this);
            }
            return channel;
        }
    }

    private static native void initIDs();
    // 這個靜態塊在類載入時呼叫
    // 設定類中屬性的記憶體地址偏移量,便於在必要時操作記憶體給它賦值
    static {
        initIDs();
    }