1. 引言

本文将深入对比Java中的PrintStreamPrintWriter两个类,帮助开发者根据实际场景选择最合适的实现。在开始前,建议先了解这两个类的基础用法(可参考相关文档)。

2. PrintStreamPrintWriter的共性

这两个类功能高度重叠,容易让开发者混淆。我们先看它们的共同点,再分析差异。

2.1. 字符编码支持

字符编码确保文本在不同平台间保持一致解析。自JDK 1.4起,PrintStream的构造器新增了编码参数:

public PrintStream(OutputStream out, boolean autoFlush, String encoding) throws UnsupportedEncodingException {
    this(requireNonNull(out, "Null output stream"), autoFlush, toCharset(encoding));
}

PrintWriter从诞生之初就支持编码参数:

public PrintWriter(OutputStream out, boolean autoFlush, Charset charset) {
    this(new BufferedWriter(new OutputStreamWriter(out, charset)), autoFlush);

    // save print stream for error propagation
    if (out instanceof java.io.PrintStream) {
        psOut = (PrintStream) out;
    }
}

⚠️ 关键点:若未指定编码,两者都会使用平台默认编码。

2.2. 文件写入能力

两者都支持直接写入文件,可通过构造器传入文件路径或File对象:

PrintStream示例:

public PrintStream(File file) throws FileNotFoundException {
    this(false, new FileOutputStream(file));
}

PrintWriter示例:

public PrintWriter(File file) throws FileNotFoundException {
    this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file))), false);
}

虽然都能写文件,但底层实现差异显著(后续章节会展开)。

3. PrintStreamPrintWriter的差异

尽管功能相似,但底层实现差异决定了它们适用场景不同。

3.1. 数据处理机制

PrintStream继承自OutputStream,属于字节流,逐字节处理数据。而PrintWriter字符流,逐字符处理并自动进行Unicode转换。

3.2. 非文本数据处理

处理二进制文件时差异明显。以下代码对比写入PNG文件的效果:

public class DataStream {

    public static void main (String[] args) throws IOException {
        FileInputStream inputStream = new FileInputStream("image.png");
        PrintStream printStream = new PrintStream("ps.png");

        int b;
        while ((b = inputStream.read()) != -1) {
            printStream.write(b);
        }
        printStream.close();

        FileReader reader = new FileReader("image.png");
        PrintWriter writer = new PrintWriter("pw.png");
    
        int c;
        while ((c = reader.read()) != -1) {
            writer.write(c);
        }
        writer.close();
        inputStream.close();
    }
}

结果对比:

  • ps.png:原始数据完整保留(字节流处理)
  • pw.png:文件损坏(字符流错误解析二进制数据)

踩坑提示:用PrintWriter处理二进制文件会导致数据损坏!

3.3. 文本数据处理

OutputStream直接写字符串会报错:

public class PrintStreamWriter {
    public static void main (String[] args) throws IOException {
        OutputStream out = new FileOutputStream("TestFile.txt");
        out.write("foobar"); // 编译失败!
        out.flush();
        out.close();
    }
}

需转换为字节数组:

out.write("foobar".getBytes());

PrintStream内部自动转换:

public class PrintStreamWriter {
    public static void main (String[] args) throws IOException {
        PrintStream out = new PrintStream("TestFile.txt");
        out.print("Hello, world!"); // 内部调用getBytes()
        out.flush();
        out.close();
    }
}

PrintWriter则直接处理字符:

public class PrintStreamWriter {
    public static void main (String[] args) throws IOException {
        PrintWriter out = new PrintWriter("TestFile.txt");
        out.print("Hello, world!"); // 直接处理字符
        out.flush();
        out.close();
    }
}

结论:文本I/O操作优先选择PrintWriter,其Unicode特性更利于国际化。

3.4. 刷新机制差异

自动刷新行为不同:

  • PrintStream:写入字节数组、调用println()或换行符时触发
  • PrintWriter:仅调用println()printf()format()时触发

测试代码:

public class AutoFlushExample {
    public static void main (String[] args) throws IOException {
        PrintStream printStream = new PrintStream(new FileOutputStream("autoFlushPrintStream.txt"), true);
        printStream.write("Hello, world!\n".getBytes()); // 换行符触发刷新
        printStream.close();

        PrintWriter printWriter = new PrintWriter(new FileOutputStream("autoFlushPrintWriter.txt"), true);
        printWriter.print("Hello, world!"); // 不保证刷新!
        printWriter.close();
    }
}

结果:

  • autoFlushPrintStream.txt:内容写入成功
  • autoFlushPrintWriter.txt:可能为空(未满足刷新条件)

简单粗暴解决方案:显式调用flush()确保数据写入!

4. 总结

通过对比分析,我们得出以下结论:

  1. 数据类型适配

    • 二进制数据 → 优先PrintStream(字节流)
    • 文本数据 → 优先PrintWriter(字符流+Unicode)
  2. 国际化支持

    • PrintWriter的Unicode特性更利于多语言场景
  3. 刷新机制

    • PrintStream的自动刷新更可靠
    • PrintWriter需显式调用flush()或使用特定方法

选择建议:

graph LR
A[数据类型] -->|二进制| B[PrintStream]
A -->|文本| C[PrintWriter]
C --> D[需要国际化?]
D -->|是| C
D -->|否| E[均可]

完整代码示例可在GitHub获取。


原始标题:PrintStream vs PrintWriter in Java