實例化——執行成員變量的賦值語句和構造函數。包括成員變量的賦值,以及{}代碼塊。自上而下執行。
構造函數最后執行
使用——每個線程一個棧,調用方法,就往里面壓一個Stack Frame。方法執行結束后則彈出。實例化對
象后,對象是在堆中,棧中有個引用指向堆中的對象,堆中的對象有個地址指向方法區中的
類的信息
卸載——略
二。直接引用
以下是視為主動使用一個類,其他情況均視為被動使用!
1) 最為常用的new一個類的實例對象(聲明不叫主動使用)
2) 直接調用類的靜態方法。
3) 對類的靜態變量進行讀取、賦值操作的
4) 反射調用一個類的方法。
5) 初始化一個類的子類的時候,父類也相當於被程序主動調用了(如果調用子類的靜態變量是從父類
繼承過來並沒有復寫的,那么也就相當於只用到了父類的東東,和子類無關,所以這個時候子類
不需要進行類初始化)。
6) 直接運行一個main函數入口的類。
三。類初始化與對象實例化
(1)類初始化流程:
<1>父類靜態變量賦值、靜態代碼塊執行(形式如static {})
<2>子類靜態變量賦值、靜態代碼塊執行(形式如static {})
對象實例化流程:
<1>父類成員變量賦值、成員代碼塊執行(形式如{})
<2>父類構造函數
<3>子類成員變量賦值、成員代碼塊執行(形式如{})
<4>子類構造函數
(2)類初始化只需執行一遍,對象實例化可執行多遍
(3)實例化時需類初始化(沒初始化過),但是並不一定是初始化做完再實例化。初始化進行到一半,
遇到實例化,會先執行實例化的部分,再繼續執行初始化的部分。初始化可以被實例化即時打斷,
但是實例化時建議就不要再實例化自己以及父類相關對象,容易死循環
(4)main方法會在所在類初始化后執行
四。簡單例題
梳理至此,應該是把整個加載過程理順了。我自己網上找了些類似的搞來搞去關於加載執行順利的題,
都能准確應付了。下面來試試手,也歡迎各位找出各種變態奇葩各種中二的題目或想法來討論。
Class A extends B{
A(){
System.out.println("A-new");
}
{
System.out.println("A-1");
}
static{
System.out.println("A-static-1");
new A();
}
public static void main(String[] args){
System.out.println("A-main");
}
static{
System.out.println("A-static-2");
}
}
Class B{
B(){
System.out.println("B-new");
}
static{
System.out.println("B-static-1");
}
{
System.out.println("B-1");
}
}
執行結果是:
B-static-1——main函數在A中,A繼承自B,故先走父類B的初始化流程,B靜態代碼塊執行
A-static-1——走完父類B的初始化流程后,再走子類A的初始化流程,A靜態代碼塊執行
B-1——其實子類A的初始化流程還沒走完,但是遇到了new A(),就直接開始走A的實例化流程,
先走父類B實例化流程,B的成員代碼塊執行
B-new——繼續B的實例化流程,走B的構造函數
A-1——父類B的實例化流程結束,走子類A的實例化流程,A的成員代碼塊執行
A-new——繼續子類A的實例化流程,走A的構造函數
A-static-2——A實例化結束后,繼續之前被打斷的類A的初始化流程
A-main——main所在類A的初始化結束后,走main方法