盡管Java是基于C++的,但是相比之下,Java是更純粹的面向?qū)ο蟪绦蛟O(shè)計語言。
Java語言盡管將一切都視為對象,但實際上操控著程序的是對象的一個引用(reference)吕粗。可以將這種情形想象成遙控器(引用)控制著電視機(對象)巴帮。如果沒有電視機溯泣,遙控器也可單獨存在,即可能存在不指向任何對象的引用榕茧,如果意圖使用空引用進行任何操作垃沦,都會觸發(fā)空指針異常錯誤(java.lang.NullPointerException)。因此一種安全的做法是創(chuàng)建引用時便進行初始化用押,比如 :
String s = "當(dāng)時只道是尋常"
更通用的初始化方法通常是使用new關(guān)鍵字來關(guān)聯(lián)一個新對象肢簿。
String s = new String("當(dāng)時只道是尋常")
String對象是Java語言中重要的數(shù)據(jù)類型,但它并不是Java的基本數(shù)據(jù)類型蜻拨。雖然有兩種初始化方式池充,然而過程卻不一樣。
String str1 = new String("ABC");
String str2 = new String("ABC");
str1 == str2 //false
String str1 = “ABC”;可能創(chuàng)建一個或者不創(chuàng)建對象缎讼,如果”ABC”這個字符串在java String池里不存在收夸,會在java String池里創(chuàng)建一個創(chuàng)建一個String對象(“ABC”),然后str1指向這個內(nèi)存地址血崭,無論以后用這種方式創(chuàng)建多少個值為”ABC”的字符串對象卧惜,始終只有一個內(nèi)存地址被分配厘灼,之后的都是String的拷貝,Java中稱為“字符串駐留”咽瓷,所有的字符串常量都會在編譯之后自動地駐留设凹。
String str3 = "ABC";
String str4 = "ABC";
String str5 = "AB" + "C";
str3 == str4 //true
str3 == str5 // true
String str2 = new String(“ABC”);至少創(chuàng)建一個對象,也可能兩個茅姜。因為用到new關(guān)鍵字闪朱,肯定會在heap中創(chuàng)建一個str2的String對象,它的value是“ABC”钻洒。同時如果這個字符串在java String池里不存在奋姿,會在java池里創(chuàng)建這個String對象“ABC”。
String a = "ABC";
String b = "AB";
String c = b + "C";
a == c //false
a航唆、b在編譯時就已經(jīng)被確定了胀蛮,而c是引用變量,不會在編譯時就被確定糯钙。
這就涉及到了Java中String對象的不可變性,什么叫不可變性呢退腥,簡單的說就是一旦一個String對象被創(chuàng)建并被賦值(初始化)這個對象的值就不會變化任岸。
一旦一個String對象在內(nèi)存中創(chuàng)建,它將是不可改變的狡刘,所有的String類中方法并不是改變String對象自己享潜,而是重新創(chuàng)建一個新的String對象。
建議在平時的使用中嗅蔬,盡量使用String = “abcd”;這種方式來創(chuàng)建字符串剑按,而不是String = new String(“abcd”);這種形式,因為使用new構(gòu)造器創(chuàng)建字符串對象一定會開辟一個新的heap空間澜术,而雙引號則是采用了String interning(字符串駐留)進行了優(yōu)化艺蝴,效率比構(gòu)造器高。
關(guān)于更多String對象的特性鸟废,請移步 http://book.51cto.com/art/201504/472207.htm
程序運行時猜敢,創(chuàng)建的對象和指向?qū)ο蟮囊檬侨绾芜M行存儲的?內(nèi)存又是如何分配的盒延?
寄存器(register)缩擂。這是最快的存儲區(qū),因為它位于不同于其他存儲區(qū)的地方——處理器內(nèi)部添寺。但是寄存器的數(shù)量極其有限胯盯,所以寄存器由編譯器根據(jù)需求進行分配。你不能直接控制计露,也不能在程序中感覺到寄存器存在的任何跡象博脑。
堆棧(stack)楞捂。位于通用RAM中,但通過它的“堆棧指針”可以從處理器那里獲得支持趋厉。堆棧指針若向下移動寨闹,則分配新的內(nèi)存;若向上移動君账,則釋放那些內(nèi)存繁堡。這是一種快速有效的分配存儲方法,僅次于寄存器乡数。創(chuàng)建程序時候椭蹄,JAVA編譯器必須知道存儲在堆棧內(nèi)所有數(shù)據(jù)的確切大小和生命周期,因為它必須生成相應(yīng)的代碼净赴,以便上下移動堆棧指針绳矩。這一約束限制了程序的靈活性,所以雖然某些JAVA數(shù)據(jù)存儲在堆棧中——特別是對象引用玖翅,但是JAVA對象不存儲其中翼馆。
堆(heap)。一種通用性的內(nèi)存池(也存在于RAM中)金度,用于存放所有的JAVA對象应媚。堆不同于堆棧的好處是:編譯器不需要知道要從堆里分配多少存儲區(qū)域,也不必知道存儲的數(shù)據(jù)在堆里存活多長時間猜极。因此中姜,在堆里分配存儲有很大的靈活性。當(dāng)你需要創(chuàng)建一個對象的時候跟伏,只需要new寫一行簡單的代碼丢胚,當(dāng)執(zhí)行這行代碼時,會自動在堆里進行存儲分配受扳。當(dāng)然携龟,為這種靈活性必須要付出相應(yīng)的代價。用堆進行存儲分配和清理可能比用堆棧進行存儲分配需要更多的時間辞色。
靜態(tài)存儲(static storage)骨宠。這里的“靜態(tài)”是指“在固定的位置”。靜態(tài)存儲里存放程序運行時一直存在的數(shù)據(jù)相满。你可用關(guān)鍵字static來標(biāo)識一個對象的特定元素是靜態(tài)的层亿,但JAVA對象本身從來不會存放在靜態(tài)存儲空間里。
常量存儲(constant storage)立美。常量值通常直接存放在程序代碼內(nèi)部匿又,這樣做是安全的,因為它們永遠不會被改變建蹄。有時碌更,在嵌入式系統(tǒng)中裕偿,常量本身會和其他部分分割離開,所以在這種情況下痛单,可以選擇將其放在ROM中嘿棘。
非RAM存儲。若數(shù)據(jù)完全獨立于一個程序之外旭绒,則程序不運行時仍可存在鸟妙,并在程序的控制范圍之外。其中兩個最主要的例子便是“流式對象”和“固定對象”挥吵。對于流式對象重父,對象會變成字節(jié)流,通常會發(fā)給另一臺機器忽匈。而對于固定對象房午,對象保存在磁盤中。即使程序中止運行丹允,它們?nèi)钥杀3肿约旱臓顟B(tài)不變郭厌。對于這些類型的數(shù)據(jù)存儲,一個特別有用的技巧就是它們能存在于其他媒體中嫌松。一旦需要沪曙,甚至能將它們恢復(fù)成普通的、基于RAM的對象萎羔。Java 1.1提供了對Lightweight persistence的支持。未來的版本甚至可能提供更完整的方案碳默。
對象引用存儲在堆棧(stack)中贾陷,對象存儲于堆(heap)中,上面談到的Java String池存儲在常量存儲區(qū)嘱根,常量存儲區(qū)也是方法區(qū)的一部分髓废。 了解更多Java虛擬機 運行時數(shù)據(jù)區(qū)
以上。
參考
《Java編程思想》
http://book.51cto.com/art/201504/472207.htm (作者:葛一鳴|來源:電子工業(yè)出版社)
https://www.cnblogs.com/mayj/p/7093526.html
http://www.cnblogs.com/Cratical/archive/2012/08/21/2649985.html