Long l1 = new Long(100); Long l2 = new Long(100); Long l3 = new Long("100"); Long l4 = 100l; Long l5 = 100L; System.out.println("-------------一般比較-------------"); System.out.println(l1 == l2);// false System.out.println(l1 == l3);// false System.out.println(l1.equals(l2));// true System.out.println(l1.equals(l3));// true,注意 System.out.println(l4 == l5);// true System.out.println(l4.equals(l5));// true String s1 = "xxx"; String s2 = "xxx"; String s3 = new String("xxx"); System.out.println("-------------字串比較-------------"); System.out.println("s1 == s2:" + s1 == s2 + "s1 == s2:");// false,注意 System.out.println(s1 == s2);// true System.out.println(s1 == s3);// false System.out.println(s1.equals(s2));// true System.out.println(s1.equals(s3));// true
第17行要小心
因為會將寫死的字串加上xxx,然後==後面的xxx+寫死的字串,結果當然是false,
必需加圓括號先做運算才可以,如下:
"s1 == s2:" + (s1 == s2) + "s1 == s2:"
一般都是像Long那樣,但String因為有字串池的原故,所以如果不用new時就不太一樣
如果比較的不是xxx,是個數字,這時如果用String.valueOf(1),結果等同有new
除非String.valueOf("1"),因為本來就是字串,強轉還是字串,這時不會new
重點是如果是自己定義的類別呢?,先寫兩個測試類別
public class Compare1 { int i = 100; public int getI() { return i; } public void setI(int i) { this.i = i; } }
public class Compare2 { int i = 100; public int getI() { return i; } public void setI(int i) { this.i = i; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (!(obj instanceof Compare2)) { return false; } return this.i == ((Compare2) obj).getI(); } }
Compare1 ca1 = new Compare1(); Compare1 ca2 = new Compare1(); Compare2 cb1 = new Compare2(); Compare2 cb2 = new Compare2(); System.out.println("-------------自定比較-------------"); System.out.println("ca1 == ca2:" + (ca1 == ca2)); System.out.println("cb1 == cb2:" + (cb1 == cb2)); System.out.println("ca1 equals ca2:" + ca1.equals(ca2)); System.out.println("cb1 equals cb2:" + cb1.equals(cb2));結果為:
-------------自定比較-------------
ca1 == ca2:false
cb1 == cb2:false
ca1 equals ca2:false
cb1 equals cb2:true
如果有new關鍵字一定是不同的記憶體空間,Object的equals方法最原始為
public boolean equals(Object obj) { return (this == obj); }所以就是比較記憶體,但有覆寫當然就把它變成我們想要的比較內容
注意:就算是實作Cloneable,然後使用clone(),這樣子記憶體還是不一樣的,也就是結果還是和cb1和cb2一樣
結論:
== 因為長得像記憶體,所以就是比較記憶體;equals 就比較內容。但沒覆寫equals方法,就是比較記憶體,因為Object就是this == obj
※注意
沒有小數點的 Byte、Short、Integer、Long 和 Character,裡面有個內部類 XxxCache如 Integer 內部有個類別,IntegerCache,會將 -128~127 cache 起來 (Character 是 0 ~127)
所以在這個範圍裡,如果內容一樣且不用 new,然後用 == 判斷的,是沒問題的,如
Character c1 = 'y', c2 = 'y'; P.S:y 的 ASCII 是 121
c1 == c2 會是 true
※Integer status = 1;
status == 1,這是可以判斷的,但要小心 null == 1,int 是沒有 null 的,會報錯
※Enum的equals、==
public class EnumEqal { public enum EnumABC { A, B, C; } private EnumABC enumAbc; // setter/getter... public static void main(String[] args) { EnumEqal ee = new EnumEqal(); ee.setEnumAbc(EnumABC.C); System.out.println(EnumABC.C.equals(ee.getEnumAbc()));// true System.out.println(EnumABC.C == ee.getEnumAbc());// true System.out.println(EnumABC.C.equals(ee.getEnumAbc().name()));// false System.out.println(EnumABC.C.toString().equals(ee.getEnumAbc().name()));// true System.out.println(EnumABC.C.toString() == ee.getEnumAbc().name());// true } }
※new一個物件,然後比較,所以不管是equals和==都會是true,就算將enumAbc改成static,然後setter/getter也改成static,結果依然是一樣的
※此例除非另外 new,否則一定都是 true
※List 的 equals
仍然是比對物件的內容和型態,不要被角括號影響了※
List<Integer> l1 = new ArrayList<>(); List<String> l2 = new ArrayList<>(); List l3 = new ArrayList(); List l4 = new ArrayList(); List<Integer> l5 = new ArrayList<>(); l5.add(8); List<Integer> l6 = new ArrayList<>(); l6.add(8); System.out.println(l1.equals(l2)); // true System.out.println(l3.equals(l4)); // true System.out.println(l5.equals(l6)); // true
※只有內容或內容的型態一樣才會是 true,其他都是 false
※Set、Map 也是同樣的道理,Map 裡的 key 或 value,其中一個不一樣就會是 false 了
※String 的 intern
指的是 java 7 之後的,此例是用 java 8 跑的
intern 表示將字串的內容放到字串池,如果沒有就放;有就什麼也不做
@Test@Test
public void test1() {
String s1 = "a"; // s1 和 s2 如果為 final,結果會是 true
String s2 = "b";
String s3 = "ab";
System.out.println(s3 == s1 + s2); // false,變數相加要看變數佔哪個記憶體空間
System.out.println(s3 == "ab"); // true
System.out.println(s3 == "a" + "b"); // true,優化成 "ab"
System.out.println(s3 == s1 + "b"); // false,無法優化成 "ab",除非 s1 是 final
}
public void test2() {
String s1 = new String("123"); // stack 是 s1;heap 是 123,字串池123。 字串的 123 會放在字串池
// String s1 = new StringBuilder("123").toString();
s1.intern(); // s1 將 123 放到字串池,但一開始就已經有 123 了,所以不會有動作
String s2 = "123";
System.out.println(s1 == s2); // false,如果想變 true,可用 s1 = s1.intern();
}@Test
public void test3() {
String s1 = new String("1") + new String("2") + new String("3"); // 字串的 1、2、3 會放在字串池
// String s1 = new StringBuilder("1").append("23").toString();
s1.intern(); // s1 將 123 放到字串池,一開始並沒有 123 ,所以會放進去
String s2 = "123";
System.out.println(s1 == s2); // true
// 總共產生幾個物件?
// new String 底層會用 new StringBuilder,最後還會 toString,因為是 new,所以最少有 5 個
// 1、2、3 如果字串池都沒有也會產生三個,最後 intern() 如果沒有 123 也會產生一個,所以最多 9 個物件
}
@Test
public void test4() {
String s1 = String.valueOf(123); // stack 是 s1;heap 是 123,字串池沒有
s1.intern(); // s1 將 123 放到字串池,一開始並沒有 123 ,所以會放進去
String s2 = "123";
System.out.println(s1 == s2); // true
}
@Test
public void testKeyWord() {
// 多個 String 相加,底層會用 StringBuilder 連起來
String str1 = new StringBuilder("aaa").append("bbb").toString();
System.out.println(str1.intern() == str1); // true
String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(str2.intern() == str2); // false,因為 java 是字串池內鍵已有的
// 內鍵的字串池可看 VersionProps 類
}
沒有留言:
張貼留言