關於類的初始化以及類的例項化一些思考

正號先生發表於2019-07-11

前言

上一篇我們知道了一個類的生命週期是:載入->驗證->準備->解析->初始化->使用->解除安裝。
當初始化完成以後,一個類所有的類變數(被static修飾的變數)都被賦值。但是未被static修飾的成員變數又是何時被賦值的呢?

一個類何時會被初始化

一個類何時被初始化可以分為以下幾類:

1.建立類的例項(new)。
 
2.訪問某個類或介面的靜態變數,或者對該靜態變數賦值。

3.呼叫類的靜態方法。

4.通過反射方式執行以上三種行為。

5.初始化子類的時候,會觸發父類的初始化。

6.Java虛擬機器啟動時被標明為啟動類的類。(有main方法的類)

7.JDK 1.7開始提供的動態語言支援。(瞭解即可)

我們來說道說道第3點和第6點

我們平常在使用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出來的。那麼在初始化完成之後會進入例項化階段。

例項化的具體步驟為:

父類非靜態成員初始化語句(包括程式碼塊,按照在類定義中的順序執行)->父類建構函式->子類非靜態成員初始化語句(包括程式碼塊,按照在類定義中的順序執行)->子類構造方法()

注意哦!
如果這個類有父類不光是先例項化父類。整體流程如下:
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一個物件出來準確的來說是進行了初始化和例項化兩個步驟,這樣應該就會清晰了很多。

初始化完成以後,類被存放在方法區,注意哦,此時並沒有存放在堆記憶體中。

只有當物件例項化進入堆記憶體中以後才會對非靜態變數進行初始化賦值。

總結

這下總算吧一個類的初始化和例項化的細節搞明白了,關於文中的方法區,堆記憶體等內容,下一節再具體分析。

相關文章