IO-輸出流及wirte()方法的理解誤區

簡介

在 Java 的 IO 輸出流中,有三個重載的 write() 方法,它們用於向輸出流所對應的文件中寫入內容,但是如果沒有真正理解輸出流及 write() 方法的原理,很容易混淆文件寫入時到底是 覆蓋 還是 追加

構造器

這裏以 FileOutputStream ,即 文件字節輸出流 爲例

1、FileOutputStream(File file)

通過一個 File 對象來構建一個 FileOutputStream 對象,默認新寫入的內容會 覆蓋 文件中舊的內容,但是這裏的 覆蓋 只針對於 輸出流關閉 之後

2、FileOutputStream(File file, boolean append)

通過一個 File 對象來構建一個 FileOutputStream 對象,第二個參數指定爲 true,表示新寫入的內容會 追加到 文件中舊的內容之後;第二個參數指定爲 false,表示新寫入的內容會 覆蓋 文件中舊的內容,這裏的 追加 或者 覆蓋 都只針對於 輸出流關閉之後

FileOutputStream(String path)FileOutputStream(String path, boolean append)FileOutputStream(File file)、FileOutputStream(File file, boolean append) 的情況是類似的,只是構建 FileOutputStream 對象的方式不同,一種是用 File 對象構建,另一種是用 完整路徑(字符串) 構建

write() 方法

write() 寫入時是覆蓋還是追加?

這裏以 write(byte[] buff),即帶一個參數的方法來舉例說明,另外兩個重載方法在寫入時是覆蓋還是追加的情況都是一樣的

/**
     * 寫入字節數組到輸出流對應的文件中
 */
@Test
public void writeFile02(){
	String filePath = "d:/java-io/a.txt";
	FileOutputStream fileOutputStream = null;
	byte[] buff = null;
	try {
		// 根據完整路徑構建一個 FileOutputStream 對象
		// 若 文件a.txt 不存在,則會自動創建
		// 若 目錄java-io 不存在,拋出 FileNotFoundException
		fileOutputStream = new FileOutputStream(filePath);
		buff = "hello,world".getBytes(); // str.getBytes(() 可以將 字符串 -> 字節數組
		// 將當前字節數組 buff 寫入到 輸出流對應的文件中
		// 注意:這裏調用了兩次 write(buff) 方法,而構造 fileOutputStream 時並沒有指定第二個參數爲 true,即默認 寫入覆蓋
		// 但是結果卻是 a.txt 中有 兩個 hello,world
		// 按常理來理解:a.txt 中應該只有一個 hello,world 因爲寫入了兩次,第二次應該會覆蓋第一次
		// 這是因爲 調用兩次 write(buff) 方法時,此時的 fileOutputStream 都還沒有 關閉,所以會寫入兩次 hello,world
		// 第二次寫入會 追加,而不是 覆蓋
		// 那麼 構造 fileOutputStream 時 指定不指定第二個參數爲 true 還有沒有意義呢?
		// 有意義:如果構造 fileOutputStream 時指定了第二個參數爲 true
		// 那麼即使流關閉了,新寫入的內容會 追加到 舊的內容之後

		// 比如:如果這裏構造 fileOutputStream 時 沒有指定 第二個參數爲 true
		// 那麼 第一次 執行該方法時,會在 a.txt 中寫入 兩個 hello,world
		// 後面 每次執行 都會寫入兩個 hello,world,但每次都會覆蓋,最後 a.txt 中只有兩個 hello,world

		// 如果這裏構造 fileOutputStream 時 指定了 第二個參數爲 true
		// 那麼 第一次 執行該方法時,會在 a.txt 中寫入 兩個 hello,world
		// 後面 每次執行 都會寫入兩個 hello,world,這時每次都會追加,最後 a.txt 中有 執行次數*2 個 hello,world
		fileOutputStream.write(buff);
		fileOutputStream.write(buff);
	} catch (IOException e) {
		e.printStackTrace();
	} finally {
		try {
			fileOutputStream.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

總結:若在構造 FileOutputStream 對象的時候,指定了 追加,那麼 輸出流關閉之後,每次的寫入都會 追加;反之如果沒有指定 追加,即默認 覆蓋,那麼 輸出流關閉之後,每次的寫入都會 覆蓋。如果輸出流 沒有關閉(即在一次寫入時調用多次 write() 方法),則每次調用 wirte() 都是 追加 而不是 覆蓋


write(byte[] buff) vs write(byte[] buff, int off, int len)

write(byte[] buff)write(byte[] buff, int off, int len) 底層都是調用的 writeBytes() 方法,只是傳入的參數不同

  1. write(byte[] buff) -> writeBytes(buff, 0, buff.length):偏移量從 0(固定) 開始,寫入的內容長度爲 buff.length(固定)
  1. write(byte[] buff, int off, int len) -> writeBytes(buff, off, len):偏移量從 off(指定) 開始,寫入的內容長度爲 len(指定)

文件拷貝案例

/**
 * 利用 FileInputStream-FileOutprtStream拷貝二進制文件
 */
public class FileCopyMain {
    public static void main(String[] args) {
        // 將 "d:/waifu.png" 拷貝到 "d:/java-io/waifu.png"
        // 1. 創建文件輸入流,將文件讀入到內存
        // 2. 創建文件輸出流,將內存中讀取到的文件數據,寫入到指定的文件中
        // 3. 在拷貝過程中,應該是讀取部分數據,就寫入到指定文件(利用循環)
        // 而不是一次性讀完再寫入(防止文件內容過大而內存不足)
        // 邊讀邊寫同時保證了在讀寫過程中,輸出流沒有關閉,因此循環每次寫入時都會追加而不是覆蓋
        String srcPath = "d:/waifu.png"; // 文件輸入流路徑
        String destPath = "d:/java-io/waifu.png"; // 文件輸出流路徑
        FileInputStream fileInputStream = null; // 文件輸入流
        FileOutputStream fileOutputStream = null; // 文件輸出流
        try {
            fileInputStream = new FileInputStream(srcPath);
            fileOutputStream = new FileOutputStream(destPath);
            byte[] buff = new byte[1024];
            int readLen = 0;
            while ((readLen = fileInputStream.read(buff)) != -1){
                // 注意:這裏要使用 write(buff, off, len)
                // 並且指定的寫入的長度爲 readLen,readLen 即爲真正讀取到的字節數
                // 假如 waifu.png 的大小爲 1025 個字節
                // 第一次寫入 讀取到的 [0-1024],write(buff)、write(buff, off, len) 都沒問題
                // 但是第二次寫入時,讀取到的是 [0-1] -> 1個字節
                // write(buff) -> writeBytes(buff, 0, buff.length) -> writeBytes(buff, 0, 1024) -> error
                // write(buff, 0, 1) -> writeBytes(buff, off, len) -> writeBytes(buff, 0, 1) -> ok
                fileOutputStream.write(buff, 0, readLen);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fileInputStream != null){
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fileOutputStream != null){
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章