PrintStreamPrintWriter 是 Java I/O 中用于输出数据的两个重要包装类。它们最大的共同点是:它们都不会抛出检查型异常,这在日常开发中非常方便。

以下是它们的详细区别以及关于异常处理机制的深入解析。


一、 PrintStream 和 PrintWriter 的核心区别

最本质的区别在于:处理数据的方式不同(字节 vs 字符)

特性PrintStream (字节流)PrintWriter (字符流)
继承体系继承自 FilterOutputStream (字节流)。继承自 Writer (字符流)。
数据处理处理的是 字节。虽然它有打印字符串的方法,但底层本质上还是写字节。处理的是 字符。专门用于处理文本输出,能够正确处理字符编码。
编码处理它使用平台默认的字符编码将字符转换为字节。在国际化场景下容易导致乱码。可以在构造时指定字符集,或者在构造时传入 OutputStream 时指定编码,对多语言支持更好。
典型应用System.out 就是 PrintStream 的实例。主要用于二进制数据输出或简单的控制台日志。用于写入文本文件、网络响应等需要处理字符编码的场景。
自动刷新只有在写入字节数组、调用 println 或写入换行符时,如果开启了自动刷新才会触发。只有在调用 println, printf, 或 format 方法时,如果开启了自动刷新才会触发。

什么时候用哪个?

1.使用 PrintStream:当你需要处理二进制数据,或者你正在编写类似 System.out 这样的底层输出工具时。
2. 使用PrintWriter:绝大多数情况下,编写文本文件、网页响应或日志时,应该首选 PrintWriter。因为它能更好地处理字符编码(UTF-8等),避免中文乱码问题。


二、 异常处理机制

这是这两个类最独特的设计之一。

1. “吞掉”异常的设计

普通的 I/O 流(如 FileOutputStreamBufferedWriter)在写入失败时(如磁盘满、文件被占用)会抛出 IOException。调用者必须通过 try-catch 捕获或声明抛出。

但是,PrintStreamPrintWriter 的方法(如 print(), println(), write()不会抛出 IOException

为什么不抛出?
设计初衷是为了简化编程。在打印日志或控制台输出时,如果每次都要写 try-catch 会非常繁琐。Java 设计者认为这些操作通常不应该中断程序流程,所以选择了“静默失败”。

2. 如何检查错误?

既然方法不抛异常,那怎么知道写入成功没有?

这两个类都提供了一个方法:checkError()

  • 它会刷新流。
  • 它返回一个 boolean 值:如果底层流曾经发生过错误,则返回 true;否则返回 false
  • 一旦发生错误,内部的错误标志位就会被设置,后续调用 checkError() 将一直返回 true(除非调用了特定的内部清除方法,通常不可达)。

3. 代码示例

普通流的异常处理(麻烦):

try {
    FileWriter fw = new FileWriter("test.txt");
    fw.write("Hello"); // 必须处理 IOException
    fw.close();
} catch (IOException e) {
    e.printStackTrace();
}

PrintStream/PrintWriter 的异常处理(简洁但有隐患):

// 构造时仍需处理 FileNotFoundException
try (PrintWriter pw = new PrintWriter("test.txt")) {
    pw.println("Hello World"); // 这里不需要 try-catch,编译通过
    pw.println(123); 
    
    // 但是,如果这里写入失败(比如磁盘满),程序不会报错,继续往下跑!
    // 我们需要手动检查:
    if (pw.checkError()) {
        System.out.println("写入文件发生错误!");
    }
} catch (FileNotFoundException e) {
    e.printStackTrace();
}

三、 总结与最佳实践

  1. 优先选择PrintWriter:在处理文本输出(特别是写文件或网络Socket)时,PrintWriter 更合适,因为它支持指定编码,避免乱码。
  2. 注意构造函数的异常:虽然 print 方法不抛异常,但构造函数(如 new PrintWriter("file.txt"))会抛出 FileNotFoundException,这个必须捕获。
  3. 谨慎对待“静默失败”
  4. 对于日志记录,静默失败通常是可以接受的(日志写不进去不应该崩掉业务系统)。
  5. 对于关键业务数据(如交易记录、入库单据),不要依赖 PrintWriterPrintStream,因为它们可能会丢数据且不报错。此时应使用普通的 BufferedWriterFileOutputStream,并严格处理 IOException
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐