在Java代碼中,常常會使用到這樣的類的聲明實例化:
Person per = new Person();
//這其實是包含了兩個步驟,聲明和實例化
Person per = null; //聲明一個名為Person類的對象per
per = new Person(); // 實例化這個per對象
聲明 指的是創建類的對象的過程;
實例化 指的是用關鍵詞new來開辟內存空間。
它們在內存中的划分是這樣的:
那什么是棧內存(heap)和棧內存(heap)呢?
棧內存:
在函數中定義的一些基本類型的變量和對象的引用變量都在函數的棧內存中分配。棧內存主要存放的是基本類型類型的數據 如( int, short, long, byte, float, double, boolean, char) 和對象句柄。注意:並沒有String基本類型、在棧內存的數據的大小及生存周期是必須確定的、其優點是寄存速度快、棧數據可以共享、缺點是數據固定、不夠靈活。
棧的共享:
String str1 = "myString"; String str2 = "myString"; System.out.println(str1 ==str2 ); //注意:這里使用的是str1 ==str2,而不是str1.equals(str2)的方式。 //因為根據JDK的說明,==號只有在兩個引用都指向了同一個對象時才返回真值 //而str1.equals(str2),只是比較兩個字符串是否相等
結果為True,這就說明了str1和str2其實指向的是同一個值。
上述代碼的原理是,首先在棧中創建一個變量為str1的引用,然后查找棧中是否有myString這個值,如果沒找到,就將myString存放進來,然后將str1指向myString。接着處理String str2 = "myString";;在創建完str2 的引用變量后,因為在棧中已經有myString這個值,便將str2 直接指向myString。這樣,就出現了str1與str2 同時指向myString。
特別注意的是,這種字面值的引用與類對象的引用不同。假定兩個類對象的引用同時指向一個對象,如果一個對象引用變量修改了這個對象的內部狀態,那么另一個對象引用變量也即刻反映出這個變化。相反,通過字面值的引用來修改其值,不會導致另一個指向此字面值的引用的值也跟着改變的情況。如上例,我們定義完str1與str2 的值后,再令str1=yourString;那么,str2不會等於yourString,還是等於myString。在編譯器內部,遇到str1=yourString;時,它就會重新搜索棧中是否有yourString的字面值,如果沒有,重新開辟地址存放yourString的值;如果已經有了,則直接將str1指向這個地址。因此str1值的改變不會影響到str2的值。
堆內存:
堆內存用來存放所有new 創建的對象和 數組的數據
String str1 = new String ("myString"); String str2 = "myString"; System.out.println(str1 ==str2 ); //False String str1 = new String ("myString"); String str2 = new String ("myString"); System.out.println(a==b); //False
創建了兩個引用,創建了兩個對象。兩個引用分別指向不同的兩個對象。以上兩段代碼說明,只要是用new()來新建對象的,都會在堆中創建,而且其字符串是單獨存值的,即使與棧中的數據相同,也不會與棧中的數據共享。