System.out.println 本质是 JVM 启动时通过本地方法初始化 PrintStream out,经编码转换、写入文件描述符 1 或调用系统 API 输出,并自动追加平台特定换行符。

System.out.println 看似简单,背后涉及 Java 的类加载、静态初始化、I/O 流封装和底层系统调用等多个层次。
System 类与静态字段 out
System 是一个 final 类,不能被继承。它内部定义了一个静态公共字段:
public static final PrintStream out = null;这个字段在 JVM 启动时由本地方法(C/C++ 实现)初始化为指向标准输出的 PrintStream 实例。也就是说,out 不是 Java 代码 new 出来的,而是由 JVM 在启动阶段注入的。
PrintStream 的作用:字节流到字符的桥接
PrintStream 是一个带自动刷新、可处理多种数据类型的输出流包装器。它内部持有:
- 一个 OutputStream(通常是 FileOutputStream,对应文件描述符 1,即 stdout)
- 一个 字符编码器(默认使用平台默认编码,如 UTF-8 或 GBK)
- 一个 缓冲区(但 println 默认会自动 flush)
调用 println("Hello World") 时,PrintStream 先将字符串转为字节数组(按当前编码),再写入底层 OutputStream。
底层如何真正“打印”?
最终,字节数据会传递到:
- Unix/Linux/macOS:写入文件描述符 1(stdout),由终端进程(如 bash、iTerm)读取并渲染到屏幕
- Windows:调用 Win32 API(如 WriteConsoleA/W)或写入控制台句柄
整个过程不经过 Java 的 NIO 或 CharsetEncoder 显式调用,而是由 JVM 内置的本地 I/O 实现完成,效率高且与平台适配。
为什么 println 会换行?
PrintStream.println() 并非只调用 write(),而是:
- 先调用 print() 输出内容
- 再调用 write(System.lineSeparator().getBytes())
而 System.lineSeparator() 返回的是当前操作系统的换行符:\n(Unix)、\r\n(Windows)或 \r(旧 Mac)。这个值由 JVM 启动时读取系统属性确定。










