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

Java equals 與 == 徹底解析:基本型別、引用比較、字串常量池與 intern() 機制

     編輯:Java 程式設計入門

核心結論先行

比較對象==.equals()
基本型別 (int, double, boolean...)比較 數值不適用 (基本型別無方法)
引用型別 (物件、陣列、String)比較 記憶體位址 (是否同一實例)比較 邏輯內容 (取決於類的實現)

基本型別:== 比值,無 equals

int a = 100, b = 100;
System.out.println(a == b); // true,值相等
// a.equals(b) 編譯錯誤:int 沒有方法

引用型別:== 比地址,equals 看實現


Object o1 = new Object();
Object o2 = new Object();
Object o3 = o1;

System.out.println(o1 == o2);        // false,不同實例
System.out.println(o1 == o3);        // true,同一實例
System.out.println(o1.equals(o2));   // false,Object.equals 預設也是比地址

String 的特殊性:內容比較與常量池

String 重寫了 equals() 比較字元序列,但 == 仍比地址。

案例 1:字面量直接賦值


String s1 = "Monday";
String s2 = "Monday";
System.out.println(s1 == s2);        // true!編譯期常量池優化,指向同一對象
System.out.println(s1.equals(s2));   // true

案例 2:new String() 強制創建新對象


String s1 = "Monday";
String s2 = new String("Monday");
System.out.println(s1 == s2);        // false,s2 在堆上新建對象
System.out.println(s1.equals(s2));   // true,內容相同

案例 3:intern() 手動入池/獲取池中引用


String s1 = "Monday";
String s2 = new String("Monday");
s2 = s2.intern(); // 檢查常量池是否有 "Monday",有則返回池中引用,無則放入並返回
System.out.println(s1 == s2);        // true,s2 現在指向池中對象
System.out.println(s1.equals(s2));   // true

字串常量池機制深度解析

  • 位置:JDK 7 之前在永久代,JDK 7+ 移至堆積。
  • 入池時機:
    • 類加載時:源碼中以雙引號定義的字面量 ("abc") 自動入池。
    • 運行時調用 String.intern():若池中已有內容相同字串,返回池中引用;否則將此字串引用加入池並返回。
  • 目的:節省記憶體 (相同字串共享實例)、加速 == 比較 (特定業務場景)。

包裝類別的緩存機制 (副作用:== 有時會 true)


Integer a = 100;  // 自動裝箱 → Integer.valueOf(100)
Integer b = 100;
System.out.println(a == b); // true!-128~127 緩存

Integer c = 200;
Integer d = 200;
System.out.println(c == d); // false,超出緩存範圍新建對象

// 正確比較包裝類別
System.out.println(c.equals(d)); // true
// 或 JDK 7+ Objects.equals(c, d) (空指針安全)

緩存範圍:Byte/Short/Integer/Long 緩存 -128~127;Character 緩存 0~127;Boolean 只有兩個單例 TRUE/FALSE。可通過 -XX:AutoBoxCacheMax= 調整 Integer 緩存上限。

最佳實踐總結

  1. 基本型別:只用 ==。
  2. 引用型別比較內容:永遠用 .equals() (或 Objects.equals(a,b) 防 NPE)。
  3. 引用型別比較是否同一實例:用 == (如單例檢查、enum 比較、同步鎖對象)。
  4. String 比較:equals() / equalsIgnoreCase() / contentEquals()。嚴禁用 ==,除非你確定雙邊皆來自常量池/已 intern()。
  5. 自定義類:覆寫 equals() 時必須遵守「自反性、對稱性、傳遞性、一致性、非空性」,並同步覆寫 hashCode()。

自定義 equals 範本 (IDE 自動生成)


@Override
public boolean equals(Object o) {
    if (this == o) return true;                    // 同一實例快速返回
    if (o == null || getClass() != o.getClass()) return null; // 類型檢查
    MyClass that = (MyClass) o;
    return Objects.equals(field1, that.field1) &&
           Objects.equals(field2, that.field2) &&
           primitiveField == that.primitiveField;
}

@Override
public int hashCode() {
    return Objects.hash(field1, field2, primitiveField);
}

總結

== 是「身份識別」,equals() 是「內容平等」。String、包裝類別因常量池/緩存機制導致 == 偶爾返回 true,屬於實現細節副作用,**不可作為業務邏輯依據**。牢記「比內容用 equals,比身份用 ==」,並為自定義類正確實現 equals/hashCode 契約,是寫出健壯 Java 代碼的基礎功夫。

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