1. 程式人生 > >Java中物件儲存位置

Java中物件儲存位置

1. 棧(stack)與堆(heap)都是Java用來在Ram中存放資料的地方。與C++不同,Java自動管理棧和堆,程式設計師不能直接地設定棧或堆。
2. 棧的優勢是,存取速度比堆要快,僅次於直接位於CPU中的暫存器。但缺點是,存在棧中的資料大小與生存期必須是確定的,缺乏靈活性。另外,棧資料可以共享,詳見第3點。堆的優勢是可以動態地分配記憶體大小,生存期也不必事先告訴編譯器,Java的垃圾收集器會自動收走這些不再使用的資料。但缺點是,由於要在執行時動態分配記憶體,存取速度較慢。
3. Java中的資料型別有兩種。
一種是基本型別(primitive types), 共有8種,即int, short, long, byte, float, double, boolean, char(注意,並沒有string的基本型別)。這種型別的定義是通過諸如int a = 3; long b = 255L;的形式來定義的,稱為自動變數。值得注意的是,自動變數存的是字面值,不是類的例項,即不是類的引用,這裡並沒有類的存在。如int a = 3; 這裡的a是一個指向int型別的引用,指向3這個字面值。這些字面值的資料,由於大小可知,生存期可知(這些字面值固定定義在某個程式塊裡面,程式塊退出後,欄位值就消失了),出於追求速度的原因,就存在於棧中。
另外,棧有一個很重要的特殊性,就是存在棧中的資料可以共享。假設我們同時定義
  int a = 3; 
  int b = 3;
編譯器先處理int a = 3;首先它會在棧中建立一個變數為a的引用,然後查詢有沒有字面值為3的地址,沒找到,就開闢一個存放3這個字面值的地址,然後將a指向3的地址。接著處理int b = 3;在建立完b的引用變數後,由於在棧中已經有3這個字面值,便將b直接指向3的地址。這樣,就出現了a與b同時均指向3的情況。
特別注意的是,這種字面值的引用與類物件的引用不同。假定兩個類物件的引用同時指向一個物件,如果一個物件引用變數修改了這個物件的內部狀態,那麼另一個物件引用變數也即刻反映出這個變化。相反,通過字面值的引用來修改其值,不會導致另一個指向此字面值的引用的值也跟著改變的情況。如上例,我們定義完a與 b的值後,再令a=4;那麼,b不會等於4,還是等於3。在編譯器內部,遇到a=4;時,它就會重新搜尋棧中是否有4的字面值,如果沒有,重新開闢地址存放4的值;如果已經有了,則直接將a指向這個地址。因此a值的改變不會影響到b的值。
另一種是包裝類資料,如Integer, String, Double等將相應的基本資料型別包裝起來的類。這些類資料全部存在於堆中,Java用new()語句來顯示地告訴編譯器,在執行時才根據需要動態建立,因此比較靈活,但缺點是要佔用更多的時間。
4. String是一個特殊的包裝類資料。即可以用String str = new String("abc");的形式來建立,也可以用String str = "abc";的形式來建立(作為對比,在JDK 5.0之前,你從未見過Integer i = 3;的表示式,因為類與字面值是不能通用的,除了String。而在JDK 5.0中,這種表示式是可以的!因為編譯器在後臺進行Integer i = new Integer(3)的轉換)。前者是規範的類的建立過程,即在Java中,一切都是物件,而物件是類的例項,全部通過new()的形式來建立。Java 中的有些類,如DateFormat類,可以通過該類的getInstance()方法來返回一個新建立的類,似乎違反了此原則。其實不然。該類運用了單例模式來返回類的例項,只不過這個例項是在該類內部通過new()來建立的,而getInstance()向外部隱藏了此細節。那為什麼在String str = "abc";中,並沒有通過new()來建立例項,是不是違反了上述原則?其實沒有。
5. 關於String str = "abc"的內部工作。Java內部將此語句轉化為以下幾個步驟:
(1)先定義一個名為str的對String類的物件引用變數:String str;
(2)在棧中查詢有沒有存放值為"abc"的地址,如果沒有,則開闢一個存放字面值為"abc"的地址,接著建立一個新的String類的物件o,並將o 的字串值指向這個地址,而且在棧中這個地址旁邊記下這個引用的物件o。如果已經有了值為"abc"的地址,則查詢物件o,並返回o的地址。
(3)將str指向物件o的地址。
值得注意的是,一般String類中字串值都是直接存值的。但像String str = "abc";這種場合下,其字串值卻是儲存了一個指向存在棧中資料的引用!