首先做個(gè)測(cè)試:
public class Test1 {
public static String name=get();
public Test1() {
}
public static String get() {
System.out.println("Test start");
return "test name";
}
public static void main(String[] args) {
System.out.println("main start");
Test1 bb = new Test1();
}
}
答案是啥鉴扫?
main start
test start
那你就out啦
正確答案是:
test start
main start
什么身害?不是從main方法執(zhí)行的嗎味悄?怎么回事呢?
Java程序運(yùn)行時(shí)塌鸯,第一件事情就是試圖訪問main方法侍瑟,因?yàn)閙ain相等于程序的入口,如果沒有main方法丙猬,程序?qū)o法啟動(dòng)涨颜,main方法更是占一個(gè)獨(dú)立的線程,找到main方法后淮悼,是不是就會(huì)執(zhí)行mian方法塊里的第一句話呢咐低?答案是不一定
分析:
因?yàn)殪o態(tài)部分是依賴于類揽思,而不是依賴于對(duì)象存在的袜腥,所以靜態(tài)部分的加載優(yōu)先于對(duì)象存在。
當(dāng)找到main方法后钉汗,因?yàn)閙ain方法雖然是一個(gè)特殊的靜態(tài)方法羹令,但是還是靜態(tài)方法,此時(shí)JVM會(huì)加載main方法所在的類损痰,試圖找到類中其他靜態(tài)部分福侈,即首先會(huì)找main方法所在的類,然后會(huì)按照順序執(zhí)行類中的靜態(tài)代碼卢未,包括靜態(tài)變量肪凛,靜態(tài)方法和靜態(tài)代碼塊,靜態(tài)方法在不會(huì)主動(dòng)調(diào)用辽社,但會(huì)加載伟墙。靜態(tài)方法執(zhí)行完之后,才是動(dòng)態(tài)代碼的執(zhí)行滴铅,包括動(dòng)態(tài)屬性賦值和代碼塊
執(zhí)行順序大致分類:
1.靜態(tài)屬性戳葵,靜態(tài)方法聲明,靜態(tài)塊汉匙。
2.動(dòng)態(tài)屬性拱烁,普通方法聲明,構(gòu)造塊噩翠。
3.構(gòu)造方法戏自。
1.1 靜態(tài):
當(dāng)加載一個(gè)類時(shí),JVM會(huì)根據(jù)屬性的數(shù)據(jù)類型第一時(shí)間賦默認(rèn)值(一舉生成的)伤锚。然后再進(jìn)行靜態(tài)屬性初始化擅笔,并為靜態(tài)屬性分配內(nèi)存空間,靜態(tài)方法的聲明,靜態(tài)塊的加載剂娄,沒有優(yōu)先級(jí)之分蠢涝,按出現(xiàn)順序執(zhí)行,靜態(tài)部分僅僅加載一次阅懦。至此為止和二,必要的類都已經(jīng)加載完畢,對(duì)象就可以被創(chuàng)建了耳胎。
1.2 普通:
當(dāng)new一個(gè)對(duì)象時(shí)惯吕,此時(shí)會(huì)調(diào)用構(gòu)造方法,但是在調(diào)用構(gòu)造方法之前怕午,(此刻1.1已經(jīng)完成废登,除非被打斷而暫停)執(zhí)行動(dòng)態(tài)屬性定義并設(shè)置默認(rèn)值(一舉生成的)。然后動(dòng)態(tài)屬性初始化郁惜,分配內(nèi)存堡距,構(gòu)造塊,普通方法聲明(只是加載兆蕉,它不需要初始化羽戒,只有調(diào)用它時(shí)才分配內(nèi)存,當(dāng)方法執(zhí)行完畢后內(nèi)存立即釋放)虎韵,沒有優(yōu)先級(jí)之分易稠,按出現(xiàn)順序執(zhí)行。最后進(jìn)行構(gòu)造方法中賦值包蓝。當(dāng)再次創(chuàng)建一個(gè)對(duì)象驶社,不再執(zhí)行靜態(tài)部分,僅僅重復(fù)執(zhí)行普通部分测萎。
現(xiàn)在是否有些理解了呢亡电?
我們?cè)賮矸治鱿聞傞_始的測(cè)試題:
1、首先jvm找到main入口绳泉,加載整個(gè)類
2逊抡、找到整個(gè)類所有的靜態(tài)部分,包括 靜態(tài)變量和靜態(tài)代碼塊零酪,靜態(tài)方法 冒嫡,這里是先找到name,發(fā)現(xiàn)它調(diào)用了get()方法四苇,就先執(zhí)行g(shù)et方法孝凌,打印 test start ,返回值給name,執(zhí)行完成之后,發(fā)現(xiàn)沒有其他的靜態(tài)變量或代碼塊啦月腋,就執(zhí)行main方法蟀架,打印main start
再來道測(cè)試題:
class A {
public A() {
System.out.println("A的構(gòu)造方法");
}
public static int j = print();
public static int print() {
System.out.println("A print");
return 521;
}
}
public class Test1 extends A {
public Test1() {
System.out.println("Test1的構(gòu)造方法");
}
public static int k = print();
public static int print() {
System.out.println("Test print");
return 522;
}
public static void main(String[] args) {
System.out.println("main start");
Test1 t1 = new Test1();
}
}
運(yùn)行結(jié)果:
A print
Test print
main start
A的構(gòu)造方法
Test1的構(gòu)造方法
如果存在繼承關(guān)系瓣赂,會(huì)先執(zhí)行父類的靜態(tài)部分,再執(zhí)行子類的靜態(tài)部分片拍,再執(zhí)行父類的動(dòng)態(tài)部分煌集,再執(zhí)行子類的動(dòng)態(tài)部分
創(chuàng)建對(duì)象時(shí),依然會(huì)首先進(jìn)行動(dòng)態(tài)屬性進(jìn)行定義并設(shè)默認(rèn)值捌省,然后父類的構(gòu)造器才會(huì)被調(diào)用苫纤,其他一切都是先父類再子類(因?yàn)樽宇惖膕tatic初始化可能會(huì)依賴于父類成員能否被正確初始化),如果父類還有父類纲缓,依次類推卷拘,不管你是否打算產(chǎn)生一個(gè)該父類的對(duì)象,這都是自然發(fā)生的祝高。
最后再來一道終極測(cè)試題栗弟,這是阿里曾經(jīng)的測(cè)試題
public class Text {
public static int k = 0;
public static Text t1 = new Text("t1");
public static Text t2 = new Text("t2");
public static int i = print("i");
public static int n = 99;
public int j = print("j");
{
print("構(gòu)造塊");
}
static {
print("靜態(tài)塊");
}
public Text(String str) {
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++i;
++n;
}
public static int print(String str) {
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++n;
return ++i;
}
public static void main(String args[]) {
Text t = new Text("init");
}
}
有沒有生無可戀?工闺?乍赫??
運(yùn)行結(jié)果:
1:j i=0 n=0
2:構(gòu)造塊 i=1 n=1
3:t1 i=2 n=2
4:j i=3 n=3
5:構(gòu)造塊 i=4 n=4
6:t2 i=5 n=5
7:i i=6 n=6
8:靜態(tài)塊 i=7 n=99
9:j i=8 n=100
10:構(gòu)造塊 i=9 n=101
11:init i=10 n=102
總結(jié):只要按照這個(gè)步驟斤寂,遇到這一類問題就可以解決了耿焊。
1-3:類加載過程揪惦,不涉及構(gòu)造方法
1-5: 實(shí)例化過程遍搞,涉及構(gòu)造方法
1.類中所有屬性的默認(rèn)值(一舉而成)
2. 父類靜態(tài)屬性初始化,靜態(tài)塊器腋,靜態(tài)方法的聲明(按出現(xiàn)順序執(zhí)行)
3. 子類靜態(tài)屬性初始化溪猿,靜態(tài)塊,靜態(tài)方法的聲明 (按出現(xiàn)順序執(zhí)行)
4. 調(diào)用父類的構(gòu)造方法纫塌,
首先父類的非靜態(tài)成員初始化诊县,構(gòu)造塊,普通方法的聲明(按出現(xiàn)順序執(zhí)行)
然后父類構(gòu)造方法
5. 調(diào)用子類的構(gòu)造方法措左,
首先子類的非靜態(tài)成員初始化依痊,構(gòu)造塊,普通方法的聲明(按出現(xiàn)順序執(zhí)行)
然后子類構(gòu)造方法
注意:
類加載過程中怎披,可能調(diào)用了實(shí)例化過程(因?yàn)閟tatic可以修飾方法胸嘁,屬性,代碼塊凉逛,內(nèi)部類)性宏,此時(shí)則會(huì)暫停類加載過程而先執(zhí)行實(shí)例化過程(被打斷),執(zhí)行結(jié)束再進(jìn)行類加載過程状飞,上面阿里那道面試題就是典型的暫停類加載毫胜。
簡(jiǎn)要分析:
1书斜、首先執(zhí)行靜態(tài)部分,先賦值k=0
2酵使、賦值 t1,發(fā)現(xiàn)t1涉及到實(shí)例化過程荐吉,先斷開類加載,執(zhí)行實(shí)例化過程
3口渔、實(shí)例化過程中稍坯,執(zhí)行類的動(dòng)態(tài)部分,就是j的賦值搓劫,它執(zhí)行了print方法瞧哟,所以先打印j ,i和n都會(huì)先初始化為0
4、j賦值完成之后枪向,繼續(xù)執(zhí)行動(dòng)態(tài)部分勤揩,就是類的代碼塊,打印 print("構(gòu)造塊")
5秘蛔、執(zhí)行完成后陨亡,沒動(dòng)態(tài)塊之后再執(zhí)行構(gòu)造函數(shù),至此整個(gè)實(shí)例化過程執(zhí)行完畢,t1賦值完成
6深员、t2按照t1執(zhí)行
7负蠕、然后賦值 i,n
8、然后再執(zhí)行靜態(tài)塊倦畅,執(zhí)行 print("靜態(tài)塊")遮糖,至此整個(gè)類加載過程完成
9、執(zhí)行main函數(shù)叠赐,賦值t,t和t1欲账、t2的執(zhí)行是一樣的