Java实例化对象过程中的内存分配
問題引入
這里先定義一個很不標準的“書”類,這里為了方便演示就不對類的屬性進行封裝了。
class Book{String name; //書名double price; //價格public void getInfo(){System.out.println("name:"+name+";price:"+price);} }在這個類中定義了兩個屬性和一個方法,當然也是可以定義多和類和多個方法的。 類現在雖然已經定義好了,但是一個類要使用它必須要實例化對象,那么對象的定義格式有一下兩種格式:
//聲明并實例化對象: 類名稱 對象名稱 = new 類名稱() Book book = new Book(); //分步完成聲明和實例操作: // |- 聲明對象: 類名稱 對象名稱 = null; Book book = null; // |- 實例化對象: 對象名稱 = new 類名稱(); book = new Book();對象屬于引用數據類型,其和基本數據類型最大的不同在于引用數據類型需要進行內存分配,而關鍵字new主要的功能就是開辟內存空間,也就是說只要是使用引用數據類型就必須使用關鍵字new來開辟空間。有些時候我們需要對對象屬性進行操作,那么其中的堆棧內存空間又是如何分配的呢?接下來我們來分析一下其中的過程。
堆內存與棧內存
如果想對對象操作的過程進行內存分析,首先要了解兩塊內存空間的概念:
- 堆內存:保存每一個對象的屬性內容,堆內存需要用關鍵字new才能開辟。
- 棧內存:保存的是一塊堆內存的地址。
堆內存很好理解,可能有人會有疑問為什么會有棧內存,舉個例子,好比學校有很多教室,每個教室有一個門牌號,教室內放了很多的桌椅等等,這個編號就好比地址,老師叫小明去一個教室拿東西,老師必須把房間號告訴小明才能拿到,也就是為什么地址必須存放在一個地方,而這個地方在計算機中就是棧內存。
對象空屬性
我們先實例化一個對象,并對其的屬性不設置任何值
public class Test{public static void main(String args[]){Book book = new Book();book.getInfo();} }運行結果如下:
name:null;price:0.0其內存變化圖如下:
使用關鍵字new就在棧內存中開辟一個空間存放book對象,并且指向堆內存的一個空間,此時并未對其賦值,所以始終指向默認的堆內存空間。
操作對象屬性
我們先聲明并實例化Book類,并對實例出的book對象操作其屬性內容。
public class Test{public static void main(String args[]){Book book = new Book();book.name = "深入理解JVM";book.price = 99.8;book.getInfo();} }編譯執行后的結果如下:
name:深入理解JVM;price:99.8內存變化圖如下:
分步實例化對象
示例代碼如下:
public class Test{public static void main(String args[]){Book book = null; //聲明對象book = new Book(); //實例化對象book.name = "深入理解JVM";book.price = 99.8;book.getInfo();} }很明顯結果肯定和前面一樣
name:深入理解JVM;price:99.8表面沒什么區別,但是內存分配過程卻不一樣,接下來我們來分析一下
任何情況下只要使用了new就一定要開辟新的堆內存空間,一旦堆內存空間開辟了,里面就一定會所有類中定義的屬性內容,此時所有的屬性內容都是其對應數據類型的默認值。
直觀的說就是棧內存先要指向一個null,然后等待開辟新的棧內存空間后才能指向其屬性內容。
NullPointerException的出現
那么如果使用了沒有實例化的對象,就會出現最常見也是最讓人頭疼的一個異常NullPointerException,像下面的代碼
public class Test{public static void main(String args[]){Book book = null; // book = new Book(); //實例化的這一步被注釋book.name = "深入理解JVM";book.price = 99.8;book.getInfo();} }在編譯的過程是不會出錯的,因為只有語法錯誤才會在編譯時中斷,而這種邏輯性錯誤能成功編譯,但是執行的時候卻會拋出NullPointerException異常。 運行結果:
Exception in thread "main" java.lang.NullPointerException at language.Test.main(Test.java:19)空指針異常是平時遇到最多的一類異常,只要是引用數據類型都有可能出現它。這種異常的出現也是很容易理解的,猶如你說今天被一只恐龍追著跑,恐龍早就在幾個世紀前就滅絕了,現實生活中不可能存在,當然人們就會認為你說的這句話是謊言。在程序中也一樣,沒有被實例化的對象直接調用其中的屬性或者方法,肯定會報錯。
引用數據分析
引用是整個java中的核心精髓,引用類似于C++中的指針概念,但是又比指針的概念更加簡單。 舉個簡單的例子,比如李華的小名叫小華,一天李華因為生病向老師請假了,老師問今天誰請假了,說李華請假了和小華請假了都是一個意思,小華是李華的別名,他們兩個都是對應一個個體。 如果代碼里面聲明兩個對象,并且使用了關鍵字new為兩個對象分別進行了對象的實例化操作,那么一定是各自占用各自的堆內存空間,并且不會互相影響。
例如:聲明兩個對象
public class Test{public static void main(String args[]){Book bookA = new Book();Book bookB = new Book();bookA.name = "深入理解JVM";bookA.price = 99.8;bookA.getInfo();bookB.name = "Java多線程";bookB.price = 69.8;bookB.getInfo();} }運行結果如下:
name:深入理解JVM;price:99.8 name:Java多線程;price:69.8我們來分析一下內存的變化
接下來我們看看那對象引用傳遞
例如:對象引用傳遞
public class Test{public static void main(String args[]){Book bookA = new Book(); //聲明并實例化對象Book bookB = null; //聲明對象bookA.name = "深入理解JVM";bookA.price = 99.8;bookB = bookA; //引用傳遞bookB.price = 69.8;bookA.getInfo();} }運行結果如下:
name:深入理解JVM;price:69.8嚴格來講bookA和bookB里面保存的是對象的地址信息,所以以上的引用過程就屬于將bookA的地址賦給了bookB,此時兩個對象指向的是同一塊堆內存空間,因此任何一個對象修改了堆內存之后都會影響其他對象。
一塊堆內存可以同時被多個棧內存所指向,但是反過來,一塊棧內存只能保存一塊堆內存空間的地址。
垃圾的產生
先看如下代碼:
public class Test{public static void main(String args[]){Book bookA = new Book(); //聲明并實例化對象Book bookB = new Book(); //聲明并實例化對象bookA.name = "深入理解JVM";bookA.price = 99.8;bookB.name = "Java多線程";bookB.price = 69.8;bookB = bookA; //引用關系bookB.price = 120.8;bookA.getInfo();} }運行結果如下:
name:深入理解JVM;price:120.8整個過程內存又發生了什么變化呢?我們來看一下
在此過程中原來bookB所指向的堆內存無棧內存指向,一塊沒有任何棧內存指向的堆內存空間就將成為垃圾,等待被java中的回收機制回收,回收之后會釋放掉其占用的空間。
雖然在java中支持了自動的垃圾收集處理,但是在代碼的編寫過程中應該盡量減少垃圾空間的產生。
END
總結
以上是生活随笔為你收集整理的Java实例化对象过程中的内存分配的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 挖财信用卡管家安全吗
- 下一篇: 支付宝怎么办信用卡 支付宝办信用卡需要什