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

Java 初學者必懂的三大核心觀念:引用變數、== vs equals、String 不變性

     編輯:Java 相關知識

許多自以為「入門了」的開發者,在這三個基礎問題上仍會翻車。面試必考、實務常見 Bug 來源,徹底搞懂才能寫出正確、高效的 Java 代碼。

問題一:我到底宣告了什麼?

String s = "Hello world!";

錯誤認知:「宣告了一個內容為 Hello world 的 String 物件。」
正確認知: 宣告了一個引用變數 s,型別為 String,目前指向常量池中的 "Hello world!" 物件。

關鍵區別

  • String s:引用變數(棧上),佔 4/8 bytes,存放記憶體地址。
  • "Hello world!":String 物件(堆/常量池),內容不可變。
  • String s2 = s;:複製的是引用值(地址),兩個變數指向同一個物件,堆上不會產生新物件。

記憶體圖解

Stack:                    Heap / String Pool:
+-----+                   +------------------+
|  s  | ────────────────► | "Hello world!"   |  ← 同一物件
+-----+                   +------------------+
+-----+
| s2  | ────────────────►
+-----+

問題二:== vs equals 根本差異

== 比較的是「引用值(記憶體地址)」


int a = 10, b = 10;
a == b  // true:基本型別比較值

String a = new String("foo");
String b = new String("foo");
a == b  // false:兩個 new 產生兩個不同物件,地址不同

equals 比較的是「物件邏輯內容」


// Object 預設實現
public boolean equals(Object o) {
    return this == o;  // 同樣比較地址!
}

結論: 若類別未覆寫 equals(),呼叫 equals() 效果等同 ==。

String 覆寫了 equals


String a = new String("foo");
String b = new String("foo");
a.equals(b)  // true:比較字元序列內容

自定義類別必須覆寫 equals + hashCode


class Monster {
    private String content;
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Monster)) return false;
        Monster m = (Monster) o;
        return Objects.equals(content, m.content);
    }
    @Override
    public int hashCode() {
        return Objects.hash(content);
    }
}

鐵律: 覆寫 equals 必須覆寫 hashCode,否則無法用於 HashSet/HashMap/HashTable。

問題三:String 到底變了沒有?


String s = "Hello";
s = s + " world!";

答案:原物件沒變,s 指向新物件。

過程拆解

  1. s 指向常量池 "Hello"。
  2. s + " world!" 編譯器優化為 new StringBuilder().append("Hello").append(" world!").toString(),產生新 String 物件 "Hello world!"。
  3. s 重新指向新物件,舊 "Hello" 仍在常量池(若無其他引用最終被 GC)。

效能陷阱與解法

場景錯誤寫法正確寫法原因
循環拼接for() s += x;StringBuilder sb = new StringBuilder(); for() sb.append(x);每次 += 產生新 String + StringBuilder,O(n²) 分配
常量初始化s = new String("Init");s = "Init";new 總產生新物件;字面量共用常量池
頻繁修改用 String用 StringBuffer/StringBuilder可變字元陣列,就地修改,零分配

String 不可變的設計考量

  • 執行緒安全:不可變物件天然可共享,無同步開銷。
  • 常量池複用:相同字面量只存一份,節省記憶體。
  • 安全性:網路/檔案路徑、類別載入鍵值不可被竄改。
  • 代價:每次修改需新物件,大量拼接效能差 → 提供 StringBuilder (非同步) / StringBuffer (同步) 互補。

總結速記卡

觀念核心口訣實務鐵律
引用 vs 物件變數存地址,物件在堆上參數傳遞是「值傳遞」(傳引用的值)
== vs equals== 比地址,equals 比內容(若覆寫)自定義類用於 Set/Map 必覆寫 equals+hashCode
String 不可變改內容 = 換物件拼接用 StringBuilder,常量用字面量

自我檢測

  1. String a = "x"; String b = "x"; a == b ? → true (常量池複用)
  2. String a = new String("x"); String b = new String("x"); a == b ? → false
  3. String a = "x"; a += "y"; 原 "x" 物件會被修改嗎? → 不會
  4. 自定類別未覆寫 equals,放入 HashSet 會怎樣? → 以地址判斷相等,邏輯相同內容會被視為不同元素

全對才算真正搞懂 Java 基礎三大觀念!

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved