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