深入探究Java中equals()和==的區別是什麼

炭燒生蠔發表於2019-05-20

相等判斷符"=="介紹

 

"=="相等判斷符用於比較基本資料型別和引用型別資料. 當比較基本資料型別的時候比較的是數值, 當比較引用型別資料時比較的是引用(指標).

 

"=="判斷基本型別是否相等.

  • 首先基本資料型別指的是Java中的八大資料型別: byte, short, int, long, float, double, char, boolean
  • 這八大基本資料型別有個共同的特點是它們在記憶體中是有具體值的, 比如說一個int型別的資料"2", 它在8位資料匯流排的機器上(假設的)儲存形式為0000 0010.
  • 當使用"=="比較兩個基本資料型別的時候, 就是比較它們各自在記憶體中的值.

 

"=="判斷引用型別資料是否相等

  • 引用資料型別在字面上也是很好理解的, 就是一個引用, 它會指向一個具體的物件.
  • 比如說Student stu = new Student();, 這裡的stu就是一個引用, 它指向的是當前new出來的Student物件. 當我們想要操作這個Student物件時, 只需要操作引用即可, 比如說int age = stu.getAge();.
  • 所以用"=="判斷兩個引用資料型別是否相等的時候, 實際上是在判斷兩個引用是否指向同一個物件.
  • 看下面的示例
public static void main(String[] args) {
    String s1 = "hello";    //s1指向常量池中的"hello"字串
    String s2 = "hello";    //s2也指向常量池中的"hello"字串
    System.out.println(s1 == s2);   //true

    String s3 = new String("hello");   //s3指向的是堆記憶體中的字串物件 
    System.out.println(s1 == s3);   //false
}
  • 從上面的例子可以看到, 由於引用"s1"和"s2"指向的都是常量池中的"hello"字串, 所以返回true.
  • 而"s3"指向的是新建立字串物件, 因為只要動用了new關鍵字, 就會在堆記憶體建立一個新的物件,
  • 也就是說s1和s3指向的是不同的字串物件, 所以返回false.

 

判斷是否相等-equals()方法介紹.

equals()和==有著本質的區別, ==可以看作是對作業系統比較資料手段的封裝, 而equals()則是每個物件自帶的比較方法.

  • equals()和==的本質區別更通俗的說法是, ==的比較規則是定死的, 如上面所述; 而equals()的比較規則是不固定的, 可以由使用者自己定義.
  • 看下面的例子:
public static void main(String[] args) {
    String s1 = "hello";
    String s3 = new String("hello");    
    System.out.println(s1.equals(s3));  //true
}
  • 在用==比較的時候, 上面s1和s3比較出的結果為false. 而當用equals比較的時候, 得出的結果為true.
  • 想知道原因我們還得看原始碼, 下面是String類的equals()原始碼.
public boolean equals(Object anObject) {
    if (this == anObject) { //先比較兩個字串的引用是否相等(是否指向同一個物件), 是直接返回true
        return true;
    }
    if (anObject instanceof String) {   //兩個引用不等還會繼續比較
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;  //字串類是用字元陣列實現的, 先要拿到兩個字串的字元陣列
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {  //然後對兩個陣列逐個字元地進行比較
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}
  • 從上面的原始碼可以看到, 當呼叫String型別的equals()方法時, 首先會判斷兩個字串的引用是否相等, 也就是說兩個字串引用是否指向同一個物件, 是則返回true.
  • 如果不是指向同一個物件, 則把兩個字串中的字元挨個進行比較. 由於s1和s3字串都是"hello", 是可以匹配成功的, 所以最終返回true.

 

深入探究equals(), 為什麼會有equals()方法?

  • 通過上面的講解相信你已經知道==和equals()的區別, 一個的比較規則是定死的, 一個是可以由程式設計人員自己定義的.
  • 可是為什麼會有equals()方法, 而且還可以被自由定製呢?
  • 這個問題要落到Java語言的核心--物件導向思想了. Java不同於程式導向的C語言, Java是一款物件導向的高階語言. 如果只是程式導向, 直接操作記憶體上儲存的資料的話, 用==所定義的規則來判斷兩個資料是否相等已經足夠了.
  • 而Java中處處是物件, 我們經常要面對的問題是這兩個物件是否相等, 而不是這兩串二進位制數是否相等, 僅有"=="是完全不夠用的.
  • 考慮到程式設計人員會使用Java建立各種滿足它們業務需求的物件, 系統無法提前知道兩個物件在什麼條件下算相等, Java乾脆把判斷物件是否相等的權力交給程式設計人員.
     
  • 具體的措施是: 所有的類都必須繼承Object類, 而Object類中寫有equals()方法. 程式設計人員可以通過重寫equals()方法實現自己的比較策略, 也可以不重寫, 使用Object類的equals()比較策略.
//Object類中的equals()方法原始碼
public boolean equals(Object obj) {
    return (this == obj);
}
  • 從Object類的equals()原始碼可以看到, 如果程式設計人員沒有顯示地重寫equals()方法, 則該類物件預設通過引用資料型別進行比較, 也就是說比較兩個引用是否指向同一個物件.

 

補充: 關於基本資料型別包裝類的比較

  • 由於Java中萬物皆物件, 就連基本資料型別也有其對應的包裝物件, 那麼它們對應的比較策略是什麼呢?
public static void main(String[] args) {
    int a = 3;
    Integer b = new Integer(3);
    System.out.println(b.equals(a));    //true, 自動裝箱
}
  • 從上面的程式碼可以看到儘管兩個引用不同, 但是輸出的結果仍為true, 證明Integer包裝類重寫了equals()方法.
//Integer類中的equals方法
public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
}
  • 從原始碼看到, 基本型別包裝類在重寫的equals方法中, 比較的還是基本資料型別的值.

 

  • 最後歡迎關注我的免費知識星球, 我會在星球中持續更新系統的Java後端面試題分析, 將會囊括Java基礎知識到主流框架原理. 還會分享關於程式設計的趣味漫畫.
深入探究Java中equals()和==的區別是什麼

相關文章