一、经典 IO (java.io 包)
这是最传统和广为人知的方式,核心是 Reader 和 Writer 这两个抽象类,用于处理字符文本。
1. FileReader / FileWriter
这是最基础的实现,用于直接读写文件。
写文件示例:
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class Demo {
public static void main(String[] args) {
List<String> contents = new ArrayList<>();
contents.add("test1");
contents.add("test2");
write("D:\\test.txt", contents);
}
private static void write(String fileName, List<String> contents) {
try (FileWriter writer = new FileWriter(fileName)) {
for (String content : contents) {
writer.write(content + "\n");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
读文件示例:
import java.io.FileReader;
import java.io.IOException;
public class Demo {
public static void main(String[] args) {
read("D:\\test.txt");
}
private static void read(String fileName) {
try (FileReader reader = new FileReader(fileName)) {
int charCode;
while ((charCode = reader.read()) != -1) {
System.out.print((char) charCode);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
-
优点:
-
API 简单直观,易于学习和使用。
-
-
缺点:
-
性能极差:
FileReader的read()方法是逐字符读取的,会产生大量的系统调用。 -
功能单一:没有缓冲区,不支持按行读取等高级功能。
-
编码问题:使用平台默认的字符编码(如 Windows 上是 GBK),无法指定编码,容易导致乱码。这是最大的坑。
-
-
适用场景:
-
基本不用。除非是处理非常小的、简单的、且编码与系统默认一致的文件,否则不推荐使用。
-
2. BufferedReader / BufferedWriter
在基础流之上包装了缓冲区,是最常用和经典的高效文本处理方式。
写文件示例 (BufferedWriter):
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class Demo {
public static void main(String[] args) {
List<String> contents = new ArrayList<>();
contents.add("test1");
contents.add("test2");
write("D:\\test.txt", contents);
}
private static void write(String fileName, List<String> contents) {
try (FileWriter fw = new FileWriter(fileName);
BufferedWriter writer = new BufferedWriter(fw)) {
for (String content : contents) {
writer.write(content);
writer.newLine(); // 换行,平台无关,比写 "\n" 更好
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
读文件示例 (BufferedReader):
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class Demo {
public static void main(String[] args) {
read("D:\\test.txt");
}
private static void read(String fileName) {
try (FileReader fr = new FileReader(fileName);
BufferedReader reader = new BufferedReader(fr)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
-
优点:
-
性能高:内置缓冲区(默认8KB),大大减少了底层系统的读写次数。
-
功能强大:
BufferedReader.readLine()是处理文本文件的神器,非常适合逻辑按行处理的场景。
-
-
缺点:
-
仍然依赖于
FileReader/FileWriter,因此默认编码问题依然存在。
-
-
适用场景:
-
需要按行处理文本内容的绝大多数情况(如日志分析、数据文件读取)。
-
读写普通大小的文本文件。是 Java 7 之前事实上的标准。
-
3. InputStreamReader / OutputStreamWriter (解决编码问题的关键)
这是桥梁,用于将字节流转换为字符流,并且可以显式指定字符编码。
写文件示例 (指定UTF-8编码):
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
public class Demo {
public static void main(String[] args) {
List<String> contents = new ArrayList<>();
contents.add("test1");
contents.add("test2");
write("D:\\test.txt", contents);
}
private static void write(String fileName, List<String> contents) {
try (FileOutputStream fos = new FileOutputStream(fileName);
OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8); // 指定编码
BufferedWriter writer = new BufferedWriter(osw)) { // 再用Buffer包装,提升性能
for (String content : contents) {
writer.write(content);
writer.newLine();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
读文件示例 (指定UTF-8编码):
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
public class Demo {
public static void main(String[] args) {
read("D:\\test.txt");
}
private static void read(String fileName) {
try (FileInputStream fis = new FileInputStream(fileName);
InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8); // 指定编码
BufferedReader reader = new BufferedReader(isr)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
-
优点:
-
解决了编码问题:可以明确指定字符集(如 UTF-8, GBK),确保跨环境一致性,避免乱码。
-
灵活性高,可以对接任何字节流。
-
-
缺点:
-
用法比
FileReader/Writer稍复杂一些。
-
-
适用场景:
-
所有需要处理编码的文本读写场景。这是处理文本文件的最佳实践组合:
BufferedReader+InputStreamReader+FileInputStream。
-
4. PrintWriter
提供了丰富的打印格式方法,如 print(), println(), printf()。
写文件示例:
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
public class Demo {
public static void main(String[] args) {
List<String> contents = new ArrayList<>();
contents.add("test1");
contents.add("test2");
writ("D:\\test.txt", contents);
}
private static void writ(String fileName, List<String> contents) {
try (FileWriter fw = new FileWriter(fileName);
PrintWriter writer = new PrintWriter(fw)) { // 也可以直接包装 FileOutputStream
for (String content : contents) {
writer.println(content);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
-
优点:
-
输出格式非常方便,类似于
System.out。 -
可以自动刷新缓冲区(可选功能)。
-
-
缺点:
-
异常处理 silent:它的方法不会抛出
IOException,需要通过checkError()方法自己检查错误状态,容易让人忽略错误。
-
-
适用场景:
-
需要向文件或输出流中写入格式化文本,比如生成报告、数据文件。
-
二、NIO 和 NIO.2 (java.nio.file 包)
Java 7 引入了 NIO.2,提供了更现代、更强大的文件操作 API。
1. Files 工具类 (一次性读写)
Files 类提供了一系列静态方法,可以用一行代码完成整个文件的读写,极其方便。
读文件示例 (读取所有行到List):
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
public class Demo {
public static void main(String[] args) {
read("D:\\test.txt");
}
private static void read(String fileName) {
try {
// 读取所有行,默认编码是 UTF-8
List<String> lines = Files.readAllLines(Paths.get(fileName));
for (String line : lines) {
System.out.println(line);
}
// 读取整个文件为字节数组(适用于任何文件,包括文本)
byte[] bytes = Files.readAllBytes(Paths.get(fileName));
System.out.println(new String(bytes, StandardCharsets.UTF_8));
} catch (IOException e) {
e.printStackTrace();
}
}
}
写文件示例:
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
public class Demo {
public static void main(String[] args) {
List<String> contents = new ArrayList<>();
contents.add("test1");
contents.add("test2");
write("D:\\test.txt", contents);
}
private static void write(String fileName, List<String> contents) {
try {
// 将行的集合写入文件
Files.write(Paths.get(fileName), contents, StandardCharsets.UTF_8);
// 将字节数组写入文件
Files.write(Paths.get(fileName), "Hello Bytes".getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
e.printStackTrace();
}
}
}
-
优点:
-
极其简洁:一行代码搞定,无需关心流的打开和关闭。
-
功能强大:支持指定编码、文件打开选项(如追加、创建等)。
-
-
缺点:
-
不适合大文件:它会一次性将整个文件内容加载到内存中,如果文件非常大(如几个GB),会导致内存溢出(OOM)。
-
-
适用场景:
-
读写小文本文件(如配置文件、小规模数据文件)。
-
追求代码简洁和可读性的场景。
-
2. Files.newBufferedReader / Files.newBufferedWriter (NIO 的流式处理)
Files 类也提供了方法来创建基于 NIO Path 的 BufferedReader 和 BufferedWriter。
示例:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
public class Demo {
public static void main(String[] args) {
read("D:\\test.txt");
List<String> contents = new ArrayList<>();
contents.add("test1");
contents.add("test2");
write("D:\\test.txt", contents);
}
private static void read(String fileName) {
// 读
try (BufferedReader reader = Files.newBufferedReader(Paths.get(fileName), StandardCharsets.UTF_8)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void write(String fileName, List<String> contents) {
// 写 (支持OpenOption,比如StandardOpenOption.APPEND追加,CREATE创建等)
try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(fileName), StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.APPEND)) {
for (String content : contents) {
writer.write(content);
writer.newLine();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
-
优点:
-
结合了传统
BufferedReader/Writer的高效和 NIO.2 的现代 API(直接使用Path和指定编码更简单)。 -
支持丰富的文件打开选项。
-
适用于大文件,因为是流式处理,内存友好。
-
-
缺点:
-
相比
Files.readAllLines,代码量稍多。
-
-
适用场景:
-
处理大文本文件,需要流式、按行读取。
-
需要向文件追加内容等更复杂的操作。
-
3. Java 8 Stream API (函数式处理)
结合 Files.lines() 方法,可以用声明式的流式 API 来处理文本。
示例:
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;
public class Demo {
public static void main(String[] args) {
read("D:\\test.txt");
}
private static void read(String fileName) {
try (Stream<String> lines = Files.lines(Paths.get(fileName), StandardCharsets.UTF_8)) {
lines.filter(line -> line.contains("test")) // 过滤出包含"test"的行
.map(String::toUpperCase) // 转换为大写
.forEach(System.out::println); // 打印每一行
} catch (IOException e) {
e.printStackTrace();
}
}
}
-
优点:
-
代码非常优雅,符合函数式编程思想。
-
可以轻松地进行过滤、映射、收集等复杂操作。
-
背后也是缓冲读取,性能好且内存友好(流是惰性求值的)。
-
-
缺点:
-
需要熟悉 Java 8 Stream 的概念。
-
I/O 异常在 Lambda 表达式中处理起来不太方便。
-
-
适用场景:
-
需要对文本内容进行复杂的查找、过滤、转换、统计等操作。
-
追求现代、简洁、声明式的代码风格。
-
三、实用工具类
Scanner (主要用于读取结构化输入)
虽然常用于 System.in,但也可以用于读取文件。
示例:
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class Demo {
public static void main(String[] args) {
read("D:\\test.txt");
}
private static void read(String fileName) {
try (Scanner scanner = new Scanner(new File(fileName), "UTF-8")) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
System.out.println(line);
}
// 或者按分隔符(如空格)读取
// while (scanner.hasNext()) { String token = scanner.next(); }
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
-
优点:
-
可以按正则表达式或自定义分隔符解析文本,非常适合读取结构化的数据(如 CSV)。
-
-
缺点:
-
性能通常比
BufferedReader差。
-
-
适用场景:
-
读取格式已知的、结构化的文本数据(如由空格、逗号分隔的数据文件)。
-
选择指南
| 实现方式 | 优点 | 缺点 | 适用场景 |
| FileReader/Writer | 简单 | 性能差、编码依赖系统默认(坑) | 不推荐使用 |
| BufferedReader/Writer (包装 FileReader/Writer) | 性能高、支持按行读 | 编码依赖系统默认 | Java7前处理普通文件的标配 |
| BufferedReader/Writer (包装 InputStreamReader/Writer) | 性能高、可指定编码 | 代码稍多 | 处理编码、大文件的标准答案,兼容性好 |
| PrintWriter | 格式化输出方便 | 异常处理 | silent生成格式化报告、数据文件 |
| Files.readAllLines/write | 极其简洁、功能丰富 | 内存消耗大 | 读写小文件、配置文件 |
| Files.newBufferedReader/Writer | 现代API、指定编码、支持选项、内存友好 | 处理大文件、需要追加等操作 | |
| Files.lines() + Stream API | 声明式编程、处理逻辑强大 | 需熟悉Stream API |
对内容进行复杂查询、过滤、转换 |
| Scanner | 解析结构化数据能力强 | 性能相对较低 | 读取CSV、空格分隔等结构化文本 |
建议:
-
处理小文件(<几十MB):直接使用
Files.readAllLines()或Files.write(),代码最简洁。 -
处理大文件或需要控制内存:使用
Files.newBufferedReader()或Files.newBufferedWriter(),或者传统的BufferedReader+InputStreamReader组合。这是最稳健、最通用的方法。 -
需要对文本进行复杂处理:使用
Files.lines().stream(),利用 Stream API 的强大功能。 -
读取结构化数据:考虑使用
Scanner。 -
永远明确指定字符编码(如
StandardCharsets.UTF_8),不要依赖平台默认值。 -
始终使用 try-with-resources 语句(如示例中所示),确保流会被正确关闭,避免资源泄漏。
162

被折叠的 条评论
为什么被折叠?



