程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java回顧之Java自動管理棧和堆

Java回顧之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" ;這種場合下,其字符串值卻是保存了一個指向存在棧中數據的引用!

為了更好地說明這個疑問 ,我們可以通過以下的多個 代碼執行 驗證。

String str1 = "abc" ;

String str2 = "abc" ;

System.out.println(str1==str2); //true

留心 ,我們這裡並不用str1.equals(str2);的方式,因為這將比較兩個字符串的值能不能 相等。==號,根據JDK的說明,只有在兩個引用都指向了同一個對象時才返回真值。而我們在這裡要看的是,str1與str2能不能 都指向了同一個對象。

結果說明,JVM建立 了兩個引用str1和str2,但只建立 了一個對象,而且兩個引用都指向了這個對象。

我們再來更進一步,將以上代碼改成:

String str1 = "abc" ;

String str2 = "abc" ;

str1 = "bcd" ;

System.out.println(str1 + "," + str2); //bcd, abc

System.out.println(str1==str2); //false

這就是說,賦值的變化導致了類對象引用的變化,str1指向了另外一個新對象!而str2仍舊指向原來的對象。上例中,當我們將str1的值改為"bcd" 時,JVM發覺 在棧中沒有存放該值的地址,便開辟了這個地址,並建立 了一個新的對象,其字符串的值指向這個地址。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved