摘要:注意運行時常量池存在于方法區中。一個在堆中,一個在字符串常量池中。注意,是把這個對象添加到字符串常量池。內存中有一個基本類型封裝類的常量池。需要注意的是,和這兩個類并沒有對應的常量池。
在寫之前我們先來看幾個問題,假如你對這些問題已經很懂了的話,那大可不用看這篇文章,如果不大懂的話,那么可以看看我的想法。
問題1:
public static void main(String[] args){ String t1 = new String("2"); t1.intern(); String t2 = "2"; System.out.println(t1 == t2); String t3 = new String("2") + new String("2"); t3.intern(); String t4 = "22"; System.out.println(t3 == t4); }
答案輸出:
JDK1.6是 false false
JDK1.7是 false true;
問題2(把問題1的語句調換一下位置)
public static void main(String[] args){ String t1 = new String("2"); String t2 = "2"; t1.intern(); System.out.println(t1 == t2); String t3 = new String("2") + new String("2"); String t4 = "22"; t3.intern(); System.out.println(t3 == t4); }
答案輸出:
false false
對于這兩個問題,看了幾個人的博客,可謂百花齊放,越看越懵逼
問題3
public static void main(String[] args){ Integer a = 1; Integer b = 2; Integer c = 3; Integer d = 3; Integer e = 321; Integer f = 321; Long g = 3L; System.out.println(c == d); System.out.Println(e == f); System.out.println(c == (a + b)); System.out.println(c.equals(a+b)); System.out.println(g == (a + b)); System.out.println(g.equals(a + b)); }
答案輸出:
true
false
true
true
true
false
問題4:
運行時常量池與字符串常量池是什么關系?包含?
在解決問題之前,我們先來簡單了解一些常量池的一些知識點(大部分來源于周志明的深入Java虛擬機這本書)。
JVM中的幾種常量池 1.class文件常量池在Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池(Constant Pool Table),用于存放編譯期生成的各種字面量和符號引用。
這里簡單解釋下字面量和符號引用
字面量
字面量類似與我們平常說的常量,主要包括:
文本字符串:就是我們在代碼中能夠看到的字符串,例如String a = "aa"。其中"aa"就是字面量。
被final修飾的變量。
符號引用
主要包括以下常量:
類和接口和全限定名:例如對于String這個類,它的全限定名就是java/lang/String。
字段的名稱和描述符:所謂字段就是類或者接口中聲明的變量,包括類級別變量(static)和實例級的變量。
方法的名稱和描述符。所謂描述符就相當于方法的參數類型+返回值類型。
2.運行時常量池我們上面說的class文件中的常量池,它會在類加載后進入方法區中的運行時常量池。并且需要的注意的是,運行時常量池是全局共享的,多個類共用一個運行時常量池。并且class文件中常量池多個相同的字符串在運行時常量池只會存在一份。
注意運行時常量池存在于方法區中。
3.字符串常量池看名字我們就可以知道字符串常量池會用來存放字符串,也就是說常量池中的文本字符串會在類加載時進入字符串常量池。
那字符串常量池和運行時常量池是什么關系呢?上面我們說常量池中的字面量會在類加載后進入運行時常量池,其中字面量中有包括文本字符串,顯然從這段文字我們可以知道字符串常量池存在于運行時常量池中。也就存在于方法區中。
不過在周志明那本深入java虛擬機中有說到,到了JDK1.7時,字符串常量池就被移出了方法區,轉移到了堆里了。
那么我們可以推斷,到了JDK1.7以及之后的版本中,運行時常量池并沒有包含字符串常量池,運行時常量池存在于方法區中,而字符串常量池存在于堆中。
說了這么多,現在我們開始來解決上面提出了問題。
解決問題問題1:
public static void main(String[] args){ String t1 = new String("1"); t1.intern(); String t2 = "1"; System.out.println(t1 == t2); String t3 = new String("2") + new String("2"); t3.intern(); String t4 = "22"; System.out.println(t3 == t4); }
答案輸出:
JDK1.6是 false false。
JDK1.7是 false true;
在解決這個問題之前,我們先來看另外一道面試中經常會問到的問題。
String t = new String("tt");
假如程序中只有這樣一行代碼,那么這行代碼創建了幾個對象?
我們上面說過,"tt"屬于字面量,那么它會在類加載之后存在于字符串常量池中,也就是說,在 String t = new String("tt")這句代碼執行之前,字符串常量池就已經創建了"tt"這個字符串對象了,我們都知道,new這個關鍵字會在堆中創建一個對象。
所以,這段代碼創建了兩個對象。一個在堆中,一個在字符串常量池中。
那么下面這段代碼又是創建了幾個對象呢?
String t1 = new String("tt"); String t2 = new String("tt");
答是這段代碼創建了三個對象,我們上面說了,字符串常量池只會保存一份內容相同的字符串。也就是說,在這兩句代碼執行之前,字符串常量池就已經創建了內容為"tt"的對象了。這兩句代碼執行之后,又在堆中創建了兩個,所以一共創建了三個。
那么下面這段代碼又是創建了幾個對象?
String t = "tt";
答是1個,在這段代碼執行之前,字符串常量池已經創建了一個"tt"的對象,但由于這行代碼并非用new的方法,所以虛擬機會在字符串常量池中尋找是否有內容為"tt"的字符串對象,如果有,則直接返回這個字符串的引用,所以最終結果只創建了一個對象。
回到我們的問題,在這里我們先解釋下String 的intern方法。
例如我們調用了t.intern()。
在JDK1.6的時候,調用了這個方法之后,虛擬機會在字符串常量池在查找是否有內容與"tt"相等的對象,如果有,則返回這個對象,如果沒有,則會在字符串常量池中添加這個對象。注意,是把這個對象添加到字符串常量池。
到了JDK1.7之后,如果調用了intern這個方法,虛擬機會在字符串常量池在查找是否有內容與"tt"相等的對象,如果有,則返回這個對象,如果沒有。則會在堆中把這個對象的引用復制添加到字符串常量池中。注意,這個時候添加的是對象在堆中的引用。
現在開始來分析問題中的代碼
t1 = new String("1")。
這句代碼執行之前,字符串常量池中已經有"t"這個對象,執行之后會在堆中也創建一個"t"的對象,此時t1指向的是堆中的對象。
t1.intern();
這句代碼執行之后,會在字符串常量池尋早內容為"t"的對象,字符串常量池已經存在這個對象了,把這個對象返回(不過返回之后并沒有變量來接收)。
t2 = "1"。
這句執行后會在字符串常量池查找內容為"t"的對象,字符串常量池已經有這個對象了,返回給t2,此時t2指向的是常量池中的對象。
一個是常量池中的對象,一個是在堆中的對象,兩者能相等嗎?因此
t1 與 t2不相等。
接著下面
t3 = new String("2") + new String("2");
這段代碼調用之前,字符串常量池有一個"2"的對象,執行之后,實際上會調用StringBuilder的append()方法類進行拼接,最后在堆中創建一個"22"的對象,注意,此時字面量并沒有"22"這個字符串,也就是說在字符串常量池并沒有"22"這個對象。此時t3指向堆中"22"這個對象
t3.intern();
執行這個方法之后
在JDK1.6的時候,它在字符串常量池中并沒有找到內容為"22"的對象,所以這個時候會把這個對象添加到字符串常量池,并把這個對象返回(此時并沒有變量來接收這個返回的對象)。注意添加的是對象,而并非引用。
t4 = "22"。
這句代碼執行后,會返回字符串常量池中內容為"22"對象,此時t4指向的是字符串常量池中的對象。
顯然,一個對象在字符串常量池,一個在堆中,兩個對象并非是同一個對象,因此在JDK1.6的時候,t3與t4不相等。
但是在JDK1.7的時候
t3.intern()執行之后,由于在字符串常量池在并沒有內容為"22"的對象,所以會把堆中該對象的引用賦值到字符串常量池。注意此時字符串常量池保存的是堆中這個對象的引用。
t4 = "22"。
執行這句代碼之后,從字符串常量池返回給t4的是堆中對象的引用。此時t4指向的實際上是堆中對象的引用,也就是說,t3和t4指向的是同一個對象。
因此t3與t4相等。
不知道你明白了沒有?反正我是搞了好久才明白...
問題2至于問題2,我就只講下半部分的代碼,上半部分如果你看懂了問題1,那么問題2也差不多自然懂了。
String t3 = new String("2") + new String("2"); String t4 = "22"; t3.intern(); System.out.println(t3 == t4);
t3 = new String("2") + new String("2")。
這段代碼調用之前,字符串常量池有一個"2"的對象,執行之后,實際上會調用StringBuilder的append()方法類進行拼接,最后在堆中創建一個"22"的對象。此時t3指向堆中"22"這個對象
t4 = "22"。
這句代碼執行之前,字符串常量池已經存在"22"這個對象了,所有直接把這個對象返回給t4,此時t4指向的是字符串常量池中的對象.
所以t3和t4肯定不是同一個對象啊,t3.intern這句幾乎可以忽略,不會給t3和t4造成任何影響。
問題3public static void main(String[] args){ Integer a = 1; Integer b = 2; Integer c = 3; Integer d = 3; Integer e = 321; Integer f = 321; Long g = 3L; System.out.println(c == d); System.out.Println(e == f); System.out.println(c == (a + b)); System.out.println(c.equals(a+b)); System.out.println(g == (a + b)); System.out.println(g.equals(a + b)); }
對于這個問題,我簡單說一下可能你就懂了。
(1). 內存中有一個java基本類型封裝類的常量池。這些類包括
Byte, Short, Integer, Long, Character, Boolean。需要注意的是,Float和Double這兩個類并沒有對應的常量池。
(2).上面5種整型的包裝類也只是在對象數值在-128~127才可以使用這些常量池。
(3). 在周志明的那本虛擬機中有這樣一句話:包裝類的
"=="運行符在不遇到算術運算的情況下不會自動拆箱,以及他們的equals()方法不處理數據類型的關系,可以推斷出如果遇到“==”兩邊有算術運算是話就會自動拆箱和進行數據類型轉換處理。
(4).Long的equals方法會先判斷是否是Long類型。
(5).無論是Integer還是Long,他們的equals方法比較的是數值。
所以:
System.out.println(c == d)。
由于常量池的作用,c與d指向的是同一個對象(注意此時的==比較的是對象,也就是地址,而不是數值)。因此為true
System.out.println(e == f)。
由于321超過了127,因此常量池失去了作用,所以e和f數值雖然相同,但不是同一個對象,以此為false。
System.out.println(c == (a+b))。
此時==兩邊有算術運算,會進行拆箱,因此此時比較的是數值,而并非對象。因此為true。
System.out.println(c.equals(a+b))
c與a+b的數值相等,為true。
System.out.pirnln(g == (a + b))
由于==兩邊有算術運算,所以比較的是數值,因此為true。
System.out.println(g.equals(a+b))。
Long類型的equal在比較是時候,會先判斷a+b是否為Long類型,顯然a+b不是,因此false
問題到此就結束了,以上便是自己的理解,以上如果有不對勁的地方,非常歡迎你的指點。
完。
關注公眾號「苦逼的碼農」,獲取更多原創文章,后臺回復「禮包」送你一份特別的大禮包
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/76879.html
摘要:摘要物聯網嵌入式芯片,存儲是數據核心之一,這個對于編程和設計的人來說又比較陌生,今天來說說中的內存結構。棧堆棧又稱為棧或堆疊,是計算機科學中的一種抽象資料類型,只允許在有序的線性資料集合的一端稱為堆棧頂端,進行加入數據和移除數據的運算。 摘要:物聯網嵌入式芯片,存儲是數據核心之一,這個對于...
摘要:所以今天,就和大家一起聊一聊前端的安全那些事兒。我們就聊一聊前端工程師們需要注意的那些安全知識。殊不知,這不僅僅是違反了的標準而已,也同樣會被黑客所利用。 歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面(不僅僅是代碼):https://segmentfault.com/blog... 隨著互聯網的發達,各種WEB應用也變得越來越復雜,滿足了用戶的各種需求...
摘要:歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面不僅僅是代碼什么是功能統計作為一名開發,我們的產品發布出去之后,無論是產品還是運營,其實都是想及時了解產品對用戶產生的影響的。下一章,我們將繼續聊聊速度統計。 歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面(不僅僅是代碼):https://segmentfault.com/bl...
閱讀 672·2021-11-15 11:37
閱讀 4127·2021-09-09 09:34
閱讀 3567·2019-08-30 15:52
閱讀 2608·2019-08-29 14:03
閱讀 2850·2019-08-26 13:36
閱讀 1592·2019-08-26 12:16
閱讀 1599·2019-08-26 11:45
閱讀 3491·2019-08-23 18:41