Java IO筆記(FileInputStream/FileOutputStream)


(最近剛來到簡書平臺,以前在CSDN上寫的一些東西,也在逐漸的移到這兒來怨绣,有些篇幅是很早的時候?qū)懴碌模虼丝赡軙吹揭恍﹥?nèi)容雜亂的文章拷获,對此深感抱歉篮撑,以下為正文)


正文

本篇要學(xué)習(xí)的是Java IO包中的FileInputStream類和FileOoutputStream類。

文件是我們常見的數(shù)據(jù)源之一匆瓜,所以Java為我們封裝好了支持文件讀寫的流工具赢笨,下面我們通過源碼分別來學(xué)習(xí)這兩個類。

FileInputStream.java

package java.io;
 
import java.nio.channels.FileChannel;
import sun.nio.ch.FileChannelImpl;
 
public class FileInputStream extends InputStream
{
    //內(nèi)部聲明了一個FileDescriptor對象的句柄茧妒,用于接收被打的文件的句柄。
    private final FileDescriptor fd;
 
    //內(nèi)部聲明了一個String類型的變量path糠馆,用于存放被打開文件的文件路徑嘶伟。
    private final String path;
 
    //聲明了一個FileChannel對象,初始化為null又碌。該對象后面在NIO的學(xué)習(xí)中會詳細(xì)的提到九昧,可以對文件進(jìn)行操作
    private FileChannel channel = null;
 
    //定義了一個Object對象,為后面進(jìn)行鎖操作時提供對象鎖毕匀。
    private final Object closeLock = new Object();
    //定義了一個booelan型變量铸鹰,closed,來判斷流是否關(guān)閉皂岔,注意的是該變量被volatile關(guān)鍵字修飾蹋笼,保證了數(shù)據(jù)改變時的可見性。
    private volatile boolean closed = false;
 
    /**
     * 一個帶一個參數(shù)的構(gòu)造函數(shù),傳入的參數(shù)為所要打開文件的文件路徑剖毯。創(chuàng)建時進(jìn)行安全監(jiān)測圾笨,檢測傳入?yún)?shù)是否為null,然后調(diào)用下一個構(gòu)造函數(shù)逊谋。
     */
    public FileInputStream(String name) throws FileNotFoundException {
        this(name != null ? new File(name) : null);
    }
 
    /**
     * 一個帶一個參數(shù)的構(gòu)造函數(shù)擂达,傳入的參數(shù)為所要打開文件的file對象。創(chuàng)建是進(jìn)行安全監(jiān)測胶滋,檢測file對象是否為null板鬓。
     */
    public FileInputStream(File file) throws FileNotFoundException {
        String name = (file != null ? file.getPath() : null);
        //獲取當(dāng)前應(yīng)用的安全管理器SecruityManager,并對其是否有讀取權(quán)限進(jìn)行檢測究恤。
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkRead(name);
        }
    //如果傳入文件的路徑為null俭令,拋出NullPointerException。
        if (name == null) {
            throw new NullPointerException();
        }
    //判斷文件是否合法
        if (file.isInvalid()) {
            throw new FileNotFoundException("Invalid file path");
        }
    //創(chuàng)建一個文件描述符部宿,并將本類對象依附到該文件描述符上抄腔,并將全局變量path賦值為傳入文件的路徑,最終通過一個native的open方法打開文件窟赏。這里的attach
    //方法目的時方便最后的close方法調(diào)用時妓柜,可以方便的關(guān)閉所有需要關(guān)閉的資源箱季,這就是我們使用多個包裝流的時候無需一個一個的關(guān)閉所有流的原因涯穷。
        fd = new FileDescriptor();
        fd.attach(this);
        path = name;
        open(name);
    }
 
    /**
     * 一個帶有一個參數(shù)的構(gòu)造方法,傳入的參數(shù)為一個文件描述符藏雏。開始時需要對傳入?yún)?shù)進(jìn)行安全監(jiān)測拷况,如果為null,則拋出NullPointerException掘殴。
     */
    public FileInputStream(FileDescriptor fdObj) {
    //獲得java的安全管理器赚瘦。
        SecurityManager security = System.getSecurityManager();
        if (fdObj == null) {
            throw new NullPointerException();
        }
    //如果成功獲得了安全管理器,則對傳入的文件描述符進(jìn)行權(quán)限檢測奏寨,是否具有讀數(shù)據(jù)的權(quán)限起意。
        if (security != null) {
            security.checkRead(fdObj);
        }
    //將傳入的文件描述符fdObj賦值給全局變量fd,全局變量path賦值為null病瞳,將本類對象依附到文件描述符之上揽咕。
        fd = fdObj;
        path = null;
        fd.attach(this);
    }
 
    /**
     * 一個native關(guān)鍵字修飾的open方法,含有一個String類型的參數(shù)套菜,傳入的參數(shù)為所需打開文件的路徑亲善。關(guān)于native方法因?yàn)闋砍兜絚/c++,以后再開篇幅具體描述逗柴。
     * 此時只需知道該方法用于打開一個文件蛹头。
     */
    private native void open(String name) throws FileNotFoundException;
 
    /**
     * read方法,實(shí)際調(diào)用native方法read0()來實(shí)際讀取文件。
     */
    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;
 
    /**
     * 一個帶參的read方法渣蜗,傳入的參數(shù)為一個byte型數(shù)組屠尊,最終調(diào)用native方法readBytes方法
     */
    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);
    }
 
    /**
     * 一個native關(guān)鍵字修飾的skip方法,傳入的參數(shù)為一個long型數(shù)據(jù)耕拷,方法用于跳過傳入的長度進(jìn)行閱讀知染。
     */
    public native long skip(long n) throws IOException;
 
    /**
     * 該方法返回一個預(yù)估的流中還可以讀取的數(shù)據(jù)長度
     */
    public native int available() throws IOException;
 
    /**
     * 用于關(guān)閉流。
     */
    public void close() throws IOException {
    //加了一個同步鎖斑胜,用來設(shè)置closed狀態(tài)值控淡。
        synchronized (closeLock) {
            if (closed) {
                return;
            }
            closed = true;
        }
    //如果文件管道不為null,調(diào)用相應(yīng)的close方法止潘。
        if (channel != null) {
           channel.close();
        }
    //調(diào)用文件描述符的closeAll方法掺炭,會將所有依附的系統(tǒng)資源全部釋放。
        fd.closeAll(new Closeable() {
            public void close() throws IOException {
               close0();
           }
        });
    }
 
    /**
     * 獲得當(dāng)前流中讀取文件的文件描述符
     */
    public final FileDescriptor getFD() throws IOException {
        if (fd != null) {
            return fd;
        }
        throw new IOException();
    }
 
    /**
     * 獲取當(dāng)前流中所要讀取文件的文件管道凭戴。
     */
    public FileChannel getChannel() {
        synchronized (this) {
            if (channel == null) {
                channel = FileChannelImpl.open(fd, path, true, false, this);
            }
            return channel;
        }
    }
 
    // 用于設(shè)置fd的內(nèi)存地址偏移量涧狮,牽扯到JNI編程,這里不細(xì)說么夫。
    private static native void initIDs();
 
    private native void close0() throws IOException;
 
    static {
        initIDs();
    }
 
    /**
     * 重寫了finalize方法者冤,用于再一次確定資源的關(guān)閉。需要注意的是當(dāng)fd被分享出去后档痪,必須等到所有使用到fd引用的資源都不可達(dá)時涉枫,調(diào)用close方法。
     */
    protected void finalize() throws IOException {
        if ((fd != null) &&  (fd != FileDescriptor.in)) {
            close();
        }
    }
}

FileOutput.java

package java.io;
 
import java.nio.channels.FileChannel;
import sun.nio.ch.FileChannelImpl;
 
public class FileOutputStream extends OutputStream
{
    /**
     * 內(nèi)置了一個文件描述符的句柄腐螟,用于接收打開文件的文件描述符對象愿汰。
     */
    private final FileDescriptor fd;
 
    /**
     * 聲明了一個boolean類型的變量append,該變量值用于控制打開文件寫入時是以追加的形式寫入數(shù)據(jù)還是以覆蓋的形式寫入數(shù)據(jù)乐纸。
     */
    private final boolean append;
 
    /**
     * 內(nèi)置了一個文件管道衬廷,用于接收打開文件的文件管道對象。它的初始化不是最初就初始化的汽绢,只有在需要時(調(diào)用getChannel方法時)才會進(jìn)行初始化吗跋。
     */
    private FileChannel channel;
 
    /**
     * 聲明了一個String類型的變量,用于接收打開文件的路徑名宁昭。
     */
    private final String path;
 
    //定義了一個Object對象跌宛,為后面的同步操作提供同步鎖。
    private final Object closeLock = new Object();
    //定義了一個boolean型變量closed久窟,用來控制當(dāng)前流對象是否關(guān)閉秩冈。用volatile關(guān)鍵修飾后,保證了數(shù)據(jù)改變時的可見性斥扛。
    private volatile boolean closed = false;
 
    /**
     * 一個帶有一個參數(shù)的構(gòu)造函數(shù)入问,傳入的參數(shù)類型為String類型丹锹,為要寫入文件的路徑,內(nèi)部調(diào)用了下面的帶兩個參數(shù)的構(gòu)造方法芬失,默認(rèn)寫人模式是覆蓋的楣黍。
     */
    public FileOutputStream(String name) throws FileNotFoundException {
        this(name != null ? new File(name) : null, false);
    }
 
    /**
     * 一個帶有兩個參數(shù)的構(gòu)造函數(shù),第一個參數(shù)為String類型棱烂,為要寫入文件的路徑租漂,第二個參數(shù)為一個boolean型變量,用于控制文件寫入時的方式颊糜。內(nèi)部調(diào)用另一個構(gòu)造
     * 函數(shù)哩治。
     */
    public FileOutputStream(String name, boolean append)
        throws FileNotFoundException
    {
        this(name != null ? new File(name) : null, append);
    }
 
    /**
     * 一個帶有一個參數(shù)的構(gòu)造函數(shù),傳入的參數(shù)類型為一個File對象衬鱼,內(nèi)部繼續(xù)調(diào)用其它的構(gòu)造函數(shù)业筏,默認(rèn)寫入方式是覆蓋的。
     */
    public FileOutputStream(File file) throws FileNotFoundException {
        this(file, false);
    }
 
    /**
     * 帶兩個參數(shù)的構(gòu)造函數(shù)鸟赫,其它的構(gòu)造函數(shù)最終都是調(diào)用該構(gòu)造函數(shù)蒜胖,第一個參數(shù)為一個File類型,表示要打開的文件抛蚤,第二個參數(shù)是一個boolean型變量台谢,用于控制寫
     * 入時是覆蓋還是追加。
     */
    public FileOutputStream(File file, boolean append)
        throws FileNotFoundException
    {
    //獲取打開文件的路徑岁经。
        String name = (file != null ? file.getPath() : null);
    //獲取java安全管理器朋沮,對當(dāng)前是否具有寫入的權(quán)限進(jìn)行檢查。
        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");
        }
    //將該流與文件描述符相依附,為后面進(jìn)行close時提供便捷诉位。
        this.fd = new FileDescriptor();
        fd.attach(this);
        this.append = append;
        this.path = name;
    //調(diào)用native方法open,打開需要就行寫入的文件菜枷。
        open(name, append);
    }
 
    /**
     * 一個帶一個參數(shù)的構(gòu)造函數(shù)苍糠,參數(shù)類型是文件描述符類型,具體過程同上啤誊,此處不再細(xì)說岳瞭。
     */
    public FileOutputStream(FileDescriptor fdObj) {
        SecurityManager security = System.getSecurityManager();
        if (fdObj == null) {
            throw new NullPointerException();
        }
        if (security != null) {
            security.checkWrite(fdObj);
        }
        this.fd = fdObj;
        this.append = false;
        this.path = null;
 
        fd.attach(this);
    }
 
    /**
     * 用于打開指定文件,第一個String類型參數(shù)為需要打開文件的路徑蚊锹,第二個boolean類型參數(shù)為打開模式瞳筏,是追加還是覆蓋。
     */
    private native void open(String name, boolean append)
        throws FileNotFoundException;
 
    /**
     * 用于向打開文件中寫入數(shù)據(jù)牡昆,第一個參數(shù)是一個int型變量姚炕,為要寫入的數(shù)據(jù),第二個boolean型參數(shù)為寫入時是追加還是覆蓋。
     */
    private native void write(int b, boolean append) throws IOException;
 
    /**
     * 向文件中寫入數(shù)據(jù)柱宦,內(nèi)部調(diào)用上面的native方法進(jìn)行寫入些椒。
     */
    public void write(int b) throws IOException {
        write(b, append);
    }
 
    /**
     * 向文件中寫入數(shù)據(jù),不過不是一次寫入一個字節(jié)掸刊,而是通過一個byte數(shù)組作為緩存免糕,一次寫入一批數(shù)據(jù)。
     */
    private native void writeBytes(byte b[], int off, int len, boolean append)
        throws IOException;
 
    /**
     * 向文件中寫入數(shù)據(jù)忧侧,將傳入的byte數(shù)組中的內(nèi)容全部寫入至文件中石窑。
     */
    public void write(byte b[]) throws IOException {
        writeBytes(b, 0, b.length, append);
    }
 
    /**
     * 向文件中寫入數(shù)據(jù),傳入的三個參數(shù)分別是寫入的數(shù)據(jù)源一個byte數(shù)組蚓炬,后兩個參數(shù)為寫入數(shù)據(jù)的起點(diǎn)以及寫入數(shù)據(jù)的長度尼斧。
     */
    public void write(byte b[], int off, int len) throws IOException {
        writeBytes(b, off, len, append);
    }
 
    /**
     * 該方法用于關(guān)閉流及與其相關(guān)聯(lián)的所有的系統(tǒng)資源。
     */
    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 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, false, true, append, this);
            }
            return channel;
        }
    }
 
    /**
     * 重寫了finalize方法,調(diào)用close方法保證流與其關(guān)聯(lián)的資源能夠獲得釋放熄捍。如果是標(biāo)準(zhǔn)輸出流或者錯誤輸出流烛恤,還會調(diào)用flush方法。
     */
    protected void finalize() throws IOException {
        if (fd != null) {
            if (fd == FileDescriptor.out || fd == FileDescriptor.err) {
                flush();
            } else {
                close();
            }
        }
    }
 
    private native void close0() throws IOException;
 
    private static native void initIDs();
 
    static {
        initIDs();
    }
 
}

上面附上了兩個類的源碼余耽,并附上了一些簡單的注釋缚柏。下面我們就用具體的例子來加深我們對這兩個類的理解。
下面的例子實(shí)現(xiàn)了簡單的文件復(fù)制:


package FileInputOutput;
 
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
 
public class FileIOTest {
    public static void main(String[] args) {
        byte[] buffer = new byte[1024];
        try (FileInputStream fis = new FileInputStream("./src/file/test.txt");
                FileOutputStream fos = new FileOutputStream(
                        "./src/file/testcopy.txt")) {
            while (fis.read(buffer) != -1) {
                fos.write(buffer);
            }
            System.out.println("復(fù)制完成");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

執(zhí)行上述代碼后可以看到在指定目錄下復(fù)制了一份test.txt文件碟贾,文件中內(nèi)容也被完整復(fù)制過來币喧。


執(zhí)行效果

以上為本篇的全部內(nèi)容。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末袱耽,一起剝皮案震驚了整個濱河市杀餐,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌朱巨,老刑警劉巖史翘,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異冀续,居然都是意外死亡琼讽,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進(jìn)店門洪唐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钻蹬,“玉大人,你說我怎么就攤上這事凭需∥是罚” “怎么了肝匆?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長溅潜。 經(jīng)常有香客問我术唬,道長,這世上最難降的妖魔是什么滚澜? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任粗仓,我火速辦了婚禮,結(jié)果婚禮上设捐,老公的妹妹穿的比我還像新娘借浊。我一直安慰自己,他們只是感情好萝招,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布蚂斤。 她就那樣靜靜地躺著,像睡著了一般槐沼。 火紅的嫁衣襯著肌膚如雪曙蒸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天岗钩,我揣著相機(jī)與錄音纽窟,去河邊找鬼。 笑死兼吓,一個胖子當(dāng)著我的面吹牛臂港,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播视搏,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼审孽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了浑娜?” 一聲冷哼從身側(cè)響起佑力,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎棚愤,沒想到半個月后搓萧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宛畦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了揍移。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片次和。...
    茶點(diǎn)故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖那伐,靈堂內(nèi)的尸體忽然破棺而出踏施,到底是詐尸還是另有隱情石蔗,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布畅形,位于F島的核電站养距,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏日熬。R本人自食惡果不足惜棍厌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望竖席。 院中可真熱鬧耘纱,春花似錦、人聲如沸毕荐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽憎亚。三九已至员寇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間第美,已是汗流浹背蝶锋。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留斋日,地道東北人牲览。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像恶守,于是被迫代替她去往敵國和親第献。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 一. Java基礎(chǔ)部分.................................................
    wy_sure閱讀 3,811評論 0 11
  • (最近剛來到簡書平臺兔港,以前在CSDN上寫的一些東西庸毫,也在逐漸的移到這兒來,有些篇幅是很早的時候?qū)懴碌纳婪虼丝赡軙?..
    moonfish1994閱讀 239評論 0 1
  • 1.0 為什么要寫這個飒赃。 在當(dāng)初學(xué)java語言的時候,其實(shí)感覺這算是最難的基礎(chǔ)部分內(nèi)容之一科侈,因?yàn)樽址骱妥止?jié)流的存...
    bobokaka閱讀 589評論 0 5
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,101評論 1 32
  • 第01章 JAVA簡介第02章 基礎(chǔ)語法第02章 遞歸補(bǔ)充第03章 面向?qū)ο蟮?4章 異常處理第05章 數(shù)組第06...
    順毛閱讀 514評論 0 1