『壹』 java 常量池 到底是在堆中還是棧中

java常量池不在堆中也不在棧中,是獨立的內存空間管理。
1. 棧:存放基本類型的變數數據和對象回的引用答,但對象本身不存放在棧中,而是存放在堆(new 出來的對象)或者常量池中(字元串常量對象存放在常量池中。)
2. 堆:存放所有new出來的對象。
3. 常量池:存放字元串常量和基本類型常量(public static final)。
對於字元串:其對象的引用都是存儲在棧中的,如果是編譯期已經創建好(直接用雙引號定義的)的就存儲在常量池中,如果是運行期(new出來的)才能確定的就存儲在堆中。對於equals相等的字元串,在常量池中永遠只有一份,在堆中有多份。

『貳』 java常量池是什麼

1. 首先String不屬於8種基本數據類型,String是一個對象。
因為對象的默認值是null,所以String的默認值也是null;但它又是一種特殊的對象,有其它對象沒有的一些特性。

2. new String()和new String(「」)都是申明一個新的空字元串,是空串不是null;

3. String str=」kvill」;
String str=new String (「kvill」);的區別:
在這里,我們不談堆,也不談棧,只先簡單引入常量池這個簡單的概念。
常量池(constant pool)指的是在編譯期被確定,並被保存在已編譯的.class文件中的一些數據。它包括了關於類、方法、介面等中的常量,也包括字元串常量。
看例1:

Java代碼
String s0=」kvill」;
String s1=」kvill」;
String s2=」kv」 + 「ill」;
System.out.println( s0==s1 );
System.out.println( s0==s2 );
String s0=」kvill」;
String s1=」kvill」;
String s2=」kv」 + 「ill」;
System.out.println( s0==s1 );
System.out.println( s0==s2 );
結果為:
true
true
首先,我們要知道Java會確保一個字元串常量只有一個拷貝。
因為例子中的s0和s1中的」kvill」都是字元串常量,它們在編譯期就被確定了,所以s0==s1為true;而」kv」和」ill」也都是字元串常量,當一個字元串由多個字元串常量連接而成時,它自己肯定也是字元串常量,所以s2也同樣在編譯期就被解析為一個字元串常量,所以s2也是常量池中」kvill」的一個引用。
所以我們得出s0==s1==s2;
用new String() 創建的字元串不是常量,不能在編譯期就確定,所以new String() 創建的字元串不放入常量池中,它們有自己的地址空間。
看例2:

Java代碼
String s0=」kvill」;
String s1=new String(」kvill」);
String s2=」kv」 + new String(「ill」);
System.out.println( s0==s1 );
System.out.println( s0==s2 );
System.out.println( s1==s2 );
String s0=」kvill」;
String s1=new String(」kvill」);
String s2=」kv」 + new String(「ill」);
System.out.println( s0==s1 );
System.out.println( s0==s2 );
System.out.println( s1==s2 ); 結果為:
false
false
false
例2中s0還是常量池中」kvill」的應用,s1因為無法在編譯期確定,所以是運行時創建的新對象」kvill」的引用,s2因為有後半部分new String(「ill」)所以也無法在編譯期確定,所以也是一個新創建對象」kvill」的應用;明白了這些也就知道為何得出此結果了。

4. String.intern():
再補充介紹一點:存在於.class文件中的常量池,在運行期被JVM裝載,並且可以擴充。String的intern()方法就是擴充常量池的一個方法;當一個String實例str調用intern()方法時,Java查找常量池中是否有相同Unicode的字元串常量,如果有,則返回其的引用,如果沒有,則在常量池中增加一個Unicode等於str的字元串並返回它的引用;看例3就清楚了
例3:

Java代碼
String s0= 「kvill」;
String s1=new String(」kvill」);
String s2=new String(「kvill」);
System.out.println( s0==s1 );
System.out.println( 「**********」 );
s1.intern();
s2=s2.intern(); //把常量池中「kvill」的引用賦給s2
System.out.println( s0==s1);
System.out.println( s0==s1.intern() );
System.out.println( s0==s2 );
String s0= 「kvill」;
String s1=new String(」kvill」);
String s2=new String(「kvill」);
System.out.println( s0==s1 );
System.out.println( 「**********」 );
s1.intern();
s2=s2.intern(); //把常量池中「kvill」的引用賦給s2
System.out.println( s0==s1);
System.out.println( s0==s1.intern() );
System.out.println( s0==s2 ); 結果為:
false
**********
false //雖然執行了s1.intern(),但它的返回值沒有賦給s1
true //說明s1.intern()返回的是常量池中」kvill」的引用
true
最後我再破除一個錯誤的理解:
有人說,「使用String.intern()方法則可以將一個String類的保存到一個全局String表中,如果具有相同值的Unicode字元串已經在這個表中,那麼該方法返回表中已有字元串的地址,如果在表中沒有相同值的字元串,則將自己的地址注冊到表中「如果我把他說的這個全局的String表理解為常量池的話,他的最後一句話,「如果在表中沒有相同值的字元串,則將自己的地址注冊到表中」是錯的:
看例4:

Java代碼
String s1=new String("kvill");
String s2=s1.intern();
System.out.println( s1==s1.intern() );
System.out.println( s1+" "+s2 );
System.out.println( s2==s1.intern() );
String s1=new String("kvill");
String s2=s1.intern();
System.out.println( s1==s1.intern() );
System.out.println( s1+" "+s2 );
System.out.println( s2==s1.intern() );

結果:
false
kvill kvill
true
在這個類中我們沒有聲名一個」kvill」常量,所以常量池中一開始是沒有」kvill」的,當我們調用s1.intern()後就在常量池中新添加了一個」kvill」常量,原來的不在常量池中的」kvill」仍然存在,也就不是「將自己的地址注冊到常量池中」了。
s1==s1.intern()為false說明原來的「kvill」仍然存在;
s2現在為常量池中「kvill」的地址,所以有s2==s1.intern()為true。

5. 關於equals()和==:
這個對於String簡單來說就是比較兩字元串的Unicode序列是否相當,如果相等返回true;而==是比較兩字元串的地址是否相同,也就是是否是同一個字元串的引用。

6. 關於String是不可變的
這一說又要說很多,大家只要知道String的實例一旦生成就不會再改變了,比如說:String str=」kv」+」ill」+」 「+」ans」;
就是有4個字元串常量,首先」kv」和」ill」生成了」kvill」存在內存中,然後」kvill」又和」 「 生成 」kvill 「存在內存中,最後又和生成了」kvill ans」;並把這個字元串的地址賦給了str,就是因為String的「不可變」產生了很多臨時變數,這也就是為什麼建議用StringBuffer的原因了,因為StringBuffer是可改變的

『叄』 java的常量池裡面都放了些神馬東西

理解Java常量池
JVM運行時數據區的內存模型由五部分組成:

【1】方法區
【2】堆
【3】JAVA棧
【4】PC寄存器
【5】本地方法棧

對於String s = "haha" ,它的虛擬機指令:
0: ldc #16; //String haha
2: astore_1
3: return

對於上面虛擬機指令,其各自的指令流程在《深入JAVA虛擬機》這樣描述到(結合上面實例):

ldc指令格式:ldc,index

ldc指令過程:

要執行ldc指令,JVM首先查找index所指定的常量池入口,在index指向的常量池入口,JVM將會查找CONSTANT_Integer_info,CONSTANT_Float_info和CONSTANT_String_info入口。如果還沒有這些入口,JVM會解析它們。而對於上面的hahaJVM會找到CONSTANT_String_info入口,同時,將把指向被拘留String對象(由解析該入口的進程產生)的引用壓入操作數棧。

astore_1指令格式:astore_1

astore_1指令過程:

要執行astore_1指令,JVM從操作數棧頂部彈出一個引用類型或者returnAddress類型值,然後將該值存入由索引1指定的局部變數中,即將引用類型或者returnAddress類型值存入局部變數1。

return 指令的過程:

從方法中返回,返回值為void。

談一下我個人理解:

從上面的ldc指令的執行過程可以得出:s的值是來自被拘留String對象(由解析該入口的進程產生)的引用,即可以理解為是從被拘留String對象的引用復制而來的,故我個人的理解是s的值是存在棧當中。上面是對於s值得分析,接著是對於"haha"值的分析,我們知道,對於String s = "haha" 其中"haha"值在JAVA程序編譯期就確定下來了的。簡單一點說,就是haha的值在程序編譯成class文件後,就在class文件中生成了(大家可以用UE編輯器或其它文本編輯工具在打開class文件後的位元組碼文件中看到這個haha值)。執行JAVA程序的過程中,第一步是class文件生成,然後被JVM裝載到內存執行。那麼JVM裝載這個class到內存中,其中的haha這個值,在內存中是怎麼為其開辟空間並存儲在哪個區域中呢?

說到這里,我們不妨先來了解一下JVM常量池這個結構,《深入JAVA虛擬機》書中有這樣的描述:

常量池

虛擬機必須為每個被裝載的類型維護一個常量池。常量池就是該類型所用到常量的一個有序集和,包括直接常量(string,integer和floating point常量)和對其他類型,欄位和方法的符號引用。對於String常量,它的值是在常量池中的。而JVM中的常量池在內存當中是以表的形式存在的,對於String類型,有一張固定長度的CONSTANT_String_info表用來存儲文字字元串值,注意:該表只存儲文字字元串值,不存儲符號引用。說到這里,對常量池中的字元串值的存儲位置應該有一個比較明了的理解了。

在介紹完JVM常量池的概念後,接著談開始提到的"haha"的值的內存分布的位置。對於haha的值,實際上是在class文件被JVM裝載到內存當中並被引擎在解析ldc指令並執行ldc指令之前,JVM就已經為haha這個字元串在常量池的CONSTANT_String_info表中分配了空間來存儲haha這個值。既然haha這個字元串常量存儲在常量池中,根據《深入JAVA虛擬機》書中描述:常量池是屬於類型信息的一部分,類型信息也就是每一個被轉載的類型,這個類型反映到JVM內存模型中是對應存在於JVM內存模型的方法區中,也就是這個類型信息中的常量池概念是存在於在方法區中,而方法區是在JVM內存模型中的堆中由JVM來分配的。所以,haha的值是應該是存在堆空間中的。

而對於String s = new String("haha") ,它的JVM指令:
0: new #16; //class String
3: p
4: ldc #18; //String haha
6: invokespecial #20; //Method java/lang/String."":(Ljava/lang/String;)V
9: astore_1
10: return

對於上面虛擬機指令,其各自的指令流程在《深入JAVA虛擬機》這樣描述到(結合上面實例):

new指令格式:new indexbyte1,indexbyte2

new指令過程:

要執行new指令,Jvm通過計算(indextype1<<8)|indextype2生成一個指向常量池的無符號16位索引。然後JVM根據計算出的索引查找常量池入口。該索引所指向的常量池入口必須為CONSTANT_Class_info。如果該入口尚不存在,那麼JVM將解析這個常量池入口,該入口類型必須是類。JVM從堆中為新對象映像分配足夠大的空間,並將對象的實例變數設為默認值。最後JVM將指向新對象的引用objectref壓入操作數棧。

p指令格式:p

p指令過程:

要執行p指令,JVM復制了操作數棧頂部一個字長的內容,然後再將復制內容壓入棧。本指令能夠從操作數棧頂部復制任何單位字長的值。但絕對不要使用它來復制操作數棧頂部任何兩個字長(long型或double型)中的一個字長。上面例中,即復制引用objectref,這時在操作數棧存在2個引用。

ldc指令格式:ldc,index

ldc指令過程:

要執行ldc指令,JVM首先查找index所指定的常量池入口,在index指向的常量池入口,JVM將會查找CONSTANT_Integer_info,CONSTANT_Float_info和CONSTANT_String_info入口。如果還沒有這些入口,JVM會解析它們。而對於上面的haha,JVM會找到CONSTANT_String_info入口,同時,將把指向被拘留String對象(由解析該入口的進程產生)的引用壓入操作數棧。

invokespecial指令格式:invokespecial,indextype1,indextype2

invokespecial指令過程:對於該類而言,該指令是用來進行實例初始化方法的調用。鑒於該指令篇幅,具體可以查閱《深入JAVA虛擬機》中描述。上面例子中,即通過其中一個引用調用String類的構造器,初始化對象實例,讓另一個相同的引用指向這個被初始化的對象實例,然後前一個引用彈出操作數棧。

astore_1指令格式:astore_1

astore_1指令過程:

要執行astore_1指令,JVM從操作數棧頂部彈出一個引用類型或者returnAddress類型值,然後將該值存入由索引1指定的局部變數中,即將引用類型或者returnAddress類型值存入局部變數1。

return 指令的過程:

從方法中返回,返回值為void。

要執行astore_1指令,JVM從操作數棧頂部彈出一個引用類型或者returnAddress類型值,然後將該值存入由索引1指定的局部變數中,即將引用類型或者returnAddress類型值存入局部變數1。

通過上面6個指令,可以看出,String s = new String("haha");中的haha存儲在堆空間中,而s則是在操作數棧中。
上面是對s和haha值的內存情況的分析和理解;那對於String s = new String("haha");語句,到底創建了幾個對象呢?
我的理解:這里"haha"本身就是常量池中的一個對象,而在運行時執行new String()時,將常量池中的對象復制一份放到堆中,並且把堆中的這個對象的引用交給s持有。所以這條語句就創建了2個String對象。

下面是一些String相關的常見問題:

String中的final用法和理解
final StringBuffer a = new StringBuffer("111");
final StringBuffer b = new StringBuffer("222");
a=b;//此句編譯不通過

final StringBuffer a = new StringBuffer("111");
a.append("222");//編譯通過

可見,final只對引用的"值"(即內存地址)有效,它迫使引用只能指向初始指向的那個對象,改變它的指向會導致編譯期錯誤。至於它所指向的對象的變化,final是不負責的。

String 常量池問題的幾個例子

下面是幾個常見例子的比較分析和理解:
[1]
String a = "a1";
String b = "a" + 1;
System.out.println((a == b)); //result = true
String a = "atrue";
String b = "a" + "true";
System.out.println((a == b)); //result = true
String a = "a3.4";
String b = "a" + 3.4;
System.out.println((a == b)); //result = true

分析:JVM對於字元串常量的"+"號連接,將程序編譯期,JVM就將常量字元串的"+"連接優化為連接後的值,拿"a" + 1來說,經編譯器優化後在class中就已經是a1。在編譯期其字元串常量的值就確定下來,故上面程序最終的結果都為true。

[2]
String a = "ab";
String bb = "b";
String b = "a" + bb;
System.out.println((a == b)); //result = false

分析:JVM對於字元串引用,由於在字元串的"+"連接中,有字元串引用存在,而引用的值在程序編譯期是無法確定的,即"a" + bb無法被編譯器優化,只有在程序運行期來動態分配並將連接後的新地址賦給b。所以上面程序的結果也就為false。

[3]
String a = "ab";
final String bb = "b";
String b = "a" + bb;
System.out.println((a == b)); //result = true

分析:和[3]中唯一不同的是bb字元串加了final修飾,對於final修飾的變數,它在編譯時被解析為常量值的一個本地拷貝存儲到自己的常量池中或嵌入到它的位元組碼流中。所以此時的"a" + bb和"a" + "b"效果是一樣的。故上面程序的結果為true。

[4]
String a = "ab";
final String bb = getBB();
String b = "a" + bb;
System.out.println((a == b)); //result = false
private static String getBB() {
return "b";
}

分析:JVM對於字元串引用bb,它的值在編譯期無法確定,只有在程序運行期調用方法後,將方法的返回值和"a"來動態連接並分配地址為b,故上面程序的結果為false。

通過上面4個例子可以得出得知:
String s = "a" + "b" + "c";
就等價於String s = "abc";

String a = "a";
String b = "b";
String c = "c";
String s = a + b + c;

這個就不一樣了,最終結果等於:
StringBuffer temp = new StringBuffer();
temp.append(a).append(b).append(c);
String s = temp.toString();

由上面的分析結果,可就不難推斷出String 採用連接運算符(+)效率低下原因分析,形如這樣的代碼:

public class Test {
public static void main(String args[]) {
String s = null;
for(int i = 0; i < 100; i++) {
s += "a";
}
}
}

每做一次 + 就產生個StringBuilder對象,然後append後就扔掉。下次循環再到達時重新產生個StringBuilder對象,然後 append 字元串,如此循環直至結束。 如果我們直接採用 StringBuilder 對象進行 append 的話,我們可以節省 N - 1 次創建和銷毀對象的時間。所以對於在循環中要進行字元串連接的應用,一般都是用StringBuffer或StringBulider對象來進行append操作。

String對象的intern方法理解和分析:

public class Test4 {
private static String a = "ab";
public static void main(String[] args){
String s1 = "a";
String s2 = "b";
String s = s1 + s2;
System.out.println(s == a);//false
System.out.println(s.intern() == a);//true
}
}

這里用到Java裡面是一個常量池的問題。對於s1+s2操作,其實是在堆裡面重新創建了一個新的對象,s保存的是這個新對象在堆空間的的內容,所以s與a的值是不相等的。而當調用s.intern()方法,卻可以返回s在常量池中的地址值,因為a的值存儲在常量池中,故s.intern和a的值相等

『肆』 java常量池中存放什麼,8種基本類型和String,還有其他的嗎

常量池是介於棧和堆外的另一種獨立的內存管理空間,相同內容常量池中永遠只有一份,版基本數據類型、權對象的引用都存在棧中,執行速度快,包裝類型,對象存儲,new出來的對象都是存儲在堆中,Byte,Short,Integer,Long,Character這5種整型的包裝類只是在對應值小於等於127時才可使用對象池。超過了就會自動申請空間創建對象,所有才會出現上述情況,而String是個較為特殊的包裝類型,直接用=「」創建的數據是存放在常量池,且無論數據大小都不會申請空間創建,除非使用new關鍵字。

『伍』 java 常量池

a不是引用,一般引用存的是內中的一個地址,int a=0是在棧中開的空間,常量是指變數前有final關鍵字的情況。

『陸』 java常量池和運行時常量池的區別

在class文件中,「常量池」是最復雜也最值得關注的內容。
Java是一種動態連接的語言,常量池的作用非常重要,常量池中除了包含代碼中所定義的各種基本類型(如int、long等等)和對象型(如String及數組)的常量值還,還包含一些以文本形式出現的符號引用,比如:
類和介面的全限定名;
欄位的名稱和描述符;
方法和名稱和描述符。
在C語言中,如果一個程序要調用其它庫中的函數,在連接時,該函數在庫中的位置(即相對於庫文件開頭的偏移量)會被寫在程序中,在運行時,直接去這個地址調用函數;
而在Java語言中不是這樣,一切都是動態的。編譯時,如果發現對其它類方法的調用或者對其它類欄位的引用的話,記錄進class文件中的,只能是一個文本形式的符號引用,在連接過程中,虛擬機根據這個文本信息去查找對應的方法或欄位。
所以,與Java語言中的所謂「常量」不同,class文件中的「常量」內容很非富,這些常量集中在class中的一個區域存放,一個緊接著一個,這里就稱為「常量池」。
java中的常量池技術,是為了方便快捷地創建某些對象而出現的,當需要一個對象時,就可以從池中取一個出來(如果池中沒有則創建一個),則在需要重復重復創建相等變數時節省了很多時間。常量池其實也就是一個內存空間,不同於使用new關鍵字創建的對象所在的堆空間。本文只從java使用者的角度來探討java常量池技術,並不涉及常量池的原理及實現方法。個人認為,如果是真的專注java,就必須對這些細節方面有一定的了解。但知道它的原理和具體的實現方法則不是必須的。

『柒』 Java運行時常量池是什麼

在class文件中,「常量池」是最復雜也最值得關注的內容。
Java是一種動態連接的語言,常量池的作用非常重要,常量池中除了包含代碼中所定義的各種基本類型(如int、long等等)和對象型(如String及數組)的常量值還,還包含一些以文本形式出現的符號引用,比如:
類和介面的全限定名;
欄位的名稱和描述符;
方法和名稱和描述符。
在C語言中,如果一個程序要調用其它庫中的函數,在連接時,該函數在庫中的位置(即相對於庫文件開頭的偏移量)會被寫在程序中,在運行時,直接去這個地址調用函數;
而在Java語言中不是這樣,一切都是動態的。編譯時,如果發現對其它類方法的調用或者對其它類欄位的引用的話,記錄進class文件中的,只能是一個文本形式的符號引用,在連接過程中,虛擬機根據這個文本信息去查找對應的方法或欄位。
所以,與Java語言中的所謂「常量」不同,class文件中的「常量」內容很非富,這些常量集中在class中的一個區域存放,一個緊接著一個,這里就稱為「常量池」。
java中的常量池技術,是為了方便快捷地創建某些對象而出現的,當需要一個對象時,就可以從池中取一個出來(如果池中沒有則創建一個),則在需要重復重復創建相等變數時節省了很多時間。常量池其實也就是一個內存空間,不同於使用new關鍵字創建的對象所在的堆空間。本文只從java使用者的角度來探討java常量池技術,並不涉及常量池的原理及實現方法。個人認為,如果是真的專注java,就必須對這些細節方面有一定的了解。但知道它的原理和具體的實現方法則不是必須的。
常量池中對象和堆中的對象

[java] view plain
public class Test{

Integer i1=new Integer(1);
Integer i2=new Integer(1);
//i1,i2分別位於堆中不同的內存空間

System.out.println(i1==i2);//輸出false

Integer i3=1;
Integer i4=1;
//i3,i4指向常量池中同一個內存空間

System.out.println(i3==i4);//輸出true

//很顯然,i1,i3位於不同的內存空間

System.out.println(i1==i3);//輸出false

}

8種基本類型的包裝類和對象池

java中基本類型的包裝類的大部分都實現了常量池技術,這些類是Byte,Short,Integer,Long,Character,Boolean,另外兩種浮點數類型的包裝類則沒有實現。另外Byte,Short,Integer,Long,Character這5種整型的包裝類也只是在對應值小於等於127時才可使用對象池,也即對象不負責創建和管理大於127的這些類的對象。以下是一些對應的測試代碼:

[java] view plain
public class Test{

public static void main(String[] args){

//5種整形的包裝類Byte,Short,Integer,Long,Character的對象,

//在值小於127時可以使用常量池

Integer i1=127;

Integer i2=127;

System.out.println(i1==i2)//輸出true

//值大於127時,不會從常量池中取對象

Integer i3=128;

Integer i4=128;

System.out.println(i3==i4)//輸出false

//Boolean類也實現了常量池技術

Boolean bool1=true;

Boolean bool2=true;

System.out.println(bool1==bool2);//輸出true

//浮點類型的包裝類沒有實現常量池技術

Double d1=1.0;

Double d2=1.0;

System.out.println(d1==d2)//輸出false

}

}

String也實現了常量池技術

String類也是java中用得多的類,同樣為了創建String對象的方便,也實現了常量池的技術,測試代碼如下:
[java] view plain
public class Test{

public static void main(String[] args){

//s1,s2分別位於堆中不同空間

String s1=new String("hello");

String s2=new String("hello");

System.out.println(s1==s2)//輸出false

//s3,s4位於池中同一空間

String s3="hello";

String s4="hello";

System.out.println(s3==s4);//輸出true

}

}
最後
細節決定成敗,寫代碼更是如此。
在JDK5.0之前是不允許直接將基本數據類型的數據直接賦值給其對應地包裝類的,如:Integer i = 5;

但是在JDK5.0中支持這種寫法,因為編譯器會自動將上面的代碼轉換成如下代碼:Integer i=Integer.valueOf(5);

這就是Java的裝箱.JDK5.0也提供了自動拆箱. Integer i =5; int j = i;

Integer的封裝:

[java] view plain
public static Integer valueOf(int i) {
final int offset = 128;
if (i >= -128 && i <= 127) { // must cache
return IntegerCache.cache[i + offset];
}
return new Integer(i);
}

private static class IntegerCache {

private IntegerCache(){}
static final Integer cache[] = new Integer[-(-128) + 127 + 1];
static {
for(int i = 0; i < cache.length; i++)
cache[i] = new Integer(i - 128);
}
}

由於cache[]在IntegerCache類中是靜態數組,也就是只需要初始化一次,即static{......}部分,所以,如果Integer對象初始化時是-128~127的范圍,就不需要再重新定義申請空間,都是同一個對象---在IntegerCache.cache中,這樣可以在一定程度上提高效率。

『捌』 常量池中為什麼會存在"java"這樣的字元串

其實jvm從啟動,到執行main裡面的第一條代碼,要經歷很多的,比如載入rt.jar裡面所有的Class,載入一個class肯定要執行static{}中內容,況且rt.jar中的jdk的類裡面有很多xxx.startWith("java")或者其他用到"java"的代碼,jvm啟動的時候直接按照常量載入進來了丟到internmap裡面了

『玖』 Java中的幾種常量池

  • 字元串常量池:當類載入完成,在堆中生成字元串對象實例,然後將該字元串對象實例的引用值存到string pool中。

  • class文件常量池:用於存放編譯器生成的各種字面量(Literal)和符號引用(Symbolic References)。

  • 運行時常量池:當類載入到內存中後,jvm就會將class常量池中的內容存放到運行時常量池中。

『拾』 Java String常量池

常量池只有一個。

Stringa="abc"//現在常量池裡找abc,有的話就把a指向它,沒有的話就新建這是在編譯期間做的
Stringb=newString("abc");//直接新建一個abc,並把地址給b,這是在運行期間做的
a==b//判斷a和b的地址是否相等,明顯不相等。
a.equals(b)//判斷a和b指向的字元串常量是否相等,都是"abc",所以是true