通过子类来引用父类中定义的静态字段,只会触发父类的初始化而不会触发子类的初始化。至于是否要触发子类的加载和验证,在JVM规范中并未明确规定。对于Sun HotSpotJVM,可通过-XX:+TraceClassLoading观察到此操作会导致子类的加载。
数组类型 TypeA[] arrays = new TypeA[10] 此过程并不会触发TypeA的初始化,因为这个动作由字节码指令newarray触发。常量 常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类、因此不会触发定义常量的类的初始化。<clinit>()方法的特性
类的初始化阶段是执行类构造器<clinit>()方法的过程<clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和静态句块中的语句合并产生1.如果一个类没有静态块,也没有对类变量的赋值操作,那么编译器可以不为这个类生成<clinit>() 方法 2.接口虽然没有静态块,但是有类变量的赋值操作,依然会生成<clinit>() 方法 3.如果父类没有类变量赋值时,父接口才会初始化 4.如果接口没有类变量赋值,则接口的实现类也不会执行接口的<clinit>()
编译器收集的顺序时由语句在源文件中出现的顺序所决定的,静态语句块中只能访问到定义在静态语句块之前的变量,在前面的静态语句块可以赋值但是不能访问。如下所示,(1)能够正常编译通过,但是(2)在eclipse中会有如下提示Cannot reference a field before it is defined
public class Test { static { i = 0;//(1) System.out.println(i);//(2) } static int i = 1; }<clinit>()方法不需要显示的调用父类构造器,虚拟机会保证先执行父类<clinit>() 完之后再执行子类该方法。
1.在虚拟机中第一个执行的<clinit>()的类一定是java.lang.Object。 2.<clinit>() 执行静态块,优先执行父类<clinit>(),因此父类的静态块要优于子类执行。
JVM为了保证<clinit>() 方法在多线程环境中使用,使用了加锁与同步,如果多个线程同时去初始化一个类,那么一个线程去执行这个类的<clinit>() 方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>() 方法完毕。为了保证同一个类加载器下,一个类型只会初始化一次。执行<clinit>() 方法的线程退出后,其他线程唤醒之后不会再次进入<clinit>()方法。