如何使用System.setOut()将程序输出重定向至指定磁盘文件?
- 内容介绍
- 相关推荐
本文共计821个文字,预计阅读时间需要4分钟。
不能直接传入FileOutputStream,必须包装成PrintStream;否则会抛出NullPointerException或输出异常(如中文乱码、换行丢失等)。Java的System.out是PrintStream类型,System.setOut只接受该类型实例。
实操建议:
- 用
new PrintStream(new FileOutputStream("output.log"))构造,避免裸露流操作 - 务必在构造时显式指定字符集,例如
new PrintStream(new FileOutputStream("output.log"), true, "UTF-8"),否则默认平台编码可能导致中文写入失败 - 重定向前建议先检查目标目录可写,否则运行时抛
FileNotFoundException - 若需追加写入,用
new FileOutputStream("output.log", true)(第二个参数为true)
重定向后如何恢复原始控制台输出
调用 System.setOut() 前必须保存原始 PrintStream,否则无法还原。JVM 启动后 System.out 默认指向控制台,但该引用不可通过反射或静态变量重新获取。
实操建议:
- 在程序启动早期执行:
PrintStream originalOut = System.out; - 重定向完成后,用
System.setOut(originalOut)恢复(注意:不是System.setOut(null)或System.out = originalOut) - 若在多线程环境中使用,确保恢复操作只执行一次,且不与重定向逻辑并发
常见错误:日志写入失败但无异常提示
现象是文件被创建、大小始终为 0,或只有部分输出。根本原因通常是缓冲未刷新,或流未关闭导致内容滞留在内存缓冲区中。
实操建议:
-
PrintStream默认带缓冲,但不会自动 flush —— 每次写入后手动调用System.out.flush() - 若重定向长期存在(如整个应用生命周期),推荐启用自动 flush:构造时传入
true作为第二个参数,即new PrintStream(fos, true, "UTF-8") - 不要依赖 JVM 退出时自动关闭流;显式调用
System.out.close()前,必须先恢复原始out,否则后续println会报错
替代方案:为什么有时不该用 System.setOut()
它影响的是全局 System.out,所有线程、所有类(包括第三方库)的 println 都会写入该文件。这在 Web 容器、测试框架或日志已集成 Log4j/SLF4J 的项目中极易引发干扰。
更安全的做法:
- 用
Logger.getLogger("myapp").info(...)替代System.out.println(...) - 对单个模块临时调试,改用
try (PrintStream ps = new PrintStream(...)) { ps.println(...); } - 若必须劫持标准输出(如单元测试捕获输出),优先用 JUnit 的
SystemOutRule或ByteArrayOutputStream+PrintStream组合,而非永久修改System.out
本文共计821个文字,预计阅读时间需要4分钟。
不能直接传入FileOutputStream,必须包装成PrintStream;否则会抛出NullPointerException或输出异常(如中文乱码、换行丢失等)。Java的System.out是PrintStream类型,System.setOut只接受该类型实例。
实操建议:
- 用
new PrintStream(new FileOutputStream("output.log"))构造,避免裸露流操作 - 务必在构造时显式指定字符集,例如
new PrintStream(new FileOutputStream("output.log"), true, "UTF-8"),否则默认平台编码可能导致中文写入失败 - 重定向前建议先检查目标目录可写,否则运行时抛
FileNotFoundException - 若需追加写入,用
new FileOutputStream("output.log", true)(第二个参数为true)
重定向后如何恢复原始控制台输出
调用 System.setOut() 前必须保存原始 PrintStream,否则无法还原。JVM 启动后 System.out 默认指向控制台,但该引用不可通过反射或静态变量重新获取。
实操建议:
- 在程序启动早期执行:
PrintStream originalOut = System.out; - 重定向完成后,用
System.setOut(originalOut)恢复(注意:不是System.setOut(null)或System.out = originalOut) - 若在多线程环境中使用,确保恢复操作只执行一次,且不与重定向逻辑并发
常见错误:日志写入失败但无异常提示
现象是文件被创建、大小始终为 0,或只有部分输出。根本原因通常是缓冲未刷新,或流未关闭导致内容滞留在内存缓冲区中。
实操建议:
-
PrintStream默认带缓冲,但不会自动 flush —— 每次写入后手动调用System.out.flush() - 若重定向长期存在(如整个应用生命周期),推荐启用自动 flush:构造时传入
true作为第二个参数,即new PrintStream(fos, true, "UTF-8") - 不要依赖 JVM 退出时自动关闭流;显式调用
System.out.close()前,必须先恢复原始out,否则后续println会报错
替代方案:为什么有时不该用 System.setOut()
它影响的是全局 System.out,所有线程、所有类(包括第三方库)的 println 都会写入该文件。这在 Web 容器、测试框架或日志已集成 Log4j/SLF4J 的项目中极易引发干扰。
更安全的做法:
- 用
Logger.getLogger("myapp").info(...)替代System.out.println(...) - 对单个模块临时调试,改用
try (PrintStream ps = new PrintStream(...)) { ps.println(...); } - 若必须劫持标准输出(如单元测试捕获输出),优先用 JUnit 的
SystemOutRule或ByteArrayOutputStream+PrintStream组合,而非永久修改System.out

