Java FileWriter中flush()和close()的區別
1. 概述
文件處理是我們經常遇到的一個重要方面。當涉及到向檔案寫入資料時,通常會使用FileWriter
類別。在此類中,兩個重要的方法, flush()
和close(),
在管理檔案輸出流方面發揮不同的作用。
在本教程中,我們將介紹FileWriter
的常見用法,並深入研究其flush()
和close()
方法之間的差異。
2. FileWriter
和**try-with-resources
**
Java 中的try-with-resources
語句是一種強大的資源管理機制,尤其是在處理檔案處理等 I/O 作業時。一旦程式碼區塊退出,無論是正常退出還是由於異常退出,它都會自動關閉其聲明中指定的資源。
但是,在某些情況下,將FileWriter
與try-with-resources
一起使用可能並不理想或不必要。
無論是否try-with-resources.
FileWriter
行為可能會有所不同。接下來,我們就來詳細了解。
2.1.將FileWriter
與try-with-resources
一起使用
如果我們將FileWriter
與try-with-resources
一起使用,則當我們退出try-with-resources
區塊時, FileWriter
物件會自動刷新並關閉。
接下來,讓我們在單元測試中展示這一點:
@Test
void whenUsingFileWriterWithTryWithResources_thenAutoClosed(@TempDir Path tmpDir) throws IOException {
Path filePath = tmpDir.resolve("auto-close.txt");
File file = filePath.toFile();
try (FileWriter fw = new FileWriter(file)) {
fw.write("Catch Me If You Can");
}
List<String> lines = Files.readAllLines(filePath);
assertEquals(List.of("Catch Me If You Can"), lines);
}
由於我們的測試將寫入和讀取文件,因此我們使用了 JUnit5 的臨時目錄擴充 ( @TempDir
)。透過這個擴展,我們可以專注於測試核心邏輯,而無需手動建立和管理用於測試目的的臨時目錄和檔案。
如測試方法所示,我們在try-with-resources
區塊中寫入一個字串。然後,當我們使用Files.readAllLines(),
我們得到了預期的結果。
2.2.使用FileWriter
而不使用try-with-resources
但是,當我們使用不含 try-with-resources 的FileWriter
時try-with-resources,
FileWriter
物件不會自動刷新和關閉:
@Test
void whenUsingFileWriterWithoutFlush_thenDataWontBeWritten(@TempDir Path tmpDir) throws IOException {
Path filePath = tmpDir.resolve("noFlush.txt");
File file = filePath.toFile();
FileWriter fw = new FileWriter(file);
fw.write("Catch Me If You Can");
List<String> lines = Files.readAllLines(filePath);
assertEquals(0, lines.size());
fw.close(); //close the resource
}
如同上面的測試所示,雖然我們透過呼叫FileWriter.write(),
檔案仍然是空的。
接下來我們就來看看如何解決這個問題。
3. FileWriter.flush()
和FileWriter.close()
在本節中,我們首先解決“文件仍然為空”的問題。然後,我們將討論FileWriter.flush()
和FileWriter.close().
3.1.解決“文件仍為空”問題
首先,讓我們快速了解為什麼呼叫FileWriter.write().
當我們呼叫FileWriter.write(),
資料不會立即寫入磁碟上的檔案。相反,它被暫時儲存在緩衝區中。因此,為了視覺化文件中的數據,需要將緩衝的數據刷新到文件中。
最簡單的方法是呼叫flush()
方法:
@Test
void whenUsingFileWriterWithFlush_thenGetExpectedResult(@TempDir Path tmpDir) throws IOException {
Path filePath = tmpDir.resolve("flush1.txt");
File file = filePath.toFile();
FileWriter fw = new FileWriter(file);
fw.write("Catch Me If You Can");
fw.flush();
List<String> lines = Files.readAllLines(filePath);
assertEquals(List.of("Catch Me If You Can"), lines);
fw.close(); //close the resource
}
可以看到,在呼叫flush(),
我們可以透過讀取檔案來取得預期的資料。
或者,我們可以呼叫close()
方法將緩衝的資料傳輸到檔案。這是因為**close()
先執行刷新,然後關閉檔案流寫入器。**
接下來,讓我們建立一個測試來驗證這一點:
@Test
void whenUsingFileWriterWithClose_thenGetExpectedResult(@TempDir Path tmpDir) throws IOException {
Path filePath = tmpDir.resolve("close1.txt");
File file = filePath.toFile();
FileWriter fw = new FileWriter(file);
fw.write("Catch Me If You Can");
fw.close();
List<String> lines = Files.readAllLines(filePath);
assertEquals(List.of("Catch Me If You Can"), lines);
}
因此,它看起來與flush()
呼叫非常相似。但是,這兩種方法在處理文件輸出流時有不同的用途。
接下來我們就來仔細看看他們的差別。
3.2. flush()
和close()
方法之間的區別
flush()
方法主要用於強制立即寫入任何緩衝的資料而不關閉FileWriter
,
而close()
方法既執行刷新又釋放相關資源。
換句話說,呼叫flush()
可確保緩衝的資料立即寫入磁碟,從而允許在不關閉流的情況下繼續對檔案進行寫入或追加操作。相反,當調用close()
時,它將現有的緩衝資料寫入文件,然後關閉它。因此,除非開啟新的流(例如透過初始化新的FileWriter
物件),否則無法將更多資料寫入檔案。
接下來,讓我們透過一些例子來理解這一點:
@Test
void whenUsingFileWriterWithFlushMultiTimes_thenGetExpectedResult(@TempDir Path tmpDir) throws IOException {
List<String> lines = List.of("Catch Me If You Can", "A Man Called Otto", "Saving Private Ryan");
Path filePath = tmpDir.resolve("flush2.txt");
File file = filePath.toFile();
FileWriter fw = new FileWriter(file);
for (String line : lines) {
fw.write(line + System.lineSeparator());
fw.flush();
}
List<String> linesInFile = Files.readAllLines(filePath);
assertEquals(lines, linesInFile);
fw.close(); //close the resource
}
在上面的範例中,我們呼叫了三次write()
以將三行寫入檔案。在每次write()
呼叫之後,我們都呼叫**flush()**
。最後,我們可以從目標檔案中讀取這三行。
但是,如果我們在呼叫FileWriter.close(), IOException
並顯示錯誤訊息「Stream close」 :
@Test
void whenUsingFileWriterWithCloseMultiTimes_thenGetIOExpectedException(@TempDir Path tmpDir) throws IOException {
List<String> lines = List.of("Catch Me If You Can", "A Man Called Otto", "Saving Private Ryan");
Path filePath = tmpDir.resolve("close2.txt");
File file = filePath.toFile();
FileWriter fw = new FileWriter(file);
//write and close
fw.write(lines.get(0) + System.lineSeparator());
fw.close();
//writing again throws IOException
Throwable throwable = assertThrows(IOException.class, () -> fw.write(lines.get(1)));
assertEquals("Stream closed", throwable.getMessage());
}
4。結論
在本文中,我們探討了FileWriter
的常見用法。此外,我們也討論了FileWriter
的flush()
和close()
方法之間的差異。
與往常一樣,範例的完整原始程式碼可在 GitHub 上取得。