首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java toString 遞歸陷阱:StackOverflowError 根因與正確姿勢

     編輯:Java 相關知識

經典錯誤現場

public class InfiniteRecursion {
    @Override
    public String toString() {
        return "address: " + this;  // 隱式調用 this.toString() → 無限遞歸
    }

    public static void main(String[] args) {
        System.out.println(new InfiniteRecursion()); // StackOverflowError
    }
}

為什麼會爆棧?

  1. System.out.println(obj) 呼叫 String.valueOf(obj) → obj.toString()。
  2. 方法內 "address: " + this 觸發字串連接,編譯器轉為 new StringBuilder().append("address: ").append(this)。
  3. StringBuilder.append(Object) 再次呼叫 this.toString() → 回到第 1 步。
  4. 無基礎終止條件,棧幀疊加至 JVM 棧上限 → StackOverflowError。
  5. 正確實現三種模式

    1. 調用父類實現(最簡潔)

    @Override
    public String toString() {
        return "address: " + super.toString(); // Object.toString() 返回 "ClassName@hashCode"
    }

    2. 手動拼接業務字段(推薦)

    public class User {
        private Long id;
        private String name;
        private String email;
    
        @Override
        public String toString() {
            return "User{id=" + id + ", name='" + name + "', email='" + email + "'}";
        }
    }

    3. 使用工具類/註解生成(現代專案標配)

    // Lombok
    @Data // 含 @ToString
    public class Order {
        private Long id;
        private BigDecimal amount;
        private List items;
    }
    
    // 或 Apache Commons Lang
    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);
    }

    IDE 自動生成模板最佳化

    IntelliJ IDEA Generate → toString() 預設模板:

    public String toString() {
        return "ClassName{" +
               "field1=" + field1 +
               ", field2=" + field2 +
               '}';
    }
    • 勾選 Exclude null fields 減少輸出雜訊。
    • 勾選 Use getters 尊重封裝、觸發邏輯(如格式化)。
    • 對敏感字段(密碼、Token)在欄位加 @ToString.Exclude 或手動排除。

    進階:防禦性編程避免循環引用

    雙向關聯實體(如 Parent ↔ Child)若雙方 toString 互相調用,同樣會爆棧。

    // 解決:排除反向引用
    @Data
    @ToString(exclude = "parent")
    public class Child {
        private String name;
        private Parent parent;
    }
    
    @Data
    @ToString(exclude = "children")
    public class Parent {
        private String name;
        private List children;
    }

    總結檢查清單

    • ❌ return "..." + this;
    • ❌ return "..." + this.field.toString();(若 field 又引用 this)
    • ✅ super.toString() 獲取默認標識
    • ✅ 顯式列出業務字段
    • ✅ 使用 Lombok @ToString / @Data 統一風格
    • ✅ 雙向關聯加 exclude
    • ✅ 敏感資訊絕不輸出到 toString
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved