簡體   English   中英

關於java中==運算符的問題

[英]Question about == operator in java

public class Demo {  

    public static void main(String[] args) {

        String s1 = "Hello";
        String s2 = "Hello";
        System.out.println("s1 == s2 " + (s1 == s2));

        String s5 = "Hel" + "lo";
        String s6 = "He" + "llo";
        System.out.println("s5 == s6 " + (s5 == s6));

        String s7 = "He";
        String s8 = "Hello";
        s7 = s7.concat("llo");
        System.out.println("s7 == s8 " + (s7 == s8));

        String s10 = "He";
        s10 = s10 + "llo";
        System.out.println("s1 == s10 "+(s1 == s10));
    }
}

在前面的代碼中,s7 == s8和s1 == s10給出false。 有人可以解釋一下,在s7 = s7.concat(“llo”)中實際發生了什么; 並且s10 = s10 +“llo”; 我理解==運算符檢查引用和equal()檢查對象的內容。 但我需要知道為什么s7和s10參考變量位模式與s8和s1不同。 如果這些事情與編譯時生成的字符串和運行時生成的字符串有關,那么我如何識別它是編譯時還是運行時字符串?

之所以發生這種情況,是因為Java在編譯器中進行了優化。 當它看到你將文字字符串"Hello"分配給s1時,它對s2使用相同的“Hello”,因為所有Java String操作都是非破壞性的(例如它們返回克隆而不是修改原始字符串),所以這是一件安全的事情。

同樣的事情是"Hel" + "lo" vs "He" + "llo" ; 它足夠聰明地弄清楚它們是同一個東西。

其他的很復雜,無法對它們進行優化,因此您最終會得到單獨的對象。

==不檢查位模式,它將比較對象的內存地址。 只有同一個對象具有相同的內存地址。

克林特的答案很好,但我會進一步擴展它並在編譯器級解釋。

如您所知, s1s2將最終成為對同一字符串實例"Hello"引用。

對於s5s6 ,編譯器會看到常量表達式。 也就是說,它看到兩個常量(字符串文字)之間的操作。 編譯器知道如何添加字符串以及結果是什么。 由於這些值在編譯時立即得知,因此它會為您添加,從而產生文字字符串"Hello" 因此,它具有與s1s2相同的值,因此每個都將引用相同的實例。

s7不能以同樣的方式簡化。 s7最初以"He"開頭。 這里的區別是s7 = s7.concat("llo"); s7重新分配給函數調用的結果。 這不能簡化。 就Java編譯器而言,所有函數調用的結果在編譯時都是未知的。 由於它不知道結果值,因此不能簡化並保持不變。 結果調用返回"Hello"字符串的新實例,該字符串與編譯時實例( s8共享)的實例不同。

s10也不能以同樣的方式簡化。 s10最初以"He"開頭。 然后重新分配s10 = s10 + "llo"; 這無法簡化。 你可能會問為什么? 那么s10是非最終變量表達式。 從技術上講,編譯器不知道它的值,因為它不是常量。 如果s10被聲明為final String ,那么這可以是常量折疊(當分配給不同的變量時)。

因此,請考慮此版本的測試代碼:

public static void main(String[] args)
{
    String s1 = "Hello";
    String s2 = "Hello";
    System.out.println("1: s1 == s2 " + (s1 == s2));    // 1

    String s3 = "Hel" + "lo";
    String s4 = "Hel".concat("lo");
    System.out.println("2: s1 == s3 " + (s1 == s3));    // 2
    System.out.println("3: s1 == s4 " + (s1 == s4));    // 3

    String he = "He";
    String s5 = he + "llo";
    String s6 = he.concat("llo");
    System.out.println("4: s1 == s5 " + (s1 == s5));    // 4
    System.out.println("5: s1 == s6 " + (s1 == s6));    // 5

    final String fhe = "He";
    String s7 = fhe + "llo";
    String s8 = fhe.concat("llo");
    System.out.println("6: s1 == s7 " + (s1 == s7));    // 6
    System.out.println("7: s1 == s8 " + (s1 == s8));    // 7
}

你能弄清楚哪條線是真的嗎?

true,true,false,false,false,true,false
你可能想知道為什么3和7不是真的。 簡而言之,Java編譯器沒有編程
要足夠智能以識別concat()調用,因此將其視為常規函數調用。

equals運算符測試引用是否相同(即指向同一對象),而不是引用的值是否相同。 如果您需要測試一個字符串是否等於另一個字符串,則應使用內置的.equals方法。 這將進行對象值比較。 例如

final String expectedValue = "Foo";
final String actualValue = "F" + "oo";
if (expectedValue.equals(actualValue)) {
  // This will trigger where == would not
}

另外為了安全起見,如果你比較兩個字符串而另一個是常量,通常最好在常量上調用equals,即

String myValue = getMyValue;
boolean theSame = "Some value".equals(myValue);

而不是

String myValue = getMyValue;
boolean theSame = myValue.equals("Some Value");

原因是第二種形式存在空指針異常的風險,可以通過在保證存在的常量字符串上調用equals()來避免這種情況。

你不能對字符串對象做任何假設。

VM可以努力確保不會同時存在包含完全相同的char數組的兩個字符串對象,而其他VM將允許重復。

==運算符僅檢查兩個對象是否具有相同的地址(指針)。 僅對於不是引用的原始類型(如int,char等),它才會比較該值。

您需要使用類似s1.equals(s2)的內容來比較兩個字符串的內容。

在您提供的示例中,這是正在發生的事情:

String s7 = “He”;    //s7 is an object referencing a part of memory holding “He”
String s8 = “Hello”;   //s8 is an object referencing a part of memory holding “Hello”
s7 = s7.concat(“llo”); //s7.concat(“llo”) created a new object in memory that contains “Hello” and s7 now references this now object 

(s7==s8)             //checks if the equality of the object reference and this is false since they reference different memory addresses.

(s7.equals(s8))         //this will compare s7 and s8 however way the String class compares two String objects. 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM