System.out.println("内容"); 可向控制台打印文本,`System.out.println(“Hello World
基础打印功能实现
标准输出三剑客
| 方法名 | 特点 | 适用场景 | 示例代码 |
|---|---|---|---|
System.out.print() |
无自动换行,纯文本输出 | 连续输出同一行内容 | System.out.print("计数: " + i); |
System.out.println() |
自动添加换行符n,最常用基础方法 |
常规调试信息展示 | System.out.println("用户登录成功"); |
System.out.printf() |
支持格式化输出,类似C语言printf风格 | 结构化数据展示 | System.out.printf("%d岁", age); |
System.out.format() |
Java特有格式化方法,功能与printf完全一致 | 跨平台兼容性要求高的场景 | System.out.format("%.2f元", price); |
关键差异说明:println本质是print+换行符,而printf/format通过格式说明符实现精准排版,注意printf属于PrintStream类的方法,底层依赖本地编码设置。
对象专属打印方案
当直接打印对象实例时,会触发以下两种机制:
- 未重写toString():输出对象哈希码(十六进制表示)
- 已重写toString():执行自定义返回值
class User {
private String name;
public User(String name) { this.name = name; }
@Override
public String toString() {
return "用户[" + name + "]"; // 自定义显示格式
}
}
// 测试效果
User u = new User("张三");
System.out.println(u); // 输出:用户[张三]
重要提示:建议所有业务实体类都重写toString()方法,这对调试至关重要,IDEA等开发工具会在调试视图中自动调用该方法。
进阶输出技巧
格式化输出详解
通过占位符实现类型转换和宽度控制:
| 格式符 | 含义 | 示例 | 输出结果 |
|——–|———————–|———————-|—————-|
| %d | 十进制整数 | printf("%04d", 7) | 0007 |
| %x | 十六进制小写 | printf("%#x", 255) | 0xff |
| %f | 浮点数(默认6位小数) | printf("%.2f", 3.14)| 3.14 |
| %s | 字符串 | printf("%10s", "Hi")| ” Hi” |
| %n | 平台无关换行符 | printf("结束%n") | 结束+换行 |
特殊修饰符组合示例:
%+d:显示正负号(+10/-5)%,d:千分位分隔符(1,000)%<:左对齐(%-10s使字符串靠左)
异常堆栈打印
捕获异常后,可通过两种方式获取完整调用链:
try {
// 可能抛出异常的代码
} catch (Exception e) {
e.printStackTrace(); // 完整堆栈跟踪
System.err.println("发生严重错误: " + e.getMessage()); // 仅错误信息
}
区别说明:printStackTrace()会打印从异常发生点到主方法的完整调用路径,适合开发环境;getMessage()仅返回异常描述信息,适合生产环境日志记录。
输入输出流体系
理解底层原理有助于掌握高级用法:
System.in → InputStream
System.out → PrintStream (继承自FilterOutputStream)
System.err → PrintStream
关键特性:
- 缓冲机制:
System.out默认启用8KB缓冲区,只有遇到换行/flush/程序终止才会真正输出 - 错误分流:
System.err独立于标准输出,通常用于错误信息且默认红色显示 - 关闭限制:禁止手动关闭这三个流,尝试
System.out.close()会抛出SecurityException
强制立即输出方法:
System.out.flush():清空缓冲区new LineNumberReader(System.in):带行号读取输入
工程实践建议
| 场景 | 推荐方案 | 反例警告 |
|---|---|---|
| 日常调试 | System.out.println()+合理断点 |
滥用大量打印影响性能 |
| 生产环境日志 | 集成Log4j/SLF4J等专业日志组件 | 直接使用System.out存储日志 |
| 大数据量输出 | 批量构建StringBuilder后再输出 | 频繁调用println造成IO瓶颈 |
| 多线程安全输出 | 使用synchronized块包裹打印操作 |
并发打印导致内容错乱 |
性能对比实验:
long start = System.currentTimeMillis();
for(int i=0; i<10000; i++){
System.out.println(i); // 耗时约3秒
}
start = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for(int i=0; i<10000; i++){
sb.append(i).append("n");
}
System.out.print(sb); // 耗时约0.1秒
常见误区解析
- 混淆输出目标:
System.out用于正常信息,System.err用于错误/警告信息,二者不应混用 - 忽略资源释放:虽然不能关闭系统流,但对自定义的
PrintWriter等包装类必须显式关闭 - 过度格式化:复杂格式字符串可读性差,建议将长表达式拆分为多个简单输出
- 线程安全问题:非volatile变量在多线程环境下可能出现可见性问题,应配合原子类或同步机制
FAQs
Q1: 为什么有时候打印的对象不是预期的内容?
A: 当没有正确重写toString()方法时,默认会输出对象的类名+哈希码,解决方案是在业务类中添加@Override public String toString()方法,返回有意义的字符串表示。
Q2: 如何在打印时自动添加时间戳?
A: 可以使用SimpleDateFormat预处理时间字符串,结合printf实现:
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.printf("[%s] 操作完成", df.format(new Date()));
// 输出示例:[2025-06-15 14:30:45] 操作完成
对于高频日志场景,建议使用Logback等
