1. Overview
1.概述
File handling is an essential aspect we frequently encounter. When it comes to writing data to files, the FileWriter class is commonly used. Within this class, two important methods, flush() and close(), play distinct roles in managing file output streams.
文件处理是我们经常遇到的一个重要方面。在向文件写入数据时,通常会用到 FileWriter 类。在该类中,flush() 和 close() 这两个重要方法在管理文件输出流方面发挥着不同的作用。
In this tutorial, we’ll address FileWriter‘s common usage and delve into the differences between its flush() and close() methods.
在本教程中,我们将讨论 FileWriter 的常用用法,并深入探讨 flush() 和 close() 方法之间的区别。
2. FileWriter and try-with-resources
2.FileWriter 和 try-with-resources
The try-with-resources statement in Java is a powerful mechanism for managing resources, especially when dealing with I/O operations such as file handling. It automatically closes the resources specified in its declaration once the code block is exited, whether normally or due to an exception.
Java 中的 try-with-resources 语句是一种强大的资源管理机制,尤其是在处理文件处理等 I/O 操作时。一旦代码块退出,无论是正常退出还是由于异常退出,它都会自动关闭声明中指定的资源。
However, there might be situations where using FileWriter with try-with-resources is not ideal or necessary.
但是,在某些情况下,使用 FileWriter 和 try-with-resources 并不理想,也没有必要。
FileWriter may behave differently with or without try-with-resources. Next, let’s take a closer look at it.
FileWriter在使用或不使用 try-with-resources 时可能会有不同的表现。接下来,让我们仔细看看它。
2.1. Using FileWriter With try-with-resources
2.1.使用 FileWriter 和 try-with-resources
If we use FileWriter with try-with-resources, the FileWriter object gets flushed and closed automatically when we exit the try-with-resources block.
如果我们将 FileWriter 与 try-with-resources 一起使用,当我们退出 try-with-resources 块时,FileWriter 对象将被自动刷新和关闭。
Next, let’s show this in a unit test:
接下来,让我们在单元测试中演示一下:
@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);
}
Since our test will write and read files, we employed JUnit5’s temporary directory extension (@TempDir). With this extension, we can concentrate on testing the core logic without manually creating and managing temporary directories and files for testing purposes.
由于我们的测试将写入和读取文件,因此我们使用了JUnit5的临时目录扩展(@TempDir)。有了这个扩展,我们就可以专注于测试核心逻辑,而无需为测试目的手动创建和管理临时目录和文件。
As the test method shows, we write a string in the try-with-resources block. Then, when we check the file content using Files.readAllLines(), we get the expected result.
如测试方法所示,我们在 try-with-resources 块中写入了一个字符串。然后,当我们使用 Files.readAllLines() 检查文件内容时,我们得到了预期的结果。
2.2. Using FileWriter Without try-with-resources
2.2.使用 FileWriter 而不使用 try-with-resources
However, when we use FileWriter without try-with-resources, the FileWriter object won’t automatically get flushed and closed:
但是,当我们使用 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
}
As the test above shows, although we wrote some text to the file by calling FileWriter.write(), the file was still empty.
如上面的测试所示,尽管我们通过调用 FileWriter.write() 向文件写入了一些文本,但文件仍然是空的。
Next, let’s figure out how to solve this problem.
接下来,我们来想想如何解决这个问题。
3. FileWriter.flush() and FileWriter.close()
3.FileWriter.flush() 和 FileWriter.close()
In this section, let’s first solve the “file is still empty” problem. Then, we’ll discuss the difference between FileWriter.flush() and FileWriter.close().
在本节中,我们首先要解决 “文件仍然为空 “的问题。然后,我们将讨论 FileWriter.flush() 和 FileWriter.close() 的区别。
3.1. Solving “The File Is Still Empty” Problem
3.1.解决 “文件仍为空 “问题
First, let’s understand quickly why the file is still empty after we called FileWriter.write().
首先,让我们快速了解一下为什么在调用 FileWriter.write() 之后文件仍然是空的。</em
When we invoke FileWriter.write(), the data is not immediately written to the file on the disk. Instead, it is temporarily stored in a buffer. Consequently, to visualize the data in the file, it is necessary to flush the buffered data to the file.
当我们调用 FileWriter.write() 时, 数据不会立即写入磁盘上的文件。相反,数据会暂时存储在缓冲区中。因此,要可视化文件中的数据,就必须将缓冲数据刷新到文件中。
The straightforward way is to call the flush() method:
直接的方法是调用 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
}
As we can see, after calling flush(), we can obtain the expected data by reading the file.
我们可以看到,在调用 flush() 后, 我们可以通过读取文件获得预期的数据。
Alternatively, we can call the close() method to transfer the buffered data to the file. This is because close() first performs flush, then closes the file stream writer.
或者,我们可以调用 close() 方法将缓冲数据传输到文件。这是因为 close()首先执行刷新,然后关闭文件流写入器。
Next, let’s create a test to verify this:
接下来,让我们创建一个测试来验证这一点:
@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);
}
So, it looks pretty similar to the flush() call. However, the two methods serve different purposes when handling file output streams.
因此,它看起来与 flush() 调用非常相似。不过,这两种方法在处理文件输出流时有不同的用途。
Next, let’s have a close look at their differences.
接下来,让我们仔细看看它们的不同之处。
3.2. The Difference Between the flush() and close() Methods
3.2.flush() 和 close() 方法之间的区别
The flush() method is primarily used to force any buffered data to be written immediately without closing the FileWriter, while the close() method both performs flushing and releases associated resources.
flush() 方法主要用于在不关闭 FileWriter 的情况下强制立即写入任何缓冲数据,而 close() 方法既可执行刷新操作,又可释放相关资源。
In other words, invoking flush() ensures that the buffered data is promptly written to disk, allowing continued write or append operations to the file without closing the stream. Conversely, when close() is called, it writes the existing buffered data to the file and then closes it. Consequently, further data cannot be written to the file unless a new stream is opened, such as by initializing a new FileWriter object.
换句话说,调用 flush() 可确保缓冲数据立即写入磁盘,允许继续对文件进行写入或追加操作,而无需关闭流。相反,当调用 close() 时,它会将现有的缓冲数据写入文件,然后关闭文件。因此,除非打开一个新的流,例如初始化一个新的 FileWriter 对象,否则就无法向文件写入其他数据。
Next, let’s understand this through some examples:
接下来,让我们通过一些例子来理解这一点:
@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
}
In the above example, we called write() three times to write three lines to the file. After each write() call, we invoked flush(). In the end, we can read the three lines from the target file.
在上面的示例中,我们调用了 write() 三次,向文件中写入了三行内容。在每次调用 write() 之后,我们都调用了flush()。最后,我们可以从目标文件中读取这三行内容。
However, if we attempt to write data after calling FileWriter.close(), IOException with the error message “Stream closed” will raise:
但是,如果我们在调用 FileWriter.close() 后尝试写入数据,就会引发错误消息 “Stream closed”(流已关闭)的 IOException:
@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. Conclusion
4.结论
In this article, we explored FileWriter‘s common usage. Also, we discussed the difference between FileWriter‘s flush() and close() methods.
在本文中,我们探讨了 FileWriter 的常用用法。此外,我们还讨论了 FileWriter 的 flush() 和 close() 方法之间的区别。
As always, the complete source code for the examples is available over on GitHub.
与往常一样,这些示例的完整源代码可在 GitHub 上获取。