直接上代码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的执行范围内.可以说跟加载顺序没有任何关系.