讲讲 Java中PrintStream 和 PrintWriter的区别
在Java中,PrintStream 和 PrintWriter 都是用于格式化输出的包装流(Wrapper Stream),它们提供了极其相似的 API(如 print()、println()、printf())。
但是,它们在底层实现、处理的数据类型以及适用场景上有本质的区别。以下是它们的核心区别和详细对比:
1. 核心区别:字节流 vs 字符流
这是两者最根本的区别:
PrintStream是字节流(Byte Stream):- 它继承自
FilterOutputStream(间接继承自OutputStream)。 - 它主要用于处理字节数据(如图片、视频、二进制文件,或者默认编码的文本)。
- 我们最常用的
System.out和System.err就是PrintStream的实例。
- 它继承自
PrintWriter是字符流(Character Stream):- 它继承自
Writer。 - 它专门用于处理字符数据(文本)。
- 由于它是字符流,它能更好地支持国际化(I18N),能够自动且正确地处理各种字符编码(如 UTF-8、GBK 等)。
- 它继承自
2. 详细对比
| 特性 | PrintStream |
PrintWriter |
|---|---|---|
| 继承关系 | OutputStream -> FilterOutputStream -> PrintStream |
Writer -> PrintWriter |
| 处理单位 | 字节(Byte) | 字符(Char,2字节) |
| 国际化/编码 | 较弱。虽然可以指定编码,但其本质是字节流,转换过程中可能出错。 | 极佳。原生支持字符编码,能自动处理 Unicode 字符。 |
| 自动刷新 (Auto-flush) | 当写入字节数组、调用 println() 或写入换行符 '\n' 时触发。 |
仅在调用 println()、printf() 或 format() 方法时触发(写入 '\n' 不会触发)。 |
| 常见应用 | 控制台输出(System.out)、向套接字(Socket)写入原始字节。 |
文本文件写入、Web 开发中向浏览器返回 HTML/JSON(如 Servlet 的 response.getWriter())。 |
| 异常处理 | 都不抛出 IOException。它们内部捕获了异常,需要通过 checkError() 方法手动检查。 |
3. 自动刷新(Auto-flush)的细微区别
两者都可以通过构造函数启用“自动刷新”功能(autoFlush = true),但触发条件略有不同:
PrintStream的触发条件:- 调用了
println()方法。 - 写入了换行符
'\n'。 - 写入了字节数组(
write(byte[]))。
- 调用了
PrintWriter的触发条件:- 调用了
println()、printf()或format()方法。 - 注意:仅仅写入
'\n'字符不会触发PrintWriter的自动刷新。
- 调用了
4. 代码示例对比
示例 1:使用 PrintStream 写入文件(字节操作)
java
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
public class PrintStreamExample {
public static void main(String[] args) {
try (PrintStream ps = new PrintStream(new FileOutputStream("log.txt"))) {
ps.println("Hello World");
ps.printf("Format number: %.2f\n", 3.14159);
// 内部捕获了异常,不需要显式 try-catch 每一个 write
if (ps.checkError()) {
System.out.println("写入过程中发生错误");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
示例 2:使用 PrintWriter 写入文件(字符/文本操作,推荐)
java
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class PrintWriterExample {
public static void main(String[] args) {
// PrintWriter 允许指定编码,对中文等支持更好
try (PrintWriter pw = new PrintWriter(new FileWriter("info.txt"))) {
pw.println("你好,世界!"); // 完美支持中文
pw.printf("商品价格: %.2f 元\n", 99.9);
if (pw.checkError()) {
System.out.println("写入过程中发生错误");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
5. 总结与选型建议
- 优先选择
PrintWriter:
当你在写纯文本文件、网页内容或任何涉及字符/文字的输出时,应该优先使用PrintWriter。它对字符编码的支持更安全、更健壮。 - 选择
PrintStream的场景:- 当你需要向控制台输出调试信息时(通常直接用
System.out)。 - 当你需要处理原始字节,或者目标输出流是
OutputStream且你不想将其包装成Writer时。
- 当你需要向控制台输出调试信息时(通常直接用
右滑查看面试常问