1 init和clinit区别
1.1 init和clinit方法执行时机不同
init是对象构造器方法,也就是说在程序执行 new 一个对象调用该对象类的 constructor 方法时才会执行init方法
类初始化时机
clinit是类构造器方法,也就是在jvm进行类加载—–验证—-解析—–初始化,中的初始化阶段jvm会调用clinit方法。
1.2 init和clinit方法执行目的不同
- init是instance实例构造器,对非静态变量解析初始化
- clinit是class类构造器对静态变量,静态代码块进行初始化。
class X {
static Log log = LogFactory.getLog(); // <clinit>
private int x = 1; // <init>
X(){
// <init>
}
static {
// <clinit>
}
}
2 clinit详解
在准备阶段,变量已经赋过一次系统要求的初始值,初始化阶段是执行类构造器<clinit>()方法的过程。
2.1 <clinit>
<clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的
编译器收集的顺序是由语句在源文件中出现的顺序所决定的,静态语句块中只能访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块可以赋值,但是不能访问如下代码
public class Test{
static{
i=0;//给变量赋值可以正常编译通过
System.out.print(i);//这句编译器会提示"非法向前引用"
}
static int i=1;
}
2.2 父类的<clinit>
虚拟机会保证在子类的<clinit>()方法执行之前,父类的<clinit>()方法已经执行完毕。
public class Test{
static{
i=0;//给变量赋值可以正常编译通过
System.out.print(i);//这句编译器会提示"非法向前引用"
}
static int i=1;
}
因此在虚拟机中第一个被执行的<clinit>()方法的类肯定是java.lang.Object。
由于父类的<clinit>()方法先执行,也就意味着父类中定义的静态语句块要优先于子类的变量赋值操作,
如下代码中,字段B的值将会是2而不是1。
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.3 接口
接口中不能使用静态语句块,但仍然有变量初始化的赋值操作,因此接口与类一样都会生成<clinit>()方法。
但接口与类不同的是,执行接口的<clinit>()方法不需要先执行父接口的<clinit>()方法。
只有当父接口中定义的变量使用时,父接口才会初始化。 另外,接口的实现类在初始化时也一样不会执行接口的<clinit>()方法。
接口中的属性都是static final类型的常量,因此在准备阶段就已经初始化话
网友评论