jdk1.8 java.io.FileInputStream類源碼閱讀

介紹

jdk解釋:
public class FileInputStream
extends InputStream
A FileInputStream從文件系統中的文件獲取輸入字節。 什麼文件可用取決於主機環境。
FileInputStream用於讀取諸如圖像數據的原始字節流。 要閱讀字符串,請考慮使用FileReader 。

屬性

    /* File Descriptor - handle to the open file */
    private final FileDescriptor fd;

    /**
     * The path of the referenced file
     * (null if the stream is created with a file descriptor)
     */
    private final String path;

    private FileChannel channel = null;

    private final Object closeLock = new Object();
    private volatile boolean closed = false;

值得注意的是private final FileDescriptor fd;
FileDescriptor類jdk 1.8解釋:
文件描述符類的實例用作表示打開文件,開放套接字或其他字節源或信宿的底層機器特定結構的不透明句柄。 文件描述符的主要實際用途是創建一個FileInputStream或FileOutputStream來包含它。

FileDescriptor詳解http://wangkuiwu.github.io/2012/05/09/FileDescriptor/

構造方法

 public FileInputStream(String name) throws FileNotFoundException {
        this(name != null ? new File(name) : null);
    }
 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 FileInputStream(FileDescriptor fdObj) {
        SecurityManager security = System.getSecurityManager();
        if (fdObj == null) {
            throw new NullPointerException();
        }
        if (security != null) {
            security.checkRead(fdObj);
        }
        fd = fdObj;
        path = null;

        /*
         * FileDescriptor is being shared by streams.
         * Register this stream with FileDescriptor tracker.
         */
        fd.attach(this);
    }

先看jdk1.8的解釋
在這裏插入圖片描述
這三個構造方法的實現,終歸還是迴歸到private final FileDescriptor fd;屬性上.
public FileInputStream(File file) throws FileNotFoundException:這個構造方法的實現,首先判斷傳入的參數是否合法,然後在爲屬性
fd = new FileDescriptor();fd.attach(this);值得注意的是fd.attach(this);源碼查看attach方法

    synchronized void attach(Closeable c) {
        if (parent == null) {
            // first caller gets to do this
            parent = c;
        } else if (otherParents == null) {
            otherParents = new ArrayList<>();
            otherParents.add(parent);
            otherParents.add(c);
        } else {
            otherParents.add(c);
        }
    }

注意傳入的參數是Closeable,而我們傳入的是FileInputStream,這是因爲FileInputStream繼承了InputStream,InputStream實現了Closeable接口;
最後open(name);是FileInputStream類的一個本地方法
private native void open0(String name) throws FileNotFoundException:打開指定的文件進行讀取。

同理下面的構造方法 public FileInputStream(FileDescriptor fdObj) 也是一樣.

@Test

    @Test
    public void toPath() throws FileNotFoundException {
        File file = new File("F:\\FileStudy\\test2.txt");//存在的文件
        File file1 = new File("F:\\FileStudy\\test2.t1xt");//不存在存在的文件
        File file2 = new File("F:\\FileStudy");//存在的目錄
        File file3 = new File("F:\\FileStud");//不存在存在的文件

        FileInputStream fileInputStream =new FileInputStream(file);//1
        FileInputStream fileInputStream1 =new FileInputStream(file1);//2
        FileInputStream fileInputStream2 =new FileInputStream(file2);//3
        FileInputStream fileInputStream3 =new FileInputStream(file3);//4
        
    }
    輸出結果:
    除了第一個能成功
    2會返回:java.io.FileNotFoundException: F:\FileStudy\test2.t1xt (系統找不到指定的文件。)
    3會返回:java.io.FileNotFoundException: F:\FileStudy (拒絕訪問。)
    4會返回:java.io.FileNotFoundException: F:\FileStud (系統找不到指定的文件。)

由測試可以知,如果該文件是目錄,則創建不成功,拋出異常.

方法

(1) read()方法類:

public int read() throws IOException {
        return read0();
    }
private native int read0() throws IOException;
private native int readBytes(byte b[], int off, int len) throws IOException;
public int read(byte b[]) throws IOException {
        return readBytes(b, 0, b.length);
    }
public int read(byte b[], int off, int len) throws IOException {
        return readBytes(b, off, len);
    }

這幾類方法都是對本地方法進行重載
@Test

    @Test
    public void read() throws IOException {
        File file = new File("F:\\FileStudy\\test2.txt");//存在的文件
        FileInputStream fileInputStream =new FileInputStream(file);
        System.out.println(fileInputStream.read());
        while (fileInputStream.read()>1){
            System.out.println(fileInputStream.read());
        }
        byte[] bytes =new byte[4];
        System.out.println("--------------------------------------");
        System.out.println(fileInputStream.read(bytes));
        System.out.println(fileInputStream.read(bytes,1,1));
    }
    
輸出結果:
183
183
181
203
183
181
-1
--------------------------------------
-1
-1

(2)

public native long skip(long n) throws IOException;

也是本地方法
public long skip(long n) throws IOException
跳過並從輸入流中丟棄n個字節的數據。
由於各種原因, skip方法可能會跳過一些較小數量的字節,可能是0 。 如果n爲負,則該方法將嘗試向後跳。 如果後臺文件不支持其當前位置的向後跳過,則會拋出IOException 。 返回實際跳過的字節數。 如果它向前跳,它返回一個正值。 如果它向後跳,它返回一個負值。

該方法可能會跳過比後備文件中剩餘的字節更多的字節。 這不會產生異常,並且跳過的字節數可能包括超出後臺文件的EOF的一些字節數。 嘗試在跳過結束後從流中讀取將導致-1表示文件的結尾。

重寫:
skip在 InputStream
參數
n - 要跳過的字節數。
結果
實際跳過的字節數。
異常
IOException - 如果n爲負,如果流不支持查詢,或者發生I / O錯誤。

(3)

public native int available() throws IOException;

本地方法
public int available() throws IOException
返回從此輸入流中可以讀取(或跳過)的剩餘字節數的估計值,而不會被下一次調用此輸入流的方法阻塞。 當文件位置超出EOF時返回0。 下一個調用可能是同一個線程或另一個線程。 這個多個字節的單個讀取或跳過將不會被阻塞,但可以讀取或跳過較少的字節。
在某些情況下,非阻塞讀取(或跳過)在緩慢時可能會被阻止,例如在慢速網絡中讀取大文件時。
重寫:
available在 InputStream
結果
可以從該輸入流中讀取(或跳過)而不阻塞的剩餘字節數的估計。
異常
IOException - 如果此文件輸入流已通過調用 close關閉或發生I / O錯誤。
@Test

   @Test
    public void available() throws IOException {
        File file = new File("F:\\FileStudy\\test2.txt");//存在的文件
        FileInputStream fileInputStream =new FileInputStream(file);
        while (fileInputStream.read()>1){
            System.out.println(fileInputStream.available());
        }
    }
輸出結果:
11
10
9
8
7
6
5
4
3
2
1
0

可以看出read()方法是一個字節讀取;

(4) public void close() 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();
           }
        });
    }

public void close() throws IOException
關閉此文件輸入流並釋放與流相關聯的任何系統資源。
如果該流具有相關聯的信道,則該信道也被關閉。
Specified by:
close在接口 Closeable
Specified by:
close在界面 AutoCloseable
重寫:
close在 InputStream類
異常
IOException - 如果發生I / O錯誤。

看源碼分析可知,先判斷屬性closed是否爲true,爲true直接返回
然後在判斷channel屬性是否爲空,不爲空調用channel.close();
最後利用FileDescriptor裏面的方法closeAll,重寫Closeable類裏面的方法,最後掉成了FileInputStream中的本地方法close0();

(5)

    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;
        }
    }

該方法是獲取屬性的get方法,返回屬性實例.
值得注意的是public FileChannel getChannel(),
FileChannel類基本使用:https://blog.csdn.net/KingBoyWorld/article/details/72417461
FileChannel類Jdk1.8文檔解釋:http://www.matools.com/api/java8

總結

未完待續

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