Java重庆幸运农场系统出租出售中类的初始化过程

在Java 虚拟机原理这本书中介绍了类会被初始化的 5 种情况 。

1 遇到 new getstatic putstatic 和 invokestatic 这 4 条指令时,这4 条指定分别对应使用 new 关键字创建对象,读取和设置一个静态字段(被 final 修饰的静态字段除外,因为已经在编译期间把结果放到常量池中了)和调用一个类的静态方法 。

2 对类进行反射调用时 。

3 当其父类没有被初始化时,要初始化父类 。

4 当虚拟机启动时,用户需要指定一个包含 main 方法的类,虚拟机会优先初始化这个类。

5 当使用JDK1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果 REF_getStatic、REF_putStatic、REF_invokeStatic 的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。

对照着这些再来看一下我们经常混淆的类中结构的加载顺序 ,可能会有更加深刻的认识 。

关于类中结构的加载顺序 ,首次创建对象时 ,类中的静态方法 / 静态字段首次被访问时 ,Java 解释器必须先查找类路径 ,以定位.class 文件;然后载入 .class (这将创建一个 Class 对象) ,有关静态初始化的所有动作都会执行 。因此 ,静态初始化只在 Class 对象首次加载的时候进行一次 。当用 new 创建对象时 ,首先在堆上为对象分配足够的存储空间 。然后将堆中的属性分别赋上默认的初始值 。为属性显示赋值(如果有的话) 。最后执行构造器 。

综上我们可以得出这样的结论 ,类的加载顺序整体上为 “ 父类静态—》子类静态—》父类非静态—》父类构造器—》子类非静态—》子类构造 。”

下面说一些看似类被初始化 ,其实并没有的情况 。

A 通过子类应用父类静态字段 ,不会导致子类初始化 。

复制代码
public class SuperClass {
static {
System.out.println("SuperClass Init !!!");
}
public static int value = 123;
}

public class SubClass extends SuperClass{
static {
System.out.println("SubClass Init !!!");
}
}

public class Test {
public static void main(String[] args) {
System.out.println(SubClass.value);
}
}

//SuperClass Init !!!
//123
复制代码
B 通过数组定义来引用类 ,不会触发此类的初始化 。( 左右拖动屏幕查看代码 )

复制代码
public class Test{
public static void main(String[] args) {
//System.out.println(SubClass.value);
SuperClass[] superClasses = new SuperClass[10];
}
}
复制代码
运行结果重庆幸运农场系统出租出售Q3266397597【dashengba.com】并没有打印出 ”SuperClass Init !!!” ,这里并没有触发 SuperClass 类的初始化 。这里触发了另一个名为 “ [Lcom.sun.jojo.noinitclass.SuperClass ” 的类的初始化 ,他是虚拟机自动创建的直接继承于 java.lang.Object 的子类 ,创建动作由字节码指令 newarray 触发 。

C 常量在编译期间就会调入类的常量池中 ,所以直接引用变量的类并没有被初始化 。( 左右拖动屏幕查看代码 )

复制代码
public class ConstClass {
static {
System.out.println("ConstClass Init !!!");
}
public static final int value = 123;
}

public class Test{
public static void main(String[] args) {
//System.out.println(SubClass.value);
//SuperClass[] superClasses = new SuperClass[10];
System.out.println(ConstClass.value);
}
}
// 123 PS. ConstClass Init !!! 并没有打印
复制代码
接口的初始化和类的初始化类似 ,区别在于 5 种情况的第三种 :子类的初始化过程中其父类必须先初始化 ,但接口初始化时不要求其父接口也进行初始化 ,只有在用到父接口时 ,才会去初始化 。

猜你喜欢

转载自blog.51cto.com/13920919/2158956
今日推荐