1. 概述

在这个快速教程中,我们将学习如何使用Java以不同的方式将一个字符串列表写入文本文件。首先讨论FileWriter,然后是BufferedWriter,最后是Files.writeString

2. 使用FileWriter

java.io包包含了一个名为FileWriter的类,用于将字符数据写入文件。查看类层次结构,我们会发现FileWriter类继承自OutputStreamWriter,后者又继承自Writer类。

让我们看看初始化FileWriter可用的构造函数:

FileWriter f = new FileWriter(File file);
FileWriter f = new FileWriter(File file, boolean append);
FileWriter f = new FileWriter(FileDescriptor fd);
FileWriter f = new FileWriter(File file, Charset charset);
FileWriter f = new FileWriter(File file, Charset charset, boolean append);
FileWriter f = new FileWriter(String fileName);
FileWriter f = new FileWriter(String fileName, Boolean append);
FileWriter f = new FileWriter(String fileName, Charset charset);
FileWriter f = new FileWriter(String fileName, Charset charset, boolean append);

请注意,FileWriter的所有构造函数都假设默认的字节缓冲大小和默认的字符编码是可以接受的。

现在,让我们学习如何使用FileWriter将一个字符串列表写入文本文件:

FileWriter fileWriter = new FileWriter(TEXT_FILENAME);
for (String str : stringList) {
    fileWriter.write(str + System.lineSeparator());
}
fileWriter.close();
return TEXT_FILENAME;

在示例中需要注意的是,如果sampleTextFile.txt不存在,FileWriter会自动创建它。如果文件已存在,我们可以选择覆盖或追加内容,这取决于构造函数的选择。

3. 使用BufferedWriter

java.io包中有一个BufferedWriter类,可以与其他Writer一起使用,以提高性能地写入字符数据。它为何更高效呢?

当我们使用BufferedWriter时,字符会被写入缓冲区而不是磁盘。当缓冲区满后,所有数据一次性写入磁盘,从而减少了与磁盘之间的通信,提高了性能。

在类层次结构中,BufferedWriter类继承自Writer类。

让我们看看初始化BufferedWriter可用的构造函数:

BufferedWriter b = new BufferedWriter(Writer w);
BufferedWriter b = new BufferedWriter(Writer w, int size);

如果不指定缓冲区大小,它将使用默认值。

现在,让我们探索如何使用BufferedWriter将一个字符串列表写入文本文件:

BufferedWriter br = new BufferedWriter(new FileWriter(TEXT_FILENAME));
for (String str : stringList) {
    br.write(str + System.lineSeparator());
}
br.close();
return TEXT_FILENAME;

在上面的代码中,我们使用BufferedWriter包装了FileWriter,这样可以减少读取调用,从而提高性能。

4. 使用Files.writeString

java.nio包中的Files类提供了writeString()方法,用于将字符写入文件。这个方法在Java 11中引入。

让我们看看java.nio.file.Files中的两个重载方法:

public static Path writeString​(Path path, CharSequence csq, OpenOption… options) throws IOException
public static Path writeString​(Path path, CharSequence csq, Charset cs, OpenOption… options) throws IOException

注意,在第一个方法中,我们不需要指定Charset。默认情况下,它使用UTF-8字符集,而在第二个方法中,我们可以指定Charset。接下来,让我们学习如何使用Files.writeString将一个字符串列表写入文本文件:

Path filePath = Paths.get(TEXT_FILENAME);
Files.deleteIfExists(filePath);
Files.createFile(filePath);
for (String str : stringList) {
    Files.writeString(filePath, str + System.lineSeparator(),
    StandardOpenOption.APPEND);
}
return filePath.toString();

在上述示例中,如果文件已存在,我们先删除它,然后使用Files.createFile()方法创建新文件。我们设置了OpenOption字段为StandardOperation.APPEND,这意味着文件将以追加模式打开。如果没有指定OpenOption,可能会发生以下两种情况:

  • 如果文件已存在,它将被覆盖。
  • 如果文件不存在,Files.writeString会创建新文件并在其中写入。

5. 测试

对于JUnit测试,我们定义一个字符串列表:

private static final List<String> stringList = Arrays.asList("Hello", "World");

在这里,我们将使用NIO Files.linescount()方法来计算输出文件的行数。行数应该等于列表中的字符串数量。

现在,让我们开始测试FileWriter实现:

@Test
public void givenUsingFileWriter_whenStringList_thenGetTextFile() throws IOException {
    String fileName = FileWriterExample.generateFileFromStringList(stringList);
    long count = Files.lines(Paths.get(fileName)).count();
    assertTrue("No. of lines in file should be equal to no. of Strings in List", ((int) count) == stringList.size());
}

接下来,我们将测试BufferedWriter实现:

@Test
public void givenUsingBufferedWriter_whenStringList_thenGetTextFile() throws IOException {
    String fileName = BufferedWriterExample.generateFileFromStringList(stringList);
    long count = Files.lines(Paths.get(fileName)).count();
    assertTrue("No. of lines in file should be equal to no. of Strings in List", ((int) count) == stringList.size());
}

最后,测试Files.writeString实现:

@Test
public void givenUsingFileWriteString_whenStringList_thenGetTextFile() throws IOException {
    String fileName = FileWriteStringExample.generateFileFromStringList(stringList);
    long count = Files.lines(Paths.get(fileName)).count();
    assertTrue("No. of lines in file should be equal to no. of Strings in List", ((int) count) == stringList.size());
}

6. 总结

在这篇文章中,我们探讨了将字符串列表写入文本文件的三种常见方法,并通过JUnit测试验证了我们的实现。完整的教程代码可在GitHub上找到:https://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-11-3