一、"=="和equals()的區別
“==”
- 在比較兩個對象時,比較的是對象的地址
- 即使是直接使用"= ="操作符(“aa”==“aa”),比較的還是兩個字符串的引用地址
“equals()”
- 如果沒有重寫equals()方法比較的是對象的地址
- 重寫方法後比較的是內容(大多數方法都重寫)
- 我們都知道我們使用的類都是繼承自Object基類,Object中equals方法中是使用==來實現的,即比較的是兩者的地址值。但是Object的子類可以重寫equals方法,比如Date、String、Integer等類都是重寫了equals都是重寫了,比較的是值是否相等。https://blog.csdn.net/qq_26947195/article/details/79505553
那麼,怎樣比較對象的地址呢?
二、常量池
Integer a = new Integer(1);
Integer b = new Integer(1);
System.out.println(a==b); //false
Integer c = 1;
Integer d = 1;
System.out.println(c==d); //true
爲什麼兩個結果不同呢?
常量池在java用於保存在編譯期已確定的,已編譯的class文件中的一份數據。它包括了關於類,方法,接口等中的常量,也包括字符串常量,如String s = "java"這種申明方式;當然也可擴充,執行器產生的常量也會放入常量池,故認爲常量池是JVM的一塊特殊的內存空間。
參考文章:https://www.cnblogs.com/syp172654682/p/8082625.html
1 String s1 = "Hello";
2 String s2 = "Hello";
3 String s3 = "Hel" + "lo";
4 String s4 = "Hel" + new String("lo");
5 String s5 = new String("Hello");
6 String s6 = s5.intern();
7 String s7 = "H";
8 String s8 = "ello";
9 String s9 = s7 + s8;
10
11 System.out.println(s1 == s2); // true
12 System.out.println(s1 == s3); // true
13 System.out.println(s1 == s4); // false
14 System.out.println(s1 == s9); // false
15 System.out.println(s4 == s5); // false
16 System.out.println(s1 == s6); // true
s1 == s2這個非常好理解,s1、s2在賦值時,均使用的字符串字面量,說白話點,就是直接把字符串寫死,在編譯期間,這種字面量會直接放入class文件的常量池中,從而實現複用,載入運行時常量池後,s1、s2指向的是同一個內存地址,所以相等。
s1 = = s3這個地方有個坑,s3雖然是動態拼接出來的字符串,但是所有參與拼接的部分都是已知的字面量,在編譯期間,這種拼接會被優化,編譯器直接幫你拼好,因此String s3 = “Hel” + “lo”;在class文件中被優化成String s3 = “Hello”,所以s1 == s3成立。只有使用引號包含文本的方式創建的String對象之間使用“+”連接產生的新對象纔會被加入字符串池中。
s1 == s4當然不相等,s4雖然也是拼接出來的,但new String(“lo”)這部分不是已知字面量,是一個不可預料的部分,編譯器不會優化,必須等到運行時纔可以確定結果,結合字符串不變定理,鬼知道s4被分配到哪去了,所以地址肯定不同。對於所有包含new方式新建對象(包括null)的“+”連接表達式,它所產生的新對象都不會被加入字符串池中。
s1 == s9也不相等,道理差不多,雖然s7、s8在賦值的時候使用的字符串字面量,但是拼接成s9的時候,s7、s8作爲兩個變量,都是不可預料的,編譯器畢竟是編譯器,不可能當解釋器用,不能在編譯期被確定,所以不做優化,只能等到運行時,在堆中創建s7、s8拼接成的新字符串,在堆中地址不確定,不可能與方法區常量池中的s1地址相同。
s1 == s6這兩個相等完全歸功於intern方法,s5在堆中,內容爲Hello ,intern方法會嘗試將Hello字符串添加到常量池中,並返回其在常量池中的地址,因爲常量池中已經有了Hello字符串,所以intern方法直接返回地址;而s1在編譯期就已經指向常量池了,因此s1和s6指向同一地址,相等。
關於intern:intern方法會返回String對象在常量池中的引用,如果在常量池中已經存在這個字符串了,那麼直接返回這個引用;如果沒有,就把這個字符串複製到常量池,然後返回引用。
String a = new String("A");
String a1 = "A";
System.out.println(a==a1); //false
System.out.println(a1.intern()==a); //false
System.out.println(a.intern()==a1); //true
特例1
public static final String A = "ab"; // 常量A
public static final String B = "cd"; // 常量B
public static void main(String[] args) {
String s = A + B; // 將兩個常量用+連接對s進行初始化
String t = "abcd";
if (s == t) {
System.out.println("s等於t,它們是同一個對象");
} else {
System.out.println("s不等於t,它們不是同一個對象");
}
}
s等於t,它們是同一個對象
對比上面的s1==s9,同樣是拼接的,爲什麼這裏將A和B拼接起來就相等呢?
A和B都是常量,值是固定的,因此s的值也是固定的,它在類被編譯時就已經確定了。也就是說:String s=A+B; 等同於:String s=“ab”+“cd”;
常量
https://blog.csdn.net/qq_26947195/article/details/79505553
第一種:是一個值,這個值本身,我們就叫做常量。
整型常量:1024
實型常量:1.024
字符常量:‘g’ ‘c’ ‘w’
字符串常量:“gcw”
邏輯常量:true false
這只是我們平時我們的一個說法而已,比如數字1024,我們說它是一個int類型的常量。
第二種:不可變的變量,我們也稱爲常量。就是被我們都知道的關鍵字final修飾的變量,final修飾的變量,只要一經賦值,就不可以改變。可能它本身是個變量,但是被final修飾,我們可以認爲是個常量。比如:
final int i=1024;
特例2
public static final String A; // 常量A
public static final String B; // 常量B
static {
A = "ab";
B = "cd";
}
public static void main(String[] args) {
// 將兩個常量用+連接對s進行初始化
String s = A + B;
String t = "abcd";
if (s == t) {
System.out.println("s等於t,它們是同一個對象");
} else {
System.out.println("s不等於t,它們不是同一個對象");
}
}
s不等於t,它們不是同一個對象
A和B雖然被定義爲常量,但是它們都沒有馬上被賦值。在運算出s的值之前,他們何時被賦值,以及被賦予什麼樣的值,都是個變數。因此A和B在被賦值之前,性質類似於一個變量。那麼s就不能在編譯期被確定,而只能在運行時被創建了。