jvm系列
垃圾回收基礎
JVM的編譯策略
GC的三大基礎算法
GC的三大高級算法
GC策略的評價指標
JVM信息查看
GC通用日志解讀
jvm的card table數據結構
Java類初始化順序
Java對象結構及大小計算
Java的類加載機制
Java對象分配簡要流程
年老代過大有什么影響
Survivor空間溢出實例
關于Object=null
Java線程與Xss
序
本文主要演示Java類的初始化順序
初始化順序
對于靜態變量、靜態初始化塊、變量、初始化塊、構造器,它們的初始化順序依次是(靜態變量、靜態初始化塊)>(變量、初始化塊)>構造器。
實例代碼
public class InitialOrderTest {/* 靜態變量 */public static String staticField = "靜態變量";/* 變量 */public String field = "變量";/* 靜態初始化塊 */static {System.out.println( staticField );System.out.println( "靜態初始化塊" );}/* 初始化塊 */{System.out.println( field );System.out.println( "初始化塊" );}/* 構造器 */public InitialOrderTest(){System.out.println( "構造器" );}public static void main( String[] args ){new InitialOrderTest();}
}
輸出
運行以上代碼,我們會得到如下的輸出結果:
靜態變量
靜態初始化塊
變量
初始化塊
構造器
繼承的情況
實例代碼
class Parent {/* 靜態變量 */public static String p_StaticField = "父類--靜態變量";/* 變量 */public String p_Field = "父類--變量";protected int i = 9;protected int j = 0;/* 靜態初始化塊 */static {System.out.println( p_StaticField );System.out.println( "父類--靜態初始化塊" );}/* 初始化塊 */{System.out.println( p_Field );System.out.println( "父類--初始化塊" );}/* 構造器 */public Parent(){System.out.println( "父類--構造器" );System.out.println( "i=" + i + ", j=" + j );j = 20;}
}public class SubClass extends Parent {/* 靜態變量 */public static String s_StaticField = "子類--靜態變量";/* 變量 */public String s_Field = "子類--變量";/* 靜態初始化塊 */static {System.out.println( s_StaticField );System.out.println( "子類--靜態初始化塊" );}/* 初始化塊 */{System.out.println( s_Field );System.out.println( "子類--初始化塊" );}/* 構造器 */public SubClass(){System.out.println( "子類--構造器" );System.out.println( "i=" + i + ",j=" + j );}/* 程序入口 */public static void main( String[] args ){System.out.println( "子類main方法" );new SubClass();}
}
輸出
父類--靜態變量
父類--靜態初始化塊
子類--靜態變量
子類--靜態初始化塊
子類main方法
父類--變量
父類--初始化塊
父類--構造器
i=9, j=0
子類--變量
子類--初始化塊
子類--構造器
i=9,j=20
子類的靜態變量和靜態初始化塊的初始化是在父類的變量、初始化塊和構造器初始化之前就完成了。靜態變量、靜態初始化塊,變量、初始化塊初始化了順序取決于它們在類中出現的先后順序。
分析
(1)訪問SubClass.main(),(這是一個static方法),于是裝載器就會為你尋找已經編譯的SubClass類的代碼(也就是SubClass.class文件)。在裝載的過程中,裝載器注意到它有一個基類(也就是extends所要表示的意思),于是它再裝載基類。不管你創不創建基類對象,這個過程總會發生。如果基類還有基類,那么第二個基類也會被裝載,依此類推。
(2)執行根基類的static初始化,然后是下一個派生類的static初始化,依此類推。這個順序非常重要,因為派生類的“static初始化”有可能要依賴基類成員的正確初始化。
(3)當所有必要的類都已經裝載結束,開始執行main()方法體,并用new SubClass()創建對象。
(4)類SubClass存在父類,則調用父類的構造函數,你可以使用super來指定調用哪個構造函數。基類的構造過程以及構造順序,同派生類的相同。首先基類中各個變量按照字面順序進行初始化,然后執行基類的構造函數的其余部分。
(5)對子類成員數據按照它們聲明的順序初始化,執行子類構造函數的其余部分。
類初始化
public class ClinitDemo {/*** 父類中定義的靜態語句塊要優于子類的變量賦值操作* JVM保證一個類的clinit方法在多線程中被正確加鎖、同步*/static class Parent {public static int A = 1;static {A = 2;}}static class Sub extends Parent {public static int B = A;}public static void main(String[] args) {System.out.println(Sub.B);}}
輸出2
static變量
public class Test { static { i = 0; // 給變量復制可以正常編譯通過
// System.out.print(i); // 這句編譯器會提示“非法向前引用” } static int i = 1; static int j = 1; static{ j = 2; } public static void main(String[] args){ System.out.println(Test.i); //1 System.out.println(Test.j); //2 }
}
不觸發初始化實例
實例一二
/*** 被動使用類字段演示一:* 通過子類引用父類的靜態字段,不會導致子類初始化**/
class SuperClass {static {System.out.println("SuperClass init!");}public static int value = 123;
}class SubClass extends SuperClass {static {System.out.println("SubClass init!");}
}/*** 非主動使用類字段演示**/
public class NotInitialization {public static void main(String[] args) {
// System.out.println(SubClass.value);
//SuperClass init!
//123/*** 被動使用類字段演示二:* 通過數組定義來引用類,不會觸發此類的初始化**/SuperClass[] sca = new SuperClass[10];}
實例三
/*** 被動使用類字段演示三:** 常量在編譯階段會存入調用類的常量池中,本質上沒有直接引用到定義常量的類,* 因此不會觸發定義常量的類的初始化。**/
public class ConstClass {static {System.out.println("ConstClass init!");}public static final String HELLOWORLD = "hello world";
}
public class Test {public static void main(String[] args){System.out.println(ConstClass.HELLOWORLD);}
}
輸出
hello world
這里沒有初始化ConstClass類,是因為在編譯的時候,常量(static final 修飾的)會存入調用類的常量池【這里說的是main函數所在的類的常量池】,調用的時候本質上沒有引用到定義常量的類,而是直接訪問了自己的常量池。
參考
java類的初始化順序(
引了大半
)Java類加載的時機