String用==和equals兩種方式比較的區別

文章目錄

==

面試的時候經常會遇到讓你說明==equals的區別,通常會拿String類做舉例,比如下面代碼:

public class Test {
    public static void main(String[] args) {
        String s1="hello world";
        String s2="hello world";
        System.out.println(s1 == s2);
    }
}

輸出結果爲:true
但是如果是下面的代碼:

public class Test {
    public static void main(String[] args) {
        String s3=new String("hello world");
        String s4=new String("hello world");
        System.out.println(s3 == s4);
    }
}

輸出結果爲:false
爲什麼同樣的字符串值,一個是直接賦值給String,一個是通過new實例,2者比較卻截然不同。接下來我們先說下==的結論,然後我們再證明,結論如下:

結論一:比較兩個值或對象指向的地址值是否相同。
結論二:如果是基本數據類型,則直接比較其值是否相等。

我們看看結論一:先利用JClassLib工具查看class文件字節碼指令信息,從以下圖看出,紅圈部分就是在使用==比較判斷用的字節碼指令
在這裏插入圖片描述
if_acmpne是什麼意思呢?點擊 if_acmp 可以查看它的介紹,總的來說就是if_acmp就是比較2個的地址是否相等,它是Java虛擬機的一個字節碼指令,因此在Java虛擬機裏對==的這種比較是採用它們指向的地址是否相同,那怎麼知道2個值或對象指向的地址是否相同呢?由於Java沒有直接獲取自己的地址的方法,這邊可以用寫好的 AddressUtil.java 這個工具類可以拿到對象地址,代碼如下:

public class Test {
    public static void main(String[] args) {
        String s1="hello world";
        String s2="hello world";

        AddressUtil.printAddresses("s1的地址",s1);
        AddressUtil.printAddresses("s2的地址",s2);
        System.out.println(s1 == s2);
    }
}

輸出結果爲:

s1的地址: 0x76b18c570
s2的地址: 0x76b18c570
true

可以看到s1地址和s2的地址是相同的,因此符合s1 == s2比較的結果是true。接下來看下面的代碼:

public class Test {
    public static void main(String[] args) {
        String s3=new String("hello world");
        String s4=new String("hello world");

        AddressUtil.printAddresses("s3的地址",s3);
        AddressUtil.printAddresses("s4的地址",s4);
        System.out.println(s3 == s4);

    }
}

輸出的結果爲:

s3的地址: 0x76b18c5d0
s4的地址: 0x76b18c628
false

可以看到s3 的地址和s4的地址是不一樣的,因此符合s3 == s4比較的結果是false那麼思考一下,爲什麼s3和s4的地址不相同呢?

我們再來看看結論二:如果是基本數據類型,則直接比較其存儲的 “值”是否相等。我們看下面代碼:

public class Test {
    public static void main(String[] args) {
        int a=1;
        int b=1;
        System.out.println(a==b);
    }
}

輸出結果爲true,這裏a和b都是基本數據類型,我們來看看它是怎麼直接比較存儲值是否相等的 ,同樣用JClassLib工具查看class文件字節碼指令信息,如下圖:
在這裏插入圖片描述
看到紅圈部分if_icmpne在官網有解釋,點擊if_icmpne可以查看,總的倆說if_icmpne指令就是用來比較int值是否相等的,依此類推,你能證明float、double、long、char等等基本數據類型也是比較存儲值是否相等嗎?

equals

接下來看equals方法,代碼如下:

public class Test {
    public static void main(String[] args) {
        String s1="hello world";
        String s2="hello world";
        System.out.println(s1.equals(s2));

    }
}

輸出結果爲true
再看下面代碼:

public class Test {
    public static void main(String[] args) {
        String s3=new String("hello world");
        String s4=new String("hello world");
        System.out.println(s3.equals(s4));

    }
}

輸出結果爲true

從上面看出,不管是直接賦值給String的還是new實例出來的,equals比較都是true,那equals是比較地址的值還是比較的是內容呢?先說下equals的結論:

結論一:默認比較的是兩個值或對象指向的地址值是否相同
結論二:String類重寫了equals()方法,比較的是內容是否相同

來看看結論一,首先都知道Object是所有類的父類,任何類都默認繼承Object,而String的equals方法重寫的是Object類equals方法:java.lang.Object#equals,來看看Object類裏equals方法的代碼是怎麼實現的:

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

居然默認使用了==比較,前面說了==比較是比較的是地址是否相同,因此結論一是對的。
然後再來看結論二,String類重寫了equals()方法,來看看java.lang.String#equals的代碼:

    public boolean equals(Object anObject) {
        if (this == anObject) {
            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方法代碼邏輯是比較字符串內容的,依此類推Integer、Double、Float對象是不是也是重寫equals了方法。

總結

  1. ==和equals比較的是兩個值或對象指向的地址值是否相同
  2. 如果比較內容是否相同需要重寫equals方法,在重寫的方法實現比較內容的邏輯,比如String、Integer類就是重寫equals了方法

延伸:對象作爲 map 的 key 時,爲什麼需要重寫 equals 方法和 hashCode 方法?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章