为了账号安全,请及时绑定邮箱和手机立即绑定

Java 中类的初始化过程

标签:
Java

先来一张 JVM 中的内存模型 。 

https://img1.sycdn.imooc.com//5b6db49b0001406205190277.jpg

 

在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 创建对象时 ,首先在堆上为对象分配足够的存储空间 。然后将堆中的属性分别赋上默认的初始值 。为属性显示赋值(如果有的话) 。最后执行构造器 。

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

https://img1.sycdn.imooc.com//5b6db4a80001fc9a05920469.jpg

 

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

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];
    }
}

复制代码

运行结果并没有打印出 ”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 种情况的第三种 :子类的初始化过程中其父类必须先初始化 ,但接口初始化时不要求其父接口也进行初始化 ,只有在用到父接口时 ,才会去初始化 。

原文出处:https://www.cnblogs.com/YJK923/p/9447245.html

点击查看更多内容
1人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消