說在前面的幾句廢話
前幾天我的文章一直沒有更新,大概斷了4天左右,因為外出有事,所以給耽擱了,有朋友建議,我可以集中花幾天,每天只寫文章,這樣以後覺得哪裡不妥還可以修改,覺得言之有理就果斷採納了,前一個月開始做自媒體寫文章,這段時間有很多人給我提出了寶貴的意見,真的感謝大家的支持。同時,今天也是建黨節,作為一個愛國青年,真心祝願祖國越來越好。
廢話可以不看,下面就開始今天的文章。java對象的創建操作其實我在《JVM系列之類的加載機制》一文曾經提到過,包含兩個過程:類的初始化和實例化。為此為了理解的深入,我們還需要再來看一下類的生命周期。一張圖表示:
從上面我們可以看到,對象的創建其實包含了初始化和使用兩個階段。有了這個印象之後,我們就能開始今天的文章了。先給出這篇文章的大致脈絡:
首先,介紹一下java中對象的創建基本知識然後,介紹一下對象初始化的順序接下來,介紹一下創建對象的幾種方式最後,進行一個總結。(從內存角度去分析:重點)重點
一、基本知識
我們知道,一個對象的創建過程包含兩個過程:初始化和實例化
我們在使用一個對象時,JVM首先會檢查相關類型是否已經加載並初始化,如果沒有,則JVM立即進行加載並調用類構造器完成類的初始化。在類初始化過程中或初始化完畢後,根據具體情況才會去對類進行實例化。
實例化時候,java虛擬機就會為其分配內存來存放自己及其從父類繼承過來的實例變量。在為這些實例變量分配內存的同時,這些實例變量先會被賦予默認值(零值)。在內存分配完成之後,Java虛擬機才會對新創建的對象賦予我們程式設計師給定的值。
小結:創建一個對象包含下面兩個過程:
1、類構造器完成類初始化(分配內存、賦予默認值)
2、類實例化(賦予給定值)
二、類初始化
下面我們直接給出一個例子看一下java是如何初始化的。我們知道一個類中,往往包含靜態變量、靜態代碼塊、變量、普通方法、構造方法等信息。那麼他們是如何初始化的呢?
輸出:
上面這個例子比較簡單,我們再來看看帶有父類和接口的情況。
第一步:定義一個父類
第二步:定義一個子類實現
第三步:看結果
小結,類的初始化順序,這樣看確實不好記,不過沒有繼承關係的我們都能很好的看到。帶繼承關係的,使用網上一張圖來表示:
OK,類的初始化中的知識點基本上就是初始化的順序。
三、創建對象的幾種方式
其實對象的初始化就是在創建對象的時候由jvm完成的。對於創建對象,主要是研究創建對象的幾種方式。下面一一的解答.這裡給出6種方式,面試的時候足夠你zhuangbility。
使用new關鍵字Class對象的newInstance()方法構造函數對象的newInstance()方法對象反序列化Object對象的clone()方法使用Unsafe類創建對象最後再揭曉。。。OK,先認識一個,下面一個一個看。
(1)使用new關鍵字
(2)class的newInstance()方法
首先我們通過Class.forName()動態的加載類的Class對象,
然後通過newInstance()方法獲得Test類的對象
(3)構造函數的newInstance()方法
類Constructor也有newInstance方法,這一點和Class有點像。從它的名字可以看出它與Class的不同,Class是通過類來創建對象,而Constructor則是通過構造器。
(4)序列化
首先我們要對Test實現Serializable接口。然後開始序列化數據。最後得到反序列化的對象。
(5)clone方式
Object對象中存在clone方法,它的作用是創建一個對象的副本。
(6)使用Unsafe類創建對象
Unsafe類使Java擁有了像C語言的指針一樣操作內存空間的能力,同時也帶來了指針的問題。過度的使用Unsafe類會使得出錯的機率變大,因此Java官方並不建議使用的,官方文檔也幾乎沒有。Oracle正在計劃從Java 9中去掉Unsafe類,如果真是如此影響就太大了。
我們無法直接創建Unsafe對象。這裡我們使用反射方法得到
拿到這個對象後,調用其中的native方法allocateInstance 創建一個對象實例
Object event = unsafe.allocateInstance(Test.class);
四、總結
我們想要創建一個對象。基本上就是java虛擬機分配內存的過程。因此我們可以先回顧一下java程序的執行過程。給一張網上的圖,寫的很清晰
一個例子去解釋:(摘自我之前的文章《java8的內存結構》)
然後我們測試一下:
我們分析一下這個過程
第一步,JVM去方法區尋找Test類的代碼信息,如果有直接調用,沒有的話使用類的加載機制把類加載進來。同時把靜態變量、靜態方法、常量加載進來。這裡加載的是(「馮冬冬的IT技術棧」,「馮XX」);這是因為字符串是常量,age中的18是基本類型。
第二步,jvm進入main方法,看到Person person=new Person()。首先分析Person這個類,同樣的尋找Person類的代碼信息,有就加載,沒有的話類加載機制加載進來。同時也加載靜態變量、靜態方法、常量(「我正在走路。。。」)
第三步,jvm接下來看到了person,person在main方法內部,因而是局部變量,存放在棧空間中。
第四步,jvm接下來看到了new Person()。new出的對象(實例),存放在堆空間中。
第五步,jvm接下來看到了「=」,把new Person的地址告訴person變量,person通過四字節的地址(十六進位),引用該實例。 是不是有點暈,別著急,畫個圖看一下。
第六步,jvm看到person.name = 「馮冬冬的IT技術棧」;person通過引用new Person實例的name屬性,該name屬性通過地址指向常量池的"馮冬冬的IT技術棧"。
第七步,jvm看到person.age = 18; person的age屬性是基本數據類型,直接賦值。
第八步,jvm看到person.walk(); 調用實例的方法時,並不會在實例對象中生成一個新的方法,而是通過地址指向方法區中類信息的方法。走到這一步再看看圖怎麼變化的
大多數人基本上都能看懂。創建一個對象的過程基本上就是這。
如有問題還請批評指正。謝謝