上一篇咱們知道了一個類的生命週期是:加載->驗證->準備->解析->初始化->使用->卸載。
當初始化完成之後,一個類全部的類變量(被static修飾的變量)都被賦值。可是未被static修飾的成員變量又是什麼時候被賦值的呢?函數
一個類什麼時候被初始化能夠分爲如下幾類:學習
1.建立類的實例(new)。 2.訪問某個類或接口的靜態變量,或者對該靜態變量賦值。 3.調用類的靜態方法。 4.經過反射方式執行以上三種行爲。 5.初始化子類的時候,會觸發父類的初始化。 6.Java虛擬機啓動時被標明爲啓動類的類。(有main方法的類) 7.JDK 1.7開始提供的動態語言支持。(瞭解便可)
咱們來講道說道第3點和第6點code
咱們日常在使用main方法和調用某個類的靜態方法的時候,是否是發現,並不能直接調用靜態方法和main方法所在類的非靜態方法和非靜態變量。對象
但是明明不是說了在調用靜態方法和執行main方法的時候,所在的類已經被初始化了嗎?接口
是的!在上一章節咱們就說,類初始化的時候會按照咱們編寫代碼的順序爲類變量(static修飾的變量)進行賦值。注意哦,此時這個類僅僅只有靜態變量被正確賦值了哦。生命週期
public class People{ private static String name ="lisi"; private int age = 18; static{ name ="zhangsan"; } }
如上述代碼,在該類被初始化以後,首先按照代碼順序將類變量(被static修飾的變量)賦值。因此name被賦值"lishi",而後再將name賦值爲 "zhangsan"。此時age並無值,直接直接調用會報錯。內存
上一個章節中,咱們明白了加載->驗證->準備->解析->初始化的具體細節。虛擬機
當初始化完成後,一個類的靜態變量被正確賦值。若是這個對象是被new出來的。那麼在初始化完成以後會進入實例化階段。class
實例化的具體步驟爲:變量
父類非靜態成員初始化語句(包括代碼塊,按照在類定義中的順序執行)->父類構造函數->子類非靜態成員初始化語句(包括代碼塊,按照在類定義中的順序執行)->子類構造方法()
注意哦! 若是這個類有父類不光是先實例化父類。總體流程以下: 1. 加載父類 1.1 爲靜態屬性分配存儲空間並賦初始值 1.2 執行靜態初始化塊和靜態初始化語句(從上至下) 2. 加載子類 2.1 爲靜態屬性分配存儲空間 2.2 執行靜態初始化塊和靜態初始化語句(從上至下) 3. 加載父類構造器 3.1 爲實例屬性分配存數空間並賦初始值 3.2 執行實例初始化塊和實例初始化語句 3.3 執行構造器內容 4. 加載子類構造器 4.1 爲實例屬性分配存數空間並賦初始值 4.2 執行實例初始化塊和實例初始化語句 4.3 執行構造器內容
到這裏,咱們就很是的清楚爲何在靜態方法中不能直接調用非靜態的變量,由於此時的非靜態變量並無被賦值。
之前我在學習的時候,new一個對象出來你們老是在說,咱們初始化了這個對象,而後balabala。。。
致使我對於初始化和實例化裏的靜態變量和非靜態變量什麼時候被賦值一直有點模糊。new一個對象出來準確的來講是進行了初始化和實例化兩個步驟,這樣應該就會清晰了不少。
初始化完成之後,類被存放在方法區,注意哦,此時並無存放在堆內存中。
只有當對象實例化進入堆內存中之後纔會對非靜態變量進行初始化賦值。
這下總算吧一個類的初始化和實例化的細節搞明白了,關於文中的方法區,堆內存等內容,下一節再具體分析。