一文搞懂java中類(lèi)及static關(guān)鍵字執(zhí)行順序
類(lèi)的生命周期

- 加載:classpath、jar包、網(wǎng)絡(luò)、某個(gè)磁盤(pán)位置下的類(lèi)的class二進(jìn)制字節(jié)流讀進(jìn)來(lái),在內(nèi)存中生成一個(gè)代表這個(gè)類(lèi)的java.lang.Class對(duì)象放入元空間(方法區(qū),永久代),此階段我們程序員可以干預(yù),我們可以自定義類(lèi)加載器來(lái)實(shí)現(xiàn)類(lèi)的加載;
- 驗(yàn)證:驗(yàn)證Class文件的字節(jié)流中包含的信息符合《Java虛擬機(jī)規(guī)范》的全部約束要求,保證虛擬機(jī)的安全;
- 準(zhǔn)備:類(lèi)變量賦默認(rèn)初始值,int為0,long為0L,boolean為false,引用類(lèi)型為null;常量賦正式值;
- 解析:把符號(hào)引用翻譯為直接引用;
- 初始化:當(dāng)我們new一個(gè)類(lèi)的對(duì)象,訪問(wèn)一個(gè)類(lèi)的靜態(tài)屬性,修改一個(gè)類(lèi)的靜態(tài)屬性,調(diào)用一個(gè)類(lèi)的靜態(tài)方法,用反射API對(duì)一個(gè)類(lèi)進(jìn)行調(diào)用,初始化當(dāng)前類(lèi),其父類(lèi)也會(huì)被初始化… 那么這些都會(huì)觸發(fā)類(lèi)的初始化;
靜態(tài)變量的初始化有兩種途徑: (1)在靜態(tài)變量的聲明處進(jìn)行初始化 (2)在靜態(tài)代碼塊中進(jìn)行初始化
使用:使用這個(gè)類(lèi);卸載:
- 1.該類(lèi)所有的實(shí)例都已經(jīng)被GC,也就是JVM中不存在該Class的任何實(shí)例;
- 2.加載該類(lèi)的ClassLoader已經(jīng)被GC;
- 3.該類(lèi)的java.lang.Class 對(duì)象沒(méi)有在任何地方被引用,如不能在任何地方通過(guò)反射訪問(wèn)該類(lèi)的方法;
接著我們來(lái)了解static關(guān)鍵字的作用和使用條件
static關(guān)鍵字
static關(guān)鍵字修飾的數(shù)據(jù)存儲(chǔ)在我們的方法區(qū)中的靜態(tài)常量池中,static可以修飾方法、變量和代碼塊
static修飾方法:指定不需要實(shí)例化就可以激活的一個(gè)方法。this關(guān)鍵字不能再static方法中使用靜態(tài)方法中不能使用非靜態(tài)方法非靜態(tài)方法可以調(diào)用靜態(tài)方法。
static修飾變量:指定變量被所有對(duì)象共享,即所有實(shí)例都可以使用該變量。變量屬于這個(gè)類(lèi)。
static修飾代碼塊:無(wú)論放在哪里,都是放在main方法運(yùn)行的。通常用于初始化靜態(tài)變量,靜態(tài)代碼塊屬于類(lèi)。不可以省略,沒(méi)加static認(rèn)為是構(gòu)造代碼塊
static關(guān)鍵字執(zhí)行順序
先記住幾個(gè)準(zhǔn)則;
- 實(shí)例化對(duì)象前,先加載類(lèi)(對(duì)象載入之前,一定要是類(lèi)先被載入)
- 類(lèi)(或者靜態(tài)變量和靜態(tài)代碼塊)在生命周期結(jié)束前,只執(zhí)行一次
- 靜態(tài)變量(屬性)和靜態(tài)代碼塊誰(shuí)先聲明誰(shuí)先執(zhí)行非
- 靜態(tài)變量(屬性)和非靜態(tài)代碼塊誰(shuí)先聲明誰(shuí)先執(zhí)行靜態(tài)
- 構(gòu)造代碼塊是和類(lèi)同時(shí)加載的,構(gòu)造代碼塊是在實(shí)例化之后執(zhí)行構(gòu)造方法之前執(zhí)行的構(gòu)造方法是在構(gòu)造代碼塊執(zhí)行完之后才執(zhí)行的。
- 靜態(tài)方法屬于類(lèi)的,加載完類(lèi)就可以調(diào)用靜態(tài)方法(可以執(zhí)行多次);非靜態(tài)方法是屬于對(duì)象的,加載完對(duì)象就可以調(diào)用非靜態(tài)方法。
- 每創(chuàng)建一個(gè)對(duì)象,即每載入一個(gè)對(duì)象,非靜態(tài)代碼塊都執(zhí)行一次。執(zhí)行類(lèi)對(duì)象的載入之前就會(huì)調(diào)用
我們來(lái)通過(guò)一個(gè)例子來(lái)驗(yàn)證以下上面的觀點(diǎn)
InitializeDemo.java
package com.qcby.demo.staticDemo;
/**
* @ClassName TRRest
* @Description TODO
* @Author heaboy@heaboy.com
* @Version 1.0.0
*/
public class InitializeDemo {
private static int k = 1;
private static InitializeDemo t1 = new InitializeDemo("t1");
private static InitializeDemo t2 = new InitializeDemo("t2");
private static int i = print("i");
private static int n = 99;
{
print("初始化塊");
j=100;
}
public InitializeDemo(String str) {
System.out.println((k++) + ":" + str + " i=" + i + " n=" + n);
++i;
++n;
}
static {
print("靜態(tài)塊");
n=100;
}
private int j = print("j");
public static int print(String str) {
System.out.println((k++) + ":" + str + " i=" + i + " n=" + n);
++n;
return ++i;
}
public static void main(String[] args) {
InitializeDemo test = new InitializeDemo("test");
}
}輸出結(jié)果:

我們來(lái)逐個(gè)分析,
一開(kāi)始調(diào)用main方法,main方法內(nèi)實(shí)例化InitializeDemo的對(duì)象,在對(duì)象載入之前,一定要是類(lèi)先被載入
所以我們先加載InitializeDemo類(lèi),加載類(lèi)的同時(shí),會(huì)加載靜態(tài)變量和靜態(tài)代碼塊,但是是按順序執(zhí)行,且只執(zhí)行一次
先加載如下靜態(tài)變量
private static int k = 1;
加載如下靜態(tài)變量的時(shí)候,發(fā)現(xiàn)要去加載類(lèi),由于類(lèi)以及加載了,所以會(huì)加載這個(gè)對(duì)象,這個(gè)對(duì)象加載前,會(huì)執(zhí)行非靜態(tài)代碼塊
private static InitializeDemo t1 = new InitializeDemo("t1");此時(shí)也就是
1:初始化塊 i=0 n=0
接著執(zhí)行
private int j = print("j");2:j i=1 n=1
接著執(zhí)行構(gòu)造方法,
public InitializeDemo(String str) {
System.out.println((k++) + ":" + str + " i=" + i + " n=" + n);
++i;
++n;
}3:t1 i=2 n=2
t1的實(shí)例化執(zhí)行結(jié)束,接著執(zhí)行t2的實(shí)例化
private static InitializeDemo t2 = new InitializeDemo("t2");結(jié)果和上述一致,按非靜態(tài)代碼塊和非靜態(tài)屬性然后構(gòu)造方法方法的順序執(zhí)行
4:初始化塊 i=3 n=3
5:j i=4 n=4
6:t2 i=5 n=5
兩個(gè)靜態(tài)屬性(實(shí)例化)執(zhí)行完,執(zhí)行如下代碼
private static int i = print("i");7:i i=6 n=6
接著執(zhí)行下面的代碼,此時(shí)n變成了99
private static int n = 99;
接著執(zhí)行靜態(tài)代碼塊
static {
print("靜態(tài)塊");
n=100;
}8:靜態(tài)塊 i=7 n=99
類(lèi)加載完畢,執(zhí)行test對(duì)象的載入,參考t1,t2的實(shí)例化,按非靜態(tài)代碼塊和非靜態(tài)屬性然后構(gòu)造方法方法的順序執(zhí)行
9:初始化塊 i=8 n=100
10:j i=9 n=101
11:test i=10 n=102
繼承中的static執(zhí)行順序
例子一:
package com.qcby.demo.oop;
public class Test3 extends Base {
static {
System.out.println("test static");
}
public Test3(){
System.out.println("test constructor");
}
public static void main(String[] args) {
new Test3();
}
}
class Base{
static {
System.out.println("Base static");
}
public Base(){
System.out.println("Base constructor");
}
}
執(zhí)行Test3的構(gòu)造方法,要先加載Test3的類(lèi)加載,由于Test3繼承于Base,所以他要先加載父類(lèi)Base,
所以先Base static 后test static 在執(zhí)行子類(lèi)的構(gòu)造方法的時(shí)候,要先執(zhí)行父類(lèi)的構(gòu)造方法,如果是多級(jí)繼承,會(huì)先執(zhí)行最頂級(jí)父類(lèi)的構(gòu)造方法,然后依次執(zhí)行各級(jí)個(gè)子類(lèi)的構(gòu)造方法。 所以先執(zhí)行Base constructor后執(zhí)行test constructor結(jié)果就如上圖
例子二(重點(diǎn))
package com.qcby.demo.oop;
public class MyTest {
MyPerson person = new MyPerson("test");//這里可以理解為成員變量輔助,,要先把MyPerson先加載到j(luò)vm中
static {
System.out.println("test static");//1
}
public MyTest() {
System.out.println("test constructor");//5
}
public static void main(String[] args) {//main方法在MyTest類(lèi)中,使用mian方法先加載MyTest的靜態(tài)方法,不調(diào)用其他,
MyClass myClass =new MyClass();//對(duì)象創(chuàng)建的時(shí)候,會(huì)加載對(duì)應(yīng)的成員變量
}
}
class MyPerson {
static {
System.out.println("person static");//3
}
public MyPerson(String str) {
System.out.println("person " + str);//4 6
}
}
class MyClass extends MyTest {
MyPerson person = new MyPerson("class");//這里可以理解為成員變量輔助,要先把MyPerson先加載到j(luò)vm中
static {
System.out.println("class static");//2
}
public MyClass() {
//默認(rèn)super()
System.out.println("class constructor");//7
}
}
1.先看main方法,main方法回先加載對(duì)應(yīng)的類(lèi),此時(shí)MyTest類(lèi)和其靜態(tài)的變量,方法和代碼塊會(huì)隨類(lèi)的加載而開(kāi)辟空間。static是屬于類(lèi)的。所以test static優(yōu)先執(zhí)行,且此時(shí)MyTest類(lèi)的其他語(yǔ)句不執(zhí)行。
2.mian方法中調(diào)用了MyClass myClass =new MyClass(),實(shí)例化了一個(gè)MyClass類(lèi)的對(duì)象,這時(shí)候會(huì)初始化對(duì)象的成員變量和調(diào)用對(duì)象的構(gòu)造函數(shù),而MyClass類(lèi)繼承于MyTest類(lèi),在加載MyClass類(lèi)前,會(huì)先調(diào)用MyTest類(lèi),但是MyTest類(lèi)以及其靜態(tài)的變量,方法和代碼塊已經(jīng)加載(在類(lèi)的生命周期只執(zhí)行一次),所以返回到子類(lèi)(MyClass類(lèi))的加載,這時(shí)候會(huì)調(diào)用MyClass類(lèi)的靜態(tài)的變量,方法和代碼塊。所以class static第二個(gè)執(zhí)行。
3.MyClass類(lèi)加載完后,應(yīng)該接著調(diào)用MyClass類(lèi)的構(gòu)造方法,在調(diào)用子類(lèi)的構(gòu)造方法前,會(huì)默認(rèn)調(diào)用父類(lèi)的無(wú)參構(gòu)造方法(super()省略),調(diào)用父類(lèi)的無(wú)參構(gòu)造方法,相當(dāng)于實(shí)例化父類(lèi)的對(duì)象,這時(shí)候會(huì)先初始化對(duì)象的成員變量,這里的MyPerson person = new MyPerson(“test”);就相當(dāng)于成員變量(屬于對(duì)象,在對(duì)象的實(shí)例化的時(shí)候才加載),于是會(huì)加載MyPerson類(lèi)和其靜態(tài)的變量,方法和代碼塊。所以person static第三個(gè)執(zhí)行
4.加載完MyPerson類(lèi)和其靜態(tài)的變量,方法和代碼塊后,會(huì)調(diào)用MyPerson類(lèi)和的有參構(gòu)造方法,即person test第四個(gè)執(zhí)行
5.MyPerson類(lèi)和的有參構(gòu)造方法執(zhí)行結(jié)束,返回父類(lèi)MyTest,父類(lèi)調(diào)用構(gòu)造方法,即test constructor第五個(gè)執(zhí)行
6.父類(lèi)MyTest構(gòu)造方法執(zhí)行結(jié)束,返回子類(lèi),子類(lèi)再調(diào)用構(gòu)造方法前,先初始化對(duì)象的成員變量MyPerson person = new MyPerson(“class”);,這時(shí)候會(huì)先先加載MyPerson 和其靜態(tài)的變量,方法和代碼塊。由于上述類(lèi)以及加載,所以直接執(zhí)行其有參構(gòu)造方法,即person class第六個(gè)執(zhí)行
7.MyPerson類(lèi)和的有參構(gòu)造方法執(zhí)行結(jié)束,返回子類(lèi)MyClass,子類(lèi)調(diào)用構(gòu)造方法,即class constructor第七個(gè)執(zhí)行
父類(lèi)static代碼塊–>子類(lèi)static代碼塊–>父類(lèi)普通代碼塊(成員對(duì)象屬性初始化)–>父類(lèi)構(gòu)造方法–>子類(lèi)普通代碼塊(成員對(duì)象屬性初始化–>子類(lèi)構(gòu)造方法
總結(jié)
類(lèi)加載順序的三個(gè)原則是
- 1、父類(lèi)優(yōu)先于子類(lèi)
- 2、屬性和代碼塊(看先后順序)優(yōu)先于構(gòu)造方法
- 3、靜態(tài)優(yōu)先于非靜態(tài)
類(lèi)加載順序?yàn)椋J(rèn)變量卸載代碼塊前)
父類(lèi)靜態(tài)變量->父類(lèi)靜態(tài)語(yǔ)句塊->子類(lèi)靜態(tài)變量->子類(lèi)靜態(tài)語(yǔ)句塊->父類(lèi)普通成員變量->父類(lèi)動(dòng)態(tài)語(yǔ)句塊->父類(lèi)構(gòu)造器->子類(lèi)普通成員變量->子類(lèi)動(dòng)態(tài)語(yǔ)句塊->子類(lèi)構(gòu)造器
到此這篇關(guān)于一文搞懂java中類(lèi)及static關(guān)鍵字執(zhí)行順序的文章就介紹到這了,更多相關(guān)java static關(guān)鍵字執(zhí)行順序內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java Quartz定時(shí)器任務(wù)與Spring task定時(shí)的幾種實(shí)現(xiàn)方法
本篇文章主要介紹了java Quartz定時(shí)器任務(wù)與Spring task定時(shí)的幾種實(shí)現(xiàn)方法的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-02-02
Java語(yǔ)言描述存儲(chǔ)結(jié)構(gòu)與鄰接矩陣代碼示例
這篇文章主要介紹了Java語(yǔ)言描述存儲(chǔ)結(jié)構(gòu)與鄰接矩陣代碼示例,涉及Java存儲(chǔ)結(jié)構(gòu),鄰接矩陣,鄰接表的介紹與比較,然后分享了鄰接矩陣的Java實(shí)現(xiàn)等相關(guān)內(nèi)容,具有一定借鑒價(jià)值,需要的朋友可以參考。2017-11-11
SpringBean和Controller實(shí)現(xiàn)動(dòng)態(tài)注冊(cè)與注銷(xiāo)過(guò)程詳細(xì)講解
這篇文章主要介紹了SpringBean和Controller實(shí)現(xiàn)動(dòng)態(tài)注冊(cè)與注銷(xiāo)過(guò)程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2023-02-02
spring容器啟動(dòng)實(shí)現(xiàn)初始化某個(gè)方法(init)
這篇文章主要介紹了spring容器啟動(dòng)實(shí)現(xiàn)初始化某個(gè)方法(init),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
JAVA微信掃碼支付模式二線上支付功能實(shí)現(xiàn)以及回調(diào)
本篇文章主要介紹了JAVA微信掃碼支付模式二線上支付功能實(shí)現(xiàn)以及回調(diào),這里整理了詳細(xì)的代碼,有需要的小伙伴可以參考下。2016-11-11
SpringBoot集成P6Spy實(shí)現(xiàn)SQL日志的記錄詳解
P6Spy是一個(gè)框架,它可以無(wú)縫地?cái)r截和記錄數(shù)據(jù)庫(kù)活動(dòng),而無(wú)需更改現(xiàn)有應(yīng)用程序的代碼。一般我們使用的比較多的是使用p6spy打印我們最后執(zhí)行的sql語(yǔ)句2022-11-11
java使用this調(diào)用構(gòu)造函數(shù)的實(shí)現(xiàn)方法示例
這篇文章主要介紹了java使用this調(diào)用構(gòu)造函數(shù)的實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了java面向?qū)ο蟪绦蛟O(shè)計(jì)中函數(shù)調(diào)用相關(guān)操作技巧,需要的朋友可以參考下2019-08-08

