Java 初學者常見困惑:System.out.println(obj) 時,有的對象輸出內容(如 String、包裝類),有的輸出類似 com.example.User@15db9742 的「地址值」。本文從 JVM 規範、Object 基類、toString() 契約、最佳實踐四個維度徹底講透輸出差異本質。
// PrintStream.println(Object x) 簡化源碼
public void println(Object x) {
String s = String.valueOf(x); // 關鍵行
synchronized (this) {
print(s);
newLine();
}
}
// String.valueOf(Object obj)
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString(); // 核心:調用對象的 toString()
}
結論:println(obj) 輸出什麼,完全取決於對象類別的 toString() 實現。
// java.lang.Object 源碼
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
// 例如:com.example.User@15db9742
// 其中 hashCode() 默認返回對象頭標識(常被誤稱為「內存地址」)
toString() 的類,輸出「全限定類名@十六進制哈希碼」。hashCode() 返回值(默認基於對象頭標識計算,與地址相關但不等於地址)。| 類別 | 是否重寫 toString() | 輸出示例 | 說明 |
|---|---|---|---|
| String | ✅ 是 | "hello" | 直接返回字符串內容 |
| Integer/Long 等包裝類 | ✅ 是 | "123" | 返回數值的字符串形式 |
| 數組 | ❌ 否 | [I@15db9742 | 繼承 Object 默認實現,[I 表示 int[] |
| ArrayList/HashMap 等集合 | ✅ 是 | [a, b, c] / {k1=v1, k2=v2} | AbstractCollection 重寫,遞歸調用元素 toString() |
| 自定義類 User | ❌ 否(默認) | com.example.User@15db9742 | 需手動重寫 |
| 自定義類 User | ✅ 是 | User{name='張三', age=25} | 推薦格式 |
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", email='" + email + '\'' +
'}';
}
Alt+Insert → toString() → 勾選字段 → OK。Source → Generate toString()...。// Lombok
@Data // 含 @ToString
public class User {
private String name;
private int age;
private String email;
}
// 或精確控制
@ToString(includeFieldNames = true, callSuper = true)
public class User { ... }
// Apache Commons Lang
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);
}
equals(),通常也應重寫 toString() 保持一致性。| 場景 | 方法 |
|---|---|
| IDE 調試 | 斷點懸停變量 / Variables 面板 / Evaluate Expression 輸入 obj.toString() |
| 日志框架 | Log4j2/Logback 配置 %ex{full} 或使用 JSON 佈局自動序列化對象 |
| JSON 序列化 | new ObjectMapper().writeValueAsString(obj) (Jackson) / Gson().toJson(obj) |
| Apache Commons | ToStringBuilder.reflectionToString(obj) 利用反射輸出所有字段 |
Java 對象輸出「地址值」還是「內容值」,唯一決定因素是該類是否重寫了 toString() 方法。String、包裝類、集合類已重寫,自定義類默認繼承 Object.toString() 輸出類名@哈希碼。養成「寫類必重寫 toString()」的習慣,配合 Lombok @Data 或 IDE 自動生成,能極大提升調試效率與日誌可讀性。