Java中==和equals的區別

Java程序中測試兩個變量是否相等有兩種方式:一種是利用==運算符,另一種是利用equals()方法的功能。當使用==來判斷兩個變量是否相等時,如果兩個變量是基本類型,且都是數值類型(不一定要求數據類型嚴格相同),則只要兩個變量的值相等,就將返回true。但對於兩個引用類型變量,只有它們指向同一個對象時,==判斷纔會返回true。==不可用於比較類型上沒有父子關係的兩個對象。

/**
 * @Author: 浮雲
 * @Date: 2020/1/1 19:44
 */
public class EqualsTest {
    public static void main(String[] args) {
        int i = 66;
        float f1 = 66.0f;
        System.out.println("66和66.0f是否相等?" + (i == f1));// true
        char ch = 'B';
        System.out.println("66和'B'是否相等?" + (i == ch));// true
        String str1 = new String("hello");
        String str2 = new String("hello");
        System.out.println("str1和str2是否相等?" + (str1 == str2));// false
        System.out.println("str1 是否equals str2?" + str1.equals(str2));// true
        // 由於java.lang.String與EqualsTest類沒有繼承關係,所以下面語句導致編譯錯誤
        //System.out.println("hello" == new EqualsTest());
    }
}

從上面程序運行結果可以看出,66、66.0f和’B’相等,但str1和str2是通過new關鍵字創建的String對象(引用數據類型),因此str1和str2兩個變量不相等。
思考: "hello"直接量和new String(“hello”)有什麼區別?
當Java程序直接使用形如"hello"的字符串直接量(包括在編譯時就可以計算出來的字符串值)時,JVM將會使用常量池管理這些字符串,當使用new String(“hello”)時,JVM會先使用常量池來管理"hello"直接量,再調用String類的構造器來創建一個新的String對象,新的String對象被保存再堆內存中。換句話說,new String(“hello”)一共產生了兩個字符串對象。
解釋: 常量池(constant pool)專門用於管理在編譯時被確定並保存在已編譯的.class文件中的一些數據。包括了關於類、方法、接口中的常量,也包括字符串常量。
以下程序演示JVM使用常量池管理字符串直接量的情況

/**
 * @Author: 浮雲
 * @Date: 2020/1/1 20:17
 */
public class StringCompareTest {
    public static void main(String[] args) {
        // s1直接引用常量池的"浮雲博客"
        String s1 = "浮雲博客";
        String s2 = "浮雲";
        String s3 = "博客";
        // s4後面的字符串值可以在編譯時就被確定下來,所以直接引用常量池中的"浮雲博客"
        String s4 = "浮雲" + "博客";
        // s5和s4一樣在編譯時就能確定下來,直接引用常量池中的"浮雲博客"
        String s5 = "浮雲" + "博" + "客";
        // s6後面的字符串不能在編譯時就確定下來,所以不能引用常量池中的字符串
        String s6 = s2 + s3;
        // 使用new調用構造器會創建一個新的String對象,s7引用堆內存中新創建的String對象
        String s7 = new String("浮雲博客");
        System.out.println(s1 == s4); // true
        System.out.println(s1 == s5); // true
        System.out.println(s1 == s6); // false
        System.out.println(s1 == s7); //false
    }
}

JVM常量池保證相同的字符串直接量只有一個,不會產生多個副本。以上程序中的s1、s4、s5所引用的字符串都可以在編譯時就確定下來,因此它們都將在引用常量池中的同一哥字符串對象。使用new String()創建的字符串對象是運行時創建出來的,它被保存在運行時內存中(即堆內存),不會放入常量池中。
equals()方法是Object類提供的一個實例方法,因此所有引用變量都可以調用該方法來判斷是否與其他引用變量相等。但使用這個方法判斷兩個對象相等的便準和使用==運算符沒有區別,同樣要求兩個引用變量指向同一個對象才返回true。因此Object類提供的equals()方法沒有太大的實際意義,如果希望採用自定義的相等標準,則可採用重寫equals()方法來實現。
注: String已經重寫了Object的equals()方法,String的equals()方法判斷兩個字符串相等的標準是:只要兩個字符串所包含的字符序列相同,通過equals()比較將返回true,否則返回false。

import java.util.Objects;

/**
 * @Author: 浮雲
 * @Date: 2020/1/1 20:39
 */
class Person {
    private String name;
    private String idStr;
    public Person(){}
    public Person(String name, String idStr){
        this.name = name;
        this.idStr = idStr;
    }

    public String getName() {
        return name;
    }

    public String getIdStr() {
        return idStr;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setIdStr(String idStr) {
        this.idStr = idStr;
    }

    @Override
    public boolean equals(Object o) {
        // 如果兩個對象爲同一個對象
        if (this == o) return true;
        // 只有當o是Person對象
        if(o != null && o.getClass() == Person.class){
            Person person = (Person) o;
            // 並且當前對象的idStr與o對象的idStr相等時纔可以判斷兩個對象相等
            if(this.getIdStr().equals(person.getIdStr())){
                return true;
            }
        }
        return false;
    }
}

public class OverrideEqualsTest{
    public static void main(String[] args) {
        Person p1 = new Person("浮雲", "123456");
        Person p2 = new Person("fuyun", "123456");
        Person p3 = new Person("藍天白雲", "321456");
        System.out.println("p1和p2是否相等?" + (p1.equals(p2))); // 因爲idStr相等,所以爲true
        System.out.println("p1和p3是否相等?" + (p1.equals(p3))); // 因爲idStr不相等,所以爲false
    }
}

通常而言,正確地重寫equals()方法應沒滿足下列條件:

  • 自反性:對任意的x,x.equals(x)一定返回true
  • 對稱性:對任意x和y,如果y.equals(x)返回true,則x.equals(y)也返回true
  • 傳遞性:對任意x,y,z,如果x.equals(y)返回true,y.equals(z)返回true,則x.equals(z)一定返回true
  • 一致性:對任意x,y,如果對象中用於等價比較的信息沒有改變,那麼無論調用x.equals(y)多少次,返回的結果應該保持一致,要麼一直爲true,要麼一直爲false
  • 對任何不是null的x,x.equals(null)一定返回false

Object默認提供的equals()方法只是比較對象的地址,即Object類的equals()方法比較的結果與==運算符比較的結果完全相同。因此,實際應用中常常要重寫equals()方法,重寫equals()方式時,相等條件是由業務要求決定的,因此,equals()方法的實現也是由業務要求決定的。

發佈了100 篇原創文章 · 獲贊 46 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章