摘要:前者在中開辟一個常量,并返回相應的引用,而后者是在中開辟一個常量,再返回相應的對象。方法方法,可以在期間將常量加入到常量池。方法返回這個新創(chuàng)建的常量的引用。
1. String實例的初始化
String類型的初始化在Java中分為兩類:
一類是通過雙引號包裹一個字符來初始化;
另一類是通過關鍵字new 像一個普通的對象那樣初始化一個String實例。
前者在constant pool中開辟一個常量,并返回相應的引用,而后者是在heap中開辟一個常量,再返回相應的對象。所以,兩者的reference肯定是不同的:
public static void main(String... args) { String s1 = "abcd"; String s2 = new String("abcd"); System.out.println(s1 == s2); // false }
而constant pool中的常量是可以被共享用于節(jié)省內(nèi)存開銷和創(chuàng)建時間的開銷(這也是引入constant pool的原因)。例如:
public static void main(String... args) { String s1 = "abcd"; String s2 = "abcd"; System.out.println(s1 == s2); // true }
結合這兩者,其實還可以回答另外一個常見的面試題目:
public static void main(String... args) { String s = new String("abcd"); }
這句話創(chuàng)建了幾個對象?
首先毫無疑問,"abcd"本身是一個對象,被放于常量池。而由于這里使用了new關鍵字,所以s得到的對象必然是被創(chuàng)建在heap里的。所以,這里其實一共創(chuàng)建了2個對象。
但tricky的部分是,如果在這個函數(shù)被調(diào)用前的別的地方,已經(jīng)有了"abcd"這個字符串,那么它就事先在constant pool中被創(chuàng)建了出來。此時,這里就只會創(chuàng)建一個對象,即創(chuàng)建在heap的new String("abcd")對象。
但String的內(nèi)存分配,遠遠沒有這么簡單。對于String的拼接,需要做更深入的理解和思考。
2. String的拼接下面看一個問題:
public static void main(String... args) { String s1 = "hell" + "o"; String s2 = "h" + "ello"; System.out.println(s1 == s2); // true System.out.println(s1 == "hello"); // true System.out.println(s2 == "hello"); // true System.out.println("hello" == "hello"); // true // ------------------------ String c1 = "ab"; String c2 = c1 + "c"; System.out.println(c2 == "abc"); // false }
前面四個輸出其實很容易理解,最終的結果,都指向了constant pool里的一個常量"hello"。但奇怪的是,最后一個輸出也是"abc",并且還都是用指向constant pool中常量的變量來做的拼接,但卻得到了一個false的結果。
原因是,Java的String拼接有兩個規(guī)則:
對于拼接的值,如果都是雙引號包裹字符串的形式,則將結果放于constant pool,如果constant pool已經(jīng)有這個值了,則直接返回這個已有值。
而如果拼接的值中,有一個是非雙引號包裹字符串的形式,則從heap中開辟一個新的區(qū)域保存常量。也即是使用了String變量來做拼接的情況。
在這樣的大原則下,對聲明為final的String變量需要做更多的考慮:
如果String變量被聲明為final時就已經(jīng)被賦值,則它被編譯器自動處理為常量,因而它就會被當作常量池的變量來處理。
public class ConstantPool { public static final String s1 = "ab"; public static final String s2 = "cd"; public static void main(String... args) { String s = s1 + s2; String ss = "abcd"; System.out.println(s == ss); // true } }
而如果聲明為final的字符串沒有在聲明時被賦值,則編譯器無法事先決定它的準確值,所以依舊會把它當作一個變量來處理。
public class ConstantPool { public static final String s1; public static final String s2; static { s1 = "ab"; s2 = "cd"; } public static void main(String... args) { String s = s1 + s2; String ss = "abcd"; System.out.println(s == ss); // false } }3. intern()方法
String.intern()方法,可以在runtime期間將常量加入到常量池(constant pool)。它的運作方式是:
如果constant pool中存在一個常量恰好等于這個字符串的值,則inter()方法返回這個存在于constant pool中的常量的引用。
如果constant pool不存在常量恰好等于這個字符串的值,則在constant pool中創(chuàng)建一個新的常量,并將這個字符串的值賦予這個新創(chuàng)建的在constant pool中的常量。intern()方法返回這個新創(chuàng)建的常量的引用。
示例:
public static void main(String... args) { String s1 = "abcd"; String s2 = new String("abcd"); /** * s2.intern() will first search String constant pool, * of which the value is the same as s2. */ String s3 = s2.intern(); // As s1 comes from constant pool, and s3 is also comes from constant pool, they"re same. System.out.println(s1 == s3); // As s2 comes from heap but s3 comes from constant pool, they"re different. System.out.println(s2 == s3); } /** * Output: * true * false */
回顧到最開始的第一部分,為什么要引入intern()這個函數(shù)呢?就是因為,雖然"abcd"是被分配在constant pool里的,但是,一旦使用new String("abcd")就會在heap中新創(chuàng)建一個值為abcd的對象出來。試想,如果有100個這樣的語句,豈不是就要在heap里創(chuàng)建100個同樣值的對象?!這就造成了運行的低效和空間的浪費。
于是,如果引入了intern()它就會直接去constant pool找尋是否有值相同的String對象,這就極大地節(jié)省了空間也提高了運行效率。
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://www.ezyhdfw.cn/yun/77062.html
摘要:常量池探秘每個文件編譯為文件后,都將產(chǎn)生當前類獨有的常量池,我們稱之為靜態(tài)常量池。文件中的常量池包含兩部分字面值和符號引用。方法的調(diào)用成員變量的訪問最終都是通過運行時常量池來查找具體地址的。其中,表示將一個常量加載到操作數(shù)棧。 java中講的常量池,通常指的是運行時常量池,它是方法區(qū)的一部分,一個jvm實例只有一個運行常量池,各線程間共享該運行常量池。 java內(nèi)存模型中將內(nèi)存分為堆和...
摘要:那方法區(qū)里都存著什么呢先拋結論靜態(tài)變量常量類信息構造方法接口定義運行時常量池存在方法區(qū)中。動態(tài)常量池運行時常量池是方法區(qū)的一部分,是一塊內(nèi)存區(qū)域。文件常量池將在類加載后進入方法區(qū)的運行時常量池中存放。 一、方法區(qū)與永久代 這兩個是非常容易混淆的概念,永久代的對象放在方法區(qū)中,就會想當然地認為,方法區(qū)就等同于持久代的內(nèi)存區(qū)域。事實上兩者是這樣的關系: 《Java虛擬機規(guī)范》只是規(guī)定了有方...
摘要:的三種常量池此外,有三種常量池,即字符串常量池又叫全局字符串池文件常量池運行時常量池。開始虛擬機把字符串常量池位置從永久代挪到堆,又徹底取消,把諸如之類的元數(shù)據(jù)都挪到堆之外管理。 前言 前陣子和朋友討論一個問題: 字符串常量歸常量池管理,那比如 String str = abc; abc這個對象是放在內(nèi)存中的哪個位置,是字符串常量池中還是堆? 這句代碼的abc當然在常量池中,只有n...
摘要:為了減少在中創(chuàng)建的字符串的數(shù)量,字符串類維護了一個字符串常量池。但是當執(zhí)行了方法后,將指向字符串常量池中的那個字符串常量。由于和都是字符串常量池中的字面量的引用,所以。究其原因,是因為常量池要保存的是已確定的字面量值。 String,是Java中除了基本數(shù)據(jù)類型以外,最為重要的一個類型了。很多人會認為他比較簡單。但是和String有關的面試題有很多,下面我隨便找兩道面試題,看看你能不能...
摘要:類是類它內(nèi)部的方法也默認被修飾不能重寫字符串常量池當這樣聲明一個字符串會檢測字符串常量池中是否存在這個值的字符串如果存在就直接賦值給否則創(chuàng)建一個新的再賦值給當連續(xù)用同樣的方式聲明兩個字符串并作比較結果為這個操作符比較的是什么對于基本變量比較 String類是final類,它內(nèi)部的方法也默認被final修飾,不能重寫. 字符串常量池 當這樣聲明一個字符串 String str = h...
摘要:證明返回常量池中已存在的對象,不等于新建的對象。為什么要設計成一下內(nèi)容來自發(fā)現(xiàn)百度的中文版本基本也是此文的翻譯版??傊?,安全性和字符串常量池緩存是被設計成不可變的主要原因。 String是Java中最常用的類,是不可變的(Immutable), 那么String是如何實現(xiàn)Immutable呢,String為什么要設計成不可變呢? 前言 關于String,收集一波基礎,來源標明最后,不確...
閱讀 2664·2021-11-15 11:38
閱讀 2976·2021-11-02 14:44
閱讀 3935·2021-09-26 10:13
閱讀 3167·2021-08-13 15:02
閱讀 853·2019-08-30 15:56
閱讀 1629·2019-08-30 15:53
閱讀 2404·2019-08-30 13:01
閱讀 3293·2019-08-29 12:57