"=="和equals()的區別 、Java常量池

一、"=="和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就不能在編譯期被確定,而只能在運行時被創建了。

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