CodeGym /Java Blog /Toto sisi /比較 Java 中的字符串和等於比較
John Squirrels
等級 41
San Francisco

比較 Java 中的字符串和等於比較

在 Toto sisi 群組發布
你好!今天我們要聊一個很重要也很有趣的話題,就是對象與對象的比較(Compare Strings and Equals)。那麼在 Java 中,對象A到底什麼時候等於對象B呢?讓我們試著寫一個例子:

public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {
      
       Car car1 = new Car();
       car1.model = "Ferrari";
       car1.maxSpeed = 300;

       Car car2 = new Car();
       car2.model = "Ferrari";
       car2.maxSpeed = 300;

       System.out.println(car1 == car2);
   }
}
控制台輸出: false 等待,停止。為什麼這兩輛車不平等?我們為它們分配了相同的屬性,但比較的結果是錯誤的。答案很簡單。==運算符比較對象引用,而不是對象屬性。兩個對象甚至可以有 500 個具有相同值的字段,但比較它們仍然會產生 false。畢竟,引用car1car2指向兩個不同的對象,即指向兩個不同的地址。想像一下你在比較人的情況。當然,在世界的某個地方,有一個人的名字、眼睛顏色、年齡、身高、頭髮顏色等與你相同。這讓你們在很多方面都很相似,但你們仍然不是雙胞胎——而且你們顯然不是同一個人。
等於和字符串比較 - 2
當我們使用==運算 符比較兩個對象時,它使用的邏輯大致相同。但是,如果您需要您的程序使用不同的邏輯怎麼辦?例如,假設您的程序執行 DNA 分析。它比較兩個人的遺傳密碼,並確定他們是否是雙胞胎。

public class Man {

   int geneticCode;

   public static void main(String[] args) {

       Man man1 = new Man();
       man1.geneticCode = 1111222233;

       Man man2 = new Man();
       man2.geneticCode = 1111222233;

       System.out.println(man1 == man2);
   }
}
控制台輸出: false 我們得到了相同的邏輯結果(因為我們沒有做太多改變),但是現在邏輯不好了!畢竟,在現實生活中,DNA 分析應該給我們 100% 的保證,我們有雙胞胎站在我們面前。但是我們的程序和==運算符告訴我們相反的情況。我們如何改變這種行為並確保程序在 DNA 匹配時輸出正確的結果?Java 對此有一個特殊的方法:equals()與我們之前討論的toString()方法一樣,equals()屬於Object類——Java 中最重要的類,所有其他類都從該類派生。但是等於()不會單獨改變我們程序的行為:

public class Man {

   String geneticCode;

   public static void main(String[] args) {

       Man man1 = new Man();
       man1.geneticCode = "111122223333";

       Man man2 = new Man();
       man2.geneticCode = "111122223333";

       System.out.println(man1.equals(man2));
   }
}
控制台輸出: false 完全一樣的結果,那我們要這個方法幹什麼?:/ 這很簡單。這裡的問題是我們目前正在使用這個方法,因為它是在Object類中實現的。如果我們進入Object類的代碼並查看方法的實現,這就是我們將看到的:

public boolean equals(Object obj) {
   return (this == obj);
}
這就是為什麼程序的行為沒有改變的原因!在Object類的equals()方法中使用了完全相同的==運算符(比較引用) 。但是這個方法的訣竅是我們可以覆蓋它。覆蓋意味著在我們的Man類中編寫您自己的equals()方法,為它提供我們需要的行為!目前,我們不喜歡man1.equals(man2)本質上等同於man1 == man2的事實。這是我們在這種情況下要做的事情:

public class Man { 

   int dnaCode; 

   public boolean equals(Man man) { 
       return this.dnaCode ==  man.dnaCode; 

   } 

   public static void main(String[] args) { 

       Man man1 = new Man(); 
       man1.dnaCode = 1111222233; 

       Man man2 = new Man(); 
       man2.dnaCode = 1111222233; 

       System.out.println(man1.equals(man2)); 

   } 
} 
控制台輸出: true 現在我們得到一個完全不同的結果!通過編寫我們自己的equals()方法並使用它代替標準方法,我們產生了正確的行為:現在,如果兩個人具有相同的 DNA,程序將報告“DNA 分析證明他們是雙胞胎”並返回 true!通過覆蓋類中的equals()方法,您可以輕鬆創建所需的任何對像比較邏輯。事實上,我們只是剛剛談到了對像比較。在我們前面,還有一個關於這個主題的重要獨立課程(如果您有興趣,現在可以瀏覽一下)。

在 Java 中比較字符串

為什麼我們要將字符串比較與其他一切分開考慮?事實上,字符串本身就是編程中的一個主題。首先,如果你把所有曾經寫過的 Java 程序都拿來,你會發現其中大約 25% 的對像是字符串。所以這個話題非常重要。其次,比較字符串的過程確實與其他對像有很大不同。考慮一個簡單的例子:

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = new String("CodeGym is the best website for learning Java!");
       System.out.println(s1 == s2);
   }
}
控制台輸出: false 但為什麼我們得到 false?畢竟,字符串是完全一樣的,逐字逐句:/ 你可能已經猜到原因了:這是因為==運算符比較引用!顯然,s1s2在內存中的地址不同。如果你想到了這一點,那麼讓我們重寫我們的例子:

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = "CodeGym is the best website for learning Java!";
       System.out.println(s1 == s2);
   }
}
現在我們又得到了兩個引用,但結果卻恰恰相反: 控制台輸出: 無奈困惑?讓我們弄清楚發生了什麼。==運算符確實比較內存地址。這始終是正確的,您無需懷疑。這意味著如果s1 == s2返回 true,則這兩個字符串具有相同的地址。確實這是真的!是時候給大家介紹一個專門存放字符串的內存區域了:字符串池
等於和字符串比較 - 3
字符串池是一個區域,用於存儲您在程序中創建的所有字符串值。為什麼創建它?正如我們之前所說,字符串在所有對像中佔很大比例。任何大型程序都會創建大量字符串。創建字符串池是為了節省內存:將字符串放在那裡,隨後創建的字符串引用同一塊內存區域——不需要每次都分配額外的內存。每次你寫String = "......."程序都會檢查字符串池中是否有相同的字符串。如果存在,則不會創建新字符串。並且新引用將指向字符串池中的相同地址(相同字符串所在的位置)。所以當我們寫

String s1 = "CodeGym is the best website for learning Java!";
String s2 = "CodeGym is the best website for learning Java!";
s2指向與s1相同的位置。第一條語句在字符串池中創建一個新字符串。第二條語句只是引用與s1相同的內存區域。你可以再製作 500 個相同的字符串,結果不會改變。等一下。如果那是真的,那為什麼這個例子以前不起作用?

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = new String("CodeGym is the best website for learning Java!");
       System.out.println(s1 == s2);
   }
}
我認為您的直覺已經告訴您原因 =) 在進一步閱讀之前嘗試猜測。您可以看到這兩個字符串是以不同的方式聲明的。一個有新運算符,另一個沒有。原因就在這裡。當使用new操作符創建一個對象時,它會強制為該對象分配一塊新的內存區域。使用new創建的字符串不會在字符串池中結束——它成為一個單獨的對象,即使它的文本與字符串池中的字符串完全匹配。也就是說,如果我們編寫如下代碼:

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = "CodeGym is the best website for learning Java!";
       String s3 = new String("CodeGym is the best website for learning Java!");
   }
}
在記憶中,它看起來像這樣:
等於和字符串比較 - 4
每次使用new創建一個新對象時,都會分配一個新的內存區域,即使新字符串中的文本是相同的!看來我們已經找到了==運算符。但是我們的新朋友equals()方法呢?

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = new String("CodeGym is the best website for learning Java!");
       System.out.println(s1.equals(s2));
   }
}
控制台輸出: 有趣。我們確定s1s2指向內存中的不同區域。但是equals()方法仍然告訴我們它們是相等的。為什麼?還記得我們之前說過可以重寫equals()方法來比較我們想要的對象嗎?這正是他們對String類所做的。它覆蓋了equals()方法。它不是比較引用,而是比較字符串中的字符序列。如果文本相同,那麼它們是如何創建的或存儲在何處都無關緊要:無論是在字符串池中還是在單獨的內存區域中。比較的結果將為真。順便說一句,Java 允許您執行不區分大小寫的字符串比較。通常,如果其中一個字符串全部為大寫字母,則比較結果將為假:

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = new String("CODEGYM IS THE BEST WEBSITE FOR LEARNING JAVA!");
       System.out.println(s1.equals(s2));
   }
}
控制台輸出: false 對於不區分大小寫的比較,String類具有equalsIgnoreCase()方法。如果您只關心比較特定字符的序列而不是字母大小寫,則可以使用它。例如,這在比較兩個地址時可能會有所幫助:

public class Main {

   public static void main(String[] args) {

       String address1 = "2311 Broadway Street, San Francisco";
       String address2 = new String("2311 BROADWAY STREET, SAN FRANCISCO");
       System.out.println(address1.equalsIgnoreCase(address2));
   }
}
在這種情況下,我們顯然是在談論相同的地址,因此使用equalsIgnoreCase()方法是有意義的。

String.intern() 方法

String還有一個更棘手的方法:intern();intern ()方法直接與字符串池一起使用。如果您在某個字符串上 ​​調用intern()方法:
  • 它檢查字符串池中是否有匹配的字符串
  • 如果存在,則返回對池中字符串的引用
  • 如果不是,它將字符串添加到字符串池並返回對它的引用。
在對使用new獲得的字符串引用使用intern()方法後,我們可以使用==運算符將其與字符串池中的字符串引用進行比較。

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = new String("CodeGym is the best website for learning Java!");
       System.out.println(s1 == s2.intern());
   }
}
控制台輸出: true當我們之前在沒有intern() 的 情況下比較這些字符串時,結果為 false。現在intern()方法檢查字符串“CodeGym is the best site for learning Java!” 在字符串池中。當然,它是:我們用

String s1 = "CodeGym is the best website for learning Java!";
我們檢查s1和s2.intern()返回的引用是否指向相同的內存區域。當然,他們確實這樣做了 :) 總之,記住並應用這條重要規則:始終使用 equals ()方法來比較字符串!比較字符串時,我們幾乎總是要比較它們的字符,而不是比較引用、內存區域或其他任何東西。equals ()方法正是您所需要的。 為了鞏固您所學的知識,我們建議您觀看我們的 Java 課程中的視頻課程

更多閱讀:

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION