【Java】equals 和 == 的區別

RioTian發表於2021-06-23

之前有在 Java字串比較(3種方法)以及對比 C++ 時的注意項 中寫過一點關於 equals()與==的比較,但最近的Java考試複習過程中發現有部分情況沒涉及到,故重新學習一下


在編寫程式碼的時候我們經常會使用 equals== 來判斷兩個物件是否相等,那麼兩者有什麼區別呢,主要有以下幾點區別:

  1. 首先的區別是,equals 是方法,而 == 是操作符;

  2. 對於基本型別的變數來說(如 shortintlongfloatdouble),只能使用 == ,因為這些基本型別的變數沒有 equals 方法。對於基本型別變數的比較,使用 == 比較, 一般比較的是它們的值

  3. 對於引用型別的變數來說(例如 String 類)才有 equals 方法,因為 String 繼承了 Object 類, equals 是 Object 類的通用方法。對於該型別物件的比較,預設情況下,也就是沒有複寫 Object 類的 equals 方法,使用 == 和 equals 比較是一樣效果的,都是比較的是它們在記憶體中的存放地址。但是對於某些類來說,為了滿足自身業務需求,可能存在 equals 方法被複寫的情況,這時使用 equals 方法比較需要看具體的情況,例如 String 類,使用 equals 方法會比較它們的值

對於上述第三點理解起來可能有點複雜,因為這裡 equals 方法比較需要分兩種情況來討論,一種情況是該方法沒有被重寫,另外一種是該方法被重寫。

  • 對於 equals 方法沒有被重寫的情況。如果類沒有重寫該方法,那麼預設使用的就是 Object 類的方法,以下是 Object 類的 equals 方法:

      public boolean equals(Object obj) {
          return (this == obj);
      }
    

從原始碼可以看出,裡面使用的就是 == 比較,所以這種情況下比較的就是它們在記憶體中的存放地址。

  • 對於 equals 方法被重寫的情況。以 String 類為例,以下是 String 類中的 equals 方法:

      @Override public boolean equals(Object other) {
      if (other == this) {
        return true;
      }
      if (other instanceof String) {
          String s = (String)other;
          int count = this.count;
          if (s.count != count) {
              return false;
          }
          if (hashCode() != s.hashCode()) {
              return false;
          }
          char[] value1 = value;
          int offset1 = offset;
          char[] value2 = s.value;
          int offset2 = s.offset;
          for (int end = offset1 + count; offset1 < end; ) {
              if (value1[offset1] != value2[offset2]) {
                  return false;
              }
              offset1++;
              offset2++;
          }
          return true;
      } else {
          return false;
      }
    }
    

從原始碼可以看出, String 類複寫了 equals 方法,當使用 == 比較記憶體的存放地址不相等時,接下來會比較字串的內容是否 相等,所以 String 類中的 equals 方法會比較兩者的字串內容是否一樣。我們來看看下面的例子:

    String a = "Hello World";
    String b = new String("Hello World");
    String c = b; //引用傳遞
    System.out.println("a == b:" + a == b);             //false
    System.out.println("b == c:" + b == c);             //true
    System.out.println("a == c:" + a == c);             //false
    System.out.println("a.equals(b):" + a.equals(b));   //true
    System.out.println("b.equals(c):" + b.equals(c));   //true
    System.out.println("a.equals(c):" + a.equals(c));   //true

    最終的列印會是:
    a == b:false
    b == c:true
    a == c:false
    a.equals(b):true
    b.equals(c):true
    a.equals(c):true

因為 String b 通過 new 的方式已經開闢了新的堆記憶體,而 String a = "Hello World" 是存放在常量池裡的,兩者在 Java 記憶體裡存在放的位置是不同的,所以 a == b 為 false;而 equals 方法當兩者存放的記憶體地址不同時,會比較兩者的值,兩者的值都是 "Hello World" ,所以 a.equals(b) 為 true。


另外請思考一下下方程式碼的執行結果為什麼是 True、false

public class Java_epuals {
    public static void main(String[] args) {
        Integer i2 = 10;
        Integer i3 = 10;
        System.out.println(i2 == i3); // true

        Integer i4 = 128;
        Integer i5 = 128;
        System.out.println(i4 == i5); // false
    }
}

對於 i2i3 的比較,因為變數的定義方法是 i2(i3) = 10 它們的變數會置於常量區,兩個變數的記憶體地址相同。

此時 == 返回 true

那麼為什麼 i4i5 是返回 false 呢?

這是因為 Java 中 integer 範圍取值要在-128到+127 (為什麼Integer物件範圍(-128-127)之間),而我們賦的值是 128 ,此時變數並不在常量區定義。所以兩個變數的記憶體地址不同,== 返回 false

相關文章