System.out / System.err 控制台输出
你有没有好奇过:为什么 Java 要设计 System.out 和 System.err 两个东西?它们看起来一模一样。
答案藏在历史里。
System.in / System.out / System.err 的历史
1995 年,Java 诞生之初就定义了三个标准流:
System.in → 标准输入(stdin) → 默认连接键盘
System.out → 标准输出(stdout) → 默认连接显示器
System.err → 标准错误(stderr)→ 默认连接显示器这三个是 Java 对操作系统标准流的封装。它们在 JVM 启动时就被初始化,永远存在。
System.out vs System.err
java
System.out.println("普通输出"); // 标准输出(stdout)
System.err.println("错误输出"); // 标准错误(stderr)| 对比 | System.out | System.err |
|---|---|---|
| 用途 | 普通输出信息 | 错误、警告信息 |
| 缓冲区 | 有缓冲 | 有缓冲 |
| 默认目标 | 控制台 | 控制台 |
| 重定向 | 可以重定向 | 可以单独重定向 |
在 IDE 中,out 和 err 通常混在一起显示。但在命令行中,可以通过重定向分开:
bash
java MyApp > output.txt 2> error.txt # out 和 err 分开
java MyApp > all.txt 2>&1 # err 合并到 outSystem.in 标准输入
java
// 读取一行输入
Scanner scanner = new Scanner(System.in);
System.out.print("请输入姓名: ");
String name = scanner.nextLine();
// 更底层的读取
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String line = reader.readLine();重定向输出到文件
重定向 System.out
java
// 保存原始 System.out
PrintStream originalOut = System.out;
// 重定向到文件
try (PrintStream fileOut = new PrintStream("app.log")) {
System.setOut(fileOut);
System.out.println("这条日志会写进文件");
}
// 恢复原始 System.out
System.setOut(originalOut);
System.out.println("这条显示在控制台");重定向 System.err
java
try (PrintStream errorOut = new PrintStream("errors.log")) {
System.setErr(errorOut);
System.err.println("错误信息写进 errors.log");
}同时重定向 out 和 err
java
try (
PrintStream outLog = new PrintStream("stdout.log");
PrintStream errLog = new PrintStream("stderr.log")
) {
System.setOut(outLog);
System.setErr(errLog);
System.out.println("标准输出");
System.err.println("错误输出");
}自定义日志工具
java
public class Logger {
private static PrintStream out = System.out;
private static PrintStream err = System.err;
private static Level level = Level.INFO;
public enum Level { DEBUG, INFO, WARN, ERROR }
public static void debug(String msg) {
if (level.compareTo(Level.DEBUG) <= 0) {
out.println("[DEBUG] " + msg);
}
}
public static void info(String msg) {
if (level.compareTo(Level.INFO) <= 0) {
out.println("[INFO] " + msg);
}
}
public static void error(String msg) {
if (level.compareTo(Level.ERROR) <= 0) {
err.println("[ERROR] " + msg);
}
}
// 重定向到文件
public static void redirectToFile(String outFile, String errFile)
throws FileNotFoundException {
System.setOut(new PrintStream(outFile));
System.setErr(new PrintStream(errFile));
}
}System.console() 的安全读取
System.console() 可以读取密码等敏感信息,输入不会显示在屏幕上:
java
Console console = System.console();
if (console != null) {
// 读取普通输入
String username = console.readLine("用户名: ");
// 读取密码(不显示输入)
char[] password = console.readPassword("密码: ");
// 输入不显示在终端,返回 char[] 而不是 String
// 使用完后手动清零,防止内存泄露
Arrays.fill(password, '0');
}常见问题
IDE 中 out 和 err 混在一起
java
// 在 IDE 中运行,以下两条输出可能颜色不同(err 可能是红色)
System.out.println("普通消息");
System.err.println("错误消息");
// 但它们通常显示在同一个控制台窗口缓冲区问题
System.out 和 System.err 都有缓冲区,可能导致日志顺序错乱:
java
System.out.print("请输入: "); // 没有换行,可能还在缓冲区
System.err.println("错误!"); // 先显示出来
// 解决方案:使用 flush
System.out.print("请输入: ");
System.out.flush();记住这个设计意图:
System.out用于正常输出,System.err用于错误信息。 生产环境建议用专业日志框架(SLF4J + Logback)。
