直接上代碼1:
class Parent{
public static void test(){
A=3;
System.out.println("test方法"+A);
}
static{
System.out.println("par類靜態塊執行");
A=2;
}
public static int A=1;
}
class Sub extends Parent{
public static int B=A;
static{
System.out.println("sub類靜態塊執行");
}
}
public class TestStaticOrder {
public static void main(String[] args) {
System.out.println(Sub.B);
}
}
par類靜態塊執行
sub類靜態塊執行
1
再看代碼2
class Parent{
public static void test(){
A=3;
System.out.println("test方法"+A);
}
public static int A=1;
static{
System.out.println("par類靜態塊執行");
A=2;
}
}
class Sub extends Parent{
public static int B=A;
static{
System.out.println("sub類靜態塊執行");
}
}
public class TestStaticOrder {
public static void main(String[] args) {
System.out.println(Sub.B);
}
}
這段代碼的執行結果是:
par類靜態塊執行
sub類靜態塊執行
2
其實代碼1和代碼2, 區別只是 靜態塊的代碼和定義靜態變量代碼 位置互換了一下.執行結果卻不一樣.
前提說明:在編譯生成的class文件時,會自動產生兩個方法,一個是類的初始化方法<clinit>,另一個是實例的初始化方法<init>
<clinit>方法在jvm第一次加載class文件時調用(所以靜態塊只會執行一次),包括靜態塊的執行和靜態變量初始化.
<init>方法 在實例創建時候調用, 就是生成對象的時候, 例如new ,反射等等
結論:
1.虛擬機會保證在子類的<clinit>方法執行之前,父類的<clinit>方法已經執行完畢.(第一個被執行的肯定是Object類)
2.由於父類的<clinit>方法先執行,所以父類的靜態塊等要優先於子類的操作
3.<clinit>方法是由編譯器自動收集類中的所有類變量的賦值動作和靜態塊中的語句合併產生的.
編譯器收集的順序,是由語句在源文件中出現的順序決定的.
靜態語句塊中只能訪問到定義在靜態語句塊之前的變量
定義在它之後的變量,在前面的靜態語句塊中可以賦值,但是不能訪問.(例如代碼1中靜態塊直接輸出A是會報錯的.)
4.接口中不能使用靜態語句塊,單仍然有變量初始化的賦值操作,因此接口也會生成<clinit>方法.
但是與類不同的是,執行接口的<clinit>方法不需要先執行父類接口的<clinit>方法.
只有當父接口中定義的變量被使用時,父接口才會被初始化.
接口的實現類在初始化時也一樣不會執行接口的<clinit>方法.
5.可以看到test()方法並沒有執行.static方法不在clinit的執行範圍內.可以說跟加載順序沒有任何關係.