java中静态变量,静态代码块,静态方法,实例变量,匿名代码块的加载顺序
1. java中靜態(tài)變量,靜態(tài)代碼塊,靜態(tài)方法,實(shí)例變量,匿名代碼塊
在Java中,使用{}括起來的代碼稱為代碼塊,代碼塊可以分為以下四種:
(1)普通代碼塊:就是類中方法的方法體
public void xxx(){ //code }(2)構(gòu)造塊:用{}裹起來的代碼片段,構(gòu)造塊在創(chuàng)建對(duì)象時(shí)會(huì)被調(diào)用,每次創(chuàng)建對(duì)象時(shí)都會(huì)被調(diào)用,并且優(yōu)先于類構(gòu)造函數(shù)執(zhí)行。 構(gòu)造塊中定義的變量是局部變量。
{ //code }(3)靜態(tài)塊:用static{}裹起來的代碼片段,只會(huì)被執(zhí)行一次(第一次加載此類時(shí)執(zhí)行,比如說用Class.forName("")加載類時(shí)就會(huì)執(zhí)行static block),靜態(tài)塊優(yōu)先于構(gòu)造塊執(zhí)行。
static{ //code }(4)同步代碼塊:使用synchronized(obj){}裹起來的代碼塊,在多線程環(huán)境下,對(duì)共享數(shù)據(jù)進(jìn)行讀寫操作是需要互斥進(jìn)行的,否則會(huì)導(dǎo)致數(shù)據(jù)的不一致性。常見的是synchronized用來修飾方法,其語義是任何線程進(jìn)入synchronized需要先取得對(duì)象鎖如果被占用了,則阻塞,實(shí)現(xiàn)了互斥訪問共享資源。而synchronized也是有代價(jià)的。一個(gè)常見的場(chǎng)景是,一個(gè)冗長(zhǎng)的方法中,其實(shí)只有一小段代碼需要訪問共享資源,這時(shí)使用同步塊,就只將這小段代碼裹在synchronized block,既能夠?qū)崿F(xiàn)同步訪問,也能夠減少同步引入的開銷。 同步代碼塊須寫在方法中。
synchronized(obj){ //code }下面是一個(gè)實(shí)例:
public class Test { //1.第一步,準(zhǔn)備加載類public static void main(String[] args) {new Test(); //4.第四步,new一個(gè)類,但在new之前要處理匿名代碼塊// 這里必須等待類加載完System.out.println("done.."); Test.run();}static int num = 4; //2.第二步,靜態(tài)變量和靜態(tài)代碼塊的加載順序由編寫先后決定 static { System.out.println("num:"+num); // 3.第三步,靜態(tài)塊,然后執(zhí)行靜態(tài)代碼塊,因?yàn)橛休敵?#xff0c;故打印aSystem.out.println("a");}{num += 3;System.out.println("b:"+num); //5.第五步,按照順序加載匿名代碼塊,代碼塊中有打印}int a = 5; //6.第六步,按照順序加載變量{ // 成員變量第三個(gè)System.out.println("c:"+a); //7.第七步,按照順序打印c}Test() { // 類的構(gòu)造函數(shù),第四個(gè)加載System.out.println("d"); //8.第八步,最后加載構(gòu)造函數(shù),完成對(duì)象的建立}static void run() // 靜態(tài)方法,調(diào)用的時(shí)候才加載 {System.out.println("e");}}運(yùn)行:
num:4 a b:7 c:5 d done.. e一般順序:靜態(tài)塊(靜態(tài)變量)——>成員變量——>構(gòu)造方法——>靜態(tài)方法
1、靜態(tài)代碼塊(只加載一次)
2、構(gòu)造方法(創(chuàng)建一個(gè)實(shí)例就加載一次)
3、靜態(tài)方法需要調(diào)用才會(huì)執(zhí)行
繼承類的靜態(tài)變量,靜態(tài)代碼塊,靜態(tài)方法,實(shí)例變量之間的執(zhí)行順序:
例子1:
class Print {public Print(String s){System.out.print(s + " ");} }class Parent{public static Print obj1 = new Print("1");public Print obj2 = new Print("2");public static Print obj3 = new Print("3");static{new Print("4");}public static Print obj4 = new Print("5");public Print obj5 = new Print("6");public Parent(){new Print("7");}}class Child extends Parent{static{//System.out.println(" problem...");new Print("a");}public static Print obj1 = new Print("b");public Print obj2 = new Print("c");public Child (){new Print("d");}public static Print obj3 = new Print("e");public Print obj4 = new Print("f");}public class Test1 {public static void main(String [] args){Parent obj1 = new Child ();Parent obj2 = new Child ();}}運(yùn)行:
1 3 4 5 a b e 2 6 7 c f d 2 6 7 c f d輸出結(jié)果表明,程序的執(zhí)行順序?yàn)?#xff1a;
如果類還沒有被加載:
1、先執(zhí)行父類的靜態(tài)代碼塊和靜態(tài)變量初始化,并且靜態(tài)代碼塊和靜態(tài)變量的執(zhí)行順序只跟代碼中出現(xiàn)的順序有關(guān)。
2、執(zhí)行子類的靜態(tài)代碼塊和靜態(tài)變量初始化。
3、執(zhí)行父類的實(shí)例變量初始化
4、執(zhí)行父類的構(gòu)造函數(shù)
5、執(zhí)行子類的實(shí)例變量初始化
6、執(zhí)行子類的構(gòu)造函數(shù)
如果類已經(jīng)被加載:
則靜態(tài)代碼塊和靜態(tài)變量就不用重復(fù)執(zhí)行,再創(chuàng)建類對(duì)象時(shí),只執(zhí)行與實(shí)例相關(guān)的變量初始化和構(gòu)造方法。
例子2:
class H1{{System.out.println("父類代碼塊");}public H1(){System.out.println("父類構(gòu)造");}static{System.out.println("父類靜態(tài)代碼塊");} }class H2 extends H1{static{System.out.println("子類靜態(tài)代碼塊");}{System.out.println("子類代碼塊");}public H2(){System.out.println("子類構(gòu)造");} }public class Test1 {public static void main(String [] args){new H2();} }運(yùn)行:
父類靜態(tài)代碼塊 子類靜態(tài)代碼塊 父類代碼塊 父類構(gòu)造 子類代碼塊 子類構(gòu)造執(zhí)行流程分析:
1.java程序中靜態(tài)內(nèi)容是隨著類的加載而加載的,由于存在繼承關(guān)系,因此先加載父類而后加載子類,相應(yīng)的就是先執(zhí)行父類靜態(tài)代碼塊,再執(zhí)行子類靜態(tài)代碼塊
2.類加載完成后程序就開始執(zhí)行main方法中,緊接著進(jìn)行初始化工作,由于代碼塊執(zhí)行優(yōu)于構(gòu)造方法,因此出現(xiàn)先執(zhí)行父類代碼塊,再執(zhí)行父類構(gòu)造方法,緊接著子類代碼塊,子類構(gòu)造方法。
3.類的初始化是分層初始化的,先對(duì)父類進(jìn)行初始化,再對(duì)子類進(jìn)行初始化。在目標(biāo)類中執(zhí)行順序?yàn)?1.成員變量初始化:默認(rèn)初始化----》顯示初始化----》構(gòu)造方法初始化
2. 普通內(nèi)部類和靜態(tài)內(nèi)部類總結(jié)
(1)靜態(tài)變量和靜態(tài)方法會(huì)出現(xiàn)這個(gè)語法錯(cuò)誤(static methods can only be declared in a static or top level type)意思就是static方法只能在靜態(tài)或者頂級(jí)類型(頂級(jí)類型應(yīng)該就是外部類中)中聲明,當(dāng)然static變量和static內(nèi)部類也是一樣的道理。原因在靜態(tài)變量和靜態(tài)方法都只需要通過類名就能訪問,不必通過任何實(shí)例化對(duì)象;而普通內(nèi)部類的初始化要利用外部類的實(shí)例化對(duì)象,這明顯違背了static的設(shè)計(jì)初衷。
(2)靜態(tài)代碼塊會(huì)出現(xiàn)這個(gè)語法錯(cuò)誤(Cannot define static initializer in inner type Outer.Inner)意思是不能在內(nèi)部類中定義靜態(tài)的初始化程序。
原因跟以上的差不多,static聲明的成員只能為類所共有,而不能僅屬于一個(gè)實(shí)例化對(duì)象,通俗點(diǎn)來說就是不管有多少層的引用,都只能是類來引用而不能是對(duì)象。
3. 理解向上轉(zhuǎn)型:父類引用指向子類對(duì)象A a = New B()
向上轉(zhuǎn)型是JAVA中的一種調(diào)用方式,是多態(tài)的一種表現(xiàn)。向上轉(zhuǎn)型并非是將B自動(dòng)向上轉(zhuǎn)型為A的對(duì)象,相反它是從另一種角度去理解向上兩字的:它是對(duì)A的對(duì)象的方法的擴(kuò)充,即A的對(duì)象可訪問B從A中繼承來的和B復(fù)寫A的方法,其它的方法都不能訪問,包括A中的私有成員方法。
例子:
class Animal{public void sleep(){System.out.println("Animal sleep");}public void eat() {System.out.println("Animal eat");} }class Dog extends Animal {public void eat() {System.out.println("dog eat meat");//重寫父類方法}//子類定義了自己的新方法public void methods() {System.out.println("dog method");} }public class Demo {public static void main(String[] args) {Animal a = new Dog();a.sleep();a.eat();//a.methods(); /*報(bào)錯(cuò):The method methods() is undefined for the type Animal*/} }運(yùn)行:
Animal sleepdog eat meat可以看出:
向上轉(zhuǎn)型后的a對(duì)象只能訪問從Animal中繼承來的和Dog復(fù)寫Animal的方法,其它的方法都不能訪問,包括Animal中的私有成員方法。但如果要訪問Dog類自己的方法,必須強(qiáng)制向下轉(zhuǎn)型 Dog c = (Dog)a;。
另一個(gè)例子:
class Fu {public int num = 100;public void show() {System.out.println("show Fu");}public static void function() {System.out.println("function Fu");} }class Zi extends Fu {public int num = 1000;public int num2 = 200;public void show() {System.out.println("show Zi");}public void method() {System.out.println("method zi");}public static void function() {System.out.println("function Zi");} }public class DuoTaiDemo {public static void main(String[] args) {// 要有父類引用指向子類對(duì)象。// 父 f = new 子();Fu f = new Zi();System.out.println(f.num);// 找不到符號(hào)// System.out.println(f.num2);f.show();// 找不到符號(hào)// f.method();f.function();} }運(yùn)行:
100show Zifunction Fu我們可以看到多態(tài)中的成員訪問特點(diǎn):
- 成員變量
編譯看左邊,運(yùn)行看左邊 - 構(gòu)造方法
子類的構(gòu)造都會(huì)默認(rèn)訪問父類構(gòu)造 - 成員方法
編譯看左邊,運(yùn)行看右邊 - 靜態(tài)方法
編譯看左邊,運(yùn)行看左邊
所以靜態(tài)方法不能算方法的重寫
參考:
https://blog.csdn.net/mrzhoug/article/details/51581994
https://blog.csdn.net/gh2391292/article/details/74421308
總結(jié)
以上是生活随笔為你收集整理的java中静态变量,静态代码块,静态方法,实例变量,匿名代码块的加载顺序的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 股票基金的点数是怎么看的
- 下一篇: 国际直接投资的动机是什么