String內(nèi)存分配和intern方法

轉(zhuǎn)載启盛、引用請標(biāo)明出處
http://www.reibang.com/p/d5ecfceccccd
本文出自zhh_happig的簡書博客,謝謝

以下內(nèi)容,是本人學(xué)習(xí)的筆記和工作中的總結(jié),僅供大家參考备畦,有誤的地方還請指正

一 String.intern

1 String為什么不可變性

  • 字符串常量池的需要
    • 字符串常量池的誕生是為了提升效率和減少內(nèi)存分配
    • 程序有大部分時(shí)間在處理字符串,字符串很大概率會出現(xiàn)重復(fù)的情況许昨。String的不可變性使常量池很容易被管理和優(yōu)化
  • 安全性考慮
    • 字符串使用頻繁懂盐,設(shè)計(jì)成不可變,有效防止字符串被有意或者無意的篡改
    • String類被final修飾糕档,同時(shí)所有的屬性都被final修飾莉恼,即不可變
  • 作為HashMap、HashTable等hash型數(shù)據(jù)key的必要

2 String常量池的設(shè)計(jì)

  • 字符串常量存儲在方法區(qū)的PermGen Space。在jdk1.7之后类垫,字符串常量重新被移到了堆中
  • 常量池指的是在編譯期被確定司光,并被保存在已編譯的.class文件中的一些數(shù)據(jù)。它包括了關(guān)于類悉患、方法残家、接口等中的常量,也包括字符串常量售躁。
  • Java會確保一個(gè)常量池中相同的字符串常量有且僅有一個(gè)

3 String.intern方法

  • String str="kvill" 和 String str=new String("kvill")的區(qū)別
    • "kvill"都是字符串常量坞淮,它們在編譯期就被確定了, 會在常量池中創(chuàng)建一個(gè)"kvill"字符串對象
    • 用new String("kvill") 創(chuàng)建的字符串不是字符串常量,不能在編譯期就確定陪捷,所以new String() 創(chuàng)建的字符串不放入常量池中回窘,存放在堆空間
  • String對象的創(chuàng)建
String str1 = new String("kvill");
String str2 = new String("kvill");

new str1時(shí)創(chuàng)建了兩個(gè)對象市袖,先在常量池中創(chuàng)建的"kvill"對象啡直,再在堆中創(chuàng)建string對象,注意這個(gè)創(chuàng)建的先后順序;
new str2時(shí)創(chuàng)建一個(gè)對象苍碟,堆中的另外一個(gè)string對象

String s1=new String("str") + new String("01");//會在堆中新建s1對象"str01"; 
String s2 = new String(s1);

這種方式創(chuàng)建s2的過程中酒觅,并不會去常量池中創(chuàng)建s1的"str01"對象,而是僅在堆里創(chuàng)建一個(gè)s2對象

示例1
String s0="kvill";
String s1="kvill";
String s2="kv" + "ill";
System.out.println( s0==s1 );//true
System.out.println( s0==s2 );//true

s0和s1中的"kvill"都是字符串常量微峰,它們在編譯期就被確定了, 存在常量池中舷丹,且只有一個(gè), 所以s0==s1為true
而"kv"和"ill"也都是字符串常量,編譯階段會直接合成一個(gè)字符串蜓肆,進(jìn)而去常量池中查找是否存在"kvill"颜凯,所以s2在編譯期就被解析為一個(gè)字符串常量,它也是常量池中"kvill"的一個(gè)引用仗扬,s0==s2為true

示例2
String s0="kvill";
String s1=new String("kvill");
String s2="kv" + new String("ill");
System.out.println( s0==s1 );//false
System.out.println( s0==s2 );//false
System.out.println( s1==s2 );//false

s0還是常量池中"kvill"的引用症概,s1因?yàn)闊o法在編譯期確定,所以是運(yùn)行時(shí)創(chuàng)建的新對象"kvill"的引用
s2因?yàn)橛泻蟀氩糠謓ew String("ill")所以也無法在編譯期確定厉颤,所以也是一個(gè)新創(chuàng)建對象"kvill"的引用

  • String.intern()方法的作用
    • 在jdk1.6中穴豫,當(dāng)一個(gè)String實(shí)例str調(diào)用intern()方法時(shí)凡简,Java查找常量池中是否有相同Unicode的字符串常量逼友,如果有,則返回常量池中字符串常量的引用秤涩,如果沒有帜乞,則在常量池中增加一個(gè)Unicode等于str的字符串并返回它的引用
    • 而在jdk1.7,當(dāng)一個(gè)String實(shí)例str調(diào)用intern()方法時(shí)筐眷,Java查找常量池中是否有相同Unicode的字符串常量黎烈,如果有,則返回常量池中字符串常量的引用,這一點(diǎn)和jdk1.6沒什區(qū)別照棋。區(qū)別在于资溃,如果沒有,則不會再在常量池中增加一個(gè)Unicode等于str的字符串烈炭,而只是在常量池中生成一個(gè)指向堆中的str對象的引用溶锭,并返回
示例3
String s0= "kvill";
String s1=new String("kvill");
String s2=new String("kvill");
System.out.println( s0==s1 );//false
s1.intern();
s2=s2.intern(); //把常量池中"kvill"的引用賦給s2
System.out.println( s0==s1);//false,雖然執(zhí)行了s1.intern(),但它的返回值沒有賦給s1
System.out.println( s0==s1.intern());//true, s1.intern()返回的是常量池中"kvill"的引用
System.out.println( s0==s2 );//true

二 str2.intern()在jdk7和jdk6的重點(diǎn)分析

1 str2.intern()在jdk7和jdk6的區(qū)別

示例4
  • jdk7
String str2 = new String("str")+new String("01");//生成了3個(gè)對象, 常量池中對象"str"和"01",堆中的字符串對象"str01"
str2.intern(); //jdk1.7中符隙,在常量池中找不到對象"str01"趴捅,所以會在常量池生成一個(gè)引用,指向堆中的字符串對象"str01"
String str1 = "str01";//這句話是直接在常量池中生成"str01"對象霹疫,由于在常量池中有這么一個(gè)引用拱绑,指向堆中的字符串對象"str01",所以將這個(gè)引用給str1丽蝎,而不會再在常量池中生成"str01"對象了猎拨。
System.out.println(str2==str1);//所以str1和str2是指向同一個(gè)對象,jdk1.7中返回true
  • jdk6
String str2 = new String("str")+new String("01");//生成了3個(gè)對象, 常量池中對象"str"和"01"屠阻,堆中的字符串對象"str01"
str2.intern(); //jdk1.6會在常量池生成一個(gè)字符串對象"str01"
String str1 = "str01";//str1指向常量池字符串對象"str01"
System.out.println(str2==str1);//由于str2和str1指向不同迟几,jdk1.6中返回false

2 str2.intern()在jdk1.7典型問題分析

示例5
String str2 = new String("str")+new String("01");
str2.intern();
String str1 = "str01";
System.out.println(str2==str1);//jdk1.7中返回true。

調(diào)換代碼順序

String str2 = new String("str")+new String("01");//生成了3個(gè)對象, 常量池中對象"str"和"01"栏笆,堆中的字符串對象"str01"
String str1 = "str01";//直接在常量池中生成"str01"對象
str2.intern(); //jdk1.7中类腮,在常量池中已經(jīng)有對象"str01"了,返回常量池中"str01"對象的引用
System.out.println(str2==str1);//所以str1和str2是指向不同對象蛉加,返回false
示例6
String str2 = new String("str");//生成了2個(gè)對象, 常量池中對象"str"蚜枢,堆中的字符串對象"str"
str2.intern(); //jdk1.7中,在常量池中已經(jīng)有對象"str"了针饥,返回常量池中"str01"對象的引用
String str1 = "str";//str1直接指向常量池中已有的"str01"對象
System.out.println(str2==str1);//所以str1和str2是指向不同對象厂抽,返回false
示例7
String s1 = new String("str");//這句代碼執(zhí)行時(shí),先在常量池中創(chuàng)建的"str"對象丁眼,即使發(fā)現(xiàn)"str"對象不存在筷凤,也無法生成指向堆中的字符串對象s1的引用,因?yàn)榇藭r(shí)堆中的字符串對象s1還沒有被創(chuàng)建苞七,所以最終還是在常量池中創(chuàng)建的"str"對象藐守。
System.out.println(s1.intern() == s1);//s1.intern()指向了常量池中的"str"對象,s1指向了堆蹂风,所以返回false
String s1=new String("str") + new String("01");//生成了3個(gè)對象卢厂,常量池中對象"str"和"01",堆中的字符串對象"str01"
System.out.println(s1.intern() == s1);//注意s1.intern()惠啄,"str01"在常量池中不存在, 所以會返回s1的引用慎恒;這里返回true任内,注意與上面的區(qū)別
示例8
String s1=new String("str") + new String("01");//生成了3個(gè)對象,常量池中對象"str"和"01"融柬,堆中的字符串對象"str01"
String s2 = new String(s1);//這種方式創(chuàng)建String死嗦,并不會去常量池創(chuàng)建s1中的"str01"對象,而是僅在堆里創(chuàng)建一個(gè)s2對象
System.out.println(s2.intern() == s2); //s2.intern()發(fā)現(xiàn)沒常量池中沒有"str01"對象, 將堆中s2的引用返回粒氧;結(jié)果為true

三 其他問題

String s1 = “abc”;
String s2 = “a”;
String s3 = “bc”;
String s4 = s2 + s3;
System.out.println(s1 == s4);
輸出false越走,因?yàn)閟2+s3實(shí)際上是使用StringBuilder.append來完成,會生成不同的對象靠欢。

String s1 = “abc”;
final String s2 = “a”;
final String s3 = “bc”;
String s4 = s2 + s3;
System.out.println(s1 == s4);
輸出true廊敌,因?yàn)閒inal變量在編譯后會直接替換成對應(yīng)的值,所以實(shí)際上等于s4=“a”+”bc”门怪,而這種情況下骡澈,編譯器會直接合并為s4=“abc”,所以最終s1==s4掷空。

以上內(nèi)容肋殴,是本人學(xué)習(xí)的筆記和工作中的總結(jié),僅供大家參考坦弟,有誤的地方還請指正

轉(zhuǎn)載护锤、引用請標(biāo)明出處
http://www.reibang.com/p/d5ecfceccccd
本文出自zhh_happig的簡書博客,謝謝

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末酿傍,一起剝皮案震驚了整個(gè)濱河市烙懦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌赤炒,老刑警劉巖氯析,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異莺褒,居然都是意外死亡掩缓,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門遵岩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來你辣,“玉大人,你說我怎么就攤上這事尘执∩岷澹” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵正卧,是天一觀的道長蠢熄。 經(jīng)常有香客問我跪解,道長炉旷,這世上最難降的妖魔是什么签孔? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮窘行,結(jié)果婚禮上饥追,老公的妹妹穿的比我還像新娘。我一直安慰自己罐盔,他們只是感情好但绕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著惶看,像睡著了一般捏顺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上纬黎,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天幅骄,我揣著相機(jī)與錄音,去河邊找鬼本今。 笑死拆座,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的冠息。 我是一名探鬼主播挪凑,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼逛艰!你這毒婦竟也來了躏碳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤散怖,失蹤者是張志新(化名)和其女友劉穎唐断,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體杭抠,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡脸甘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了偏灿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丹诀。...
    茶點(diǎn)故事閱讀 40,102評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖翁垂,靈堂內(nèi)的尸體忽然破棺而出铆遭,到底是詐尸還是另有隱情,我是刑警寧澤沿猜,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布枚荣,位于F島的核電站,受9級特大地震影響啼肩,放射性物質(zhì)發(fā)生泄漏橄妆。R本人自食惡果不足惜衙伶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望害碾。 院中可真熱鬧矢劲,春花似錦、人聲如沸慌随。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽阁猜。三九已至丸逸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間剃袍,已是汗流浹背椭员。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留笛园,地道東北人隘击。 一個(gè)月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像研铆,于是被迫代替她去往敵國和親埋同。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評論 2 355