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编程工具,助力开发者即刻编程。

更多推荐