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

Java ==、equals() 與 = 深度對比:基本類型、引用類型、String 特殊性與記憶體模型圖解

     編輯:Java 綜合教學

核心結論速查表

運算符/方法類別比較內容基本類型引用類型(非 String)String
==運算符值/地址比較值是否相等比較棧內存地址(是否同一對象)比較棧內存地址(是否同一對象)
equals()Object 方法內容/邏輯相等無此方法(編譯錯誤)預設比較地址(同 ==),通常需重寫比較業務屬性已重寫,比較字串內容(字元序列)
=賦值運算符—將右值賦給左變量將右側引用地址賦給左變量(雙方指向同一對象)同引用類型

一、基本類型比較:== 與 equals()

int a = 100;
int b = 100;
System.out.println(a == b);        // true,值相等
// a.equals(b)  // 編譯錯誤:基本類型無方法

Integer i1 = 100; // 自動裝箱,-128~127 緩存池
Integer i2 = 100;
System.out.println(i1 == i2);      // true(緩存池同一對象)
System.out.println(i1.equals(i2)); // true,Integer 重寫 equals 比較值

Integer i3 = 200; // 超出緩存範圍
Integer i4 = 200;
System.out.println(i3 == i4);      // false,不同對象
System.out.println(i3.equals(i4)); // true,比較值

二、引用類型比較:== 永遠比地址

TestBean obj1 = new TestBean();
TestBean obj2 = new TestBean();
TestBean obj3 = obj1; // 引用賦值

System.out.println(obj1 == obj2); // false,堆中兩個不同對象
System.out.println(obj1 == obj3); // true,棧中引用指向同一堆地址

// 未重寫 equals 時,預設 Object.equals() 等同 ==
System.out.println(obj1.equals(obj2)); // false
System.out.println(obj1.equals(obj3)); // true

三、String 特殊性:常量池、new、intern() 與比較陷阱

1. 直接賦值(字面量)進入字串常量池

String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);        // true,常量池唯一
System.out.println(s1.equals(s2));   // true

2. new String() 強制在堆創建新對象

String s3 = new String("abc");
String s4 = new String("abc");
System.out.println(s3 == s4);        // false,堆中兩個對象
System.out.println(s3.equals(s4));   // true,內容相同

String s5 = "abc";
System.out.println(s3 == s5);        // false,堆 vs 常量池
System.out.println(s3.equals(s5));   // true

3. 引用賦值:指向同一對象

String s6 = new String("abc");
String s7 = s6; // 引用賦值
System.out.println(s6 == s7);        // true,同一引用
System.out.println(s6.equals(s7));   // true

4. intern() 手動入池/獲取池中引用

String s8 = new String("abc").intern();
String s9 = "abc";
System.out.println(s8 == s9);        // true,s8 返回常量池引用

四、記憶體模型圖解:棧、堆、方法區/常量池

+------------------+        +------------------+        +------------------+
|     Stack        |        |      Heap        |        |  Method Area     |
|  (棧幀/局部變量)  |        |  (對象實例/數組)   |        |  (類元數據/常量池) |
+------------------+        +------------------+        +------------------+
| s1  ─────────────┼───────►| "abc" (常量池)  │◄───────│ "abc" (String常量池)|
| s2  ─────────────┘        |                  |        |                  |
| s3  ─────────────────────►| "abc" (new)      |        |                  |
| s4  ─────────────────────►| "abc" (new)      |        |                  |
| s5  ─────────────────────►| "abc" (常量池)  │◄───────┘                  |
| s6  ─────────────────────►| "abc" (new)      |        |                  |
| s7  ───────┐              |                  |        |                  |
|            │ (引用賦值)   |                  |        |                  |
| s7  ───────┘              |                  |        |                  |
+------------------+        +------------------+        +------------------+

== 比較:棧中引用地址(箭頭起點)
equals 比較:堆/常量池中對象內容(箭頭終點字元序列)

五、自定義類別正確重寫 equals() 與 hashCode() 規範

public class User {
    private Long id;
    private String name;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;                    // 1. 同一引用快速返回
        if (o == null || getClass() != o.getClass()) return false; // 2. 類型檢查
        User user = (User) o;
        return Objects.equals(id, user.id) &&          // 3. 核心業務屬性比較(推薦用 Objects.equals 防 NPE)
               Objects.equals(name, user.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);                 // 4. 必須與 equals 一致,用於 HashSet/HashMap 鍵
    }
}

核心契約:a.equals(b) → a.hashCode() == b.hashCode()。違反會導致哈希容器失效(找不到元素、重複元素)。

六、常見面試陷阱題與解析

  1. String s = new String("a") + new String("b"); s.intern() == "ab" → JDK 1.6 false / JDK 1.7+ true(常量池位置遷移至堆,intern 行為變化)。
  2. Integer a = 127; Integer b = 127; a == b → true(-128~127 緩存)。
  3. Integer a = 128; Integer b = 128; a == b → false(超出緩存)。
  4. Double d1 = 1.0; Double d2 = 1.0; d1 == d2 → false(浮點數包裝類無緩存)。
  5. "a" == "a" → true(字面量常量池唯一)。

七、最佳實踐總結

  1. 基本類型用 ==,包裝類型/引用類型比較內容用 equals()。
  2. String 永遠用 equals() 比內容,== 僅用於檢查是否為同一引用(如 if (str == EMPTY_STRING))。
  3. 自定義實體類必須重寫 equals() 和 hashCode(),推薦用 IDE 自動生成或 Lombok @Data/@EqualsAndHashCode。
  4. 防空指針:用 Objects.equals(a, b) 替代 a.equals(b),或 "constant".equals(variable)(常量在前)。
  5. 集合去重/鍵值存儲依賴 equals/hashCode 契約,務必保持一致。

徹底搞懂 ==、equals()、= 的語義邊界與記憶體模型,是 Java 開發者避開「比較陷阱」、寫出健壯代碼的必修課。建議將本文對比表與記憶體圖解收藏,遇到比較相關 Bug 時快速定位根因。

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