千家信息网

Java类的加载时机是什么时候

发表于:2025-02-23 作者:千家信息网编辑
千家信息网最后更新 2025年02月23日,本篇内容主要讲解"Java类的加载时机是什么时候",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"Java类的加载时机是什么时候"吧!必须初始化的四种情况有四
千家信息网最后更新 2025年02月23日Java类的加载时机是什么时候

本篇内容主要讲解"Java类的加载时机是什么时候",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"Java类的加载时机是什么时候"吧!

必须初始化的四种情况

有四种情况类是必须要进行初始化的,对于这四种情况原文描述如下:

但是对于初始化阶段,虚拟机规范则是严格规定了有且只有4种情况必须立即对类进行初始化,而加载、验证、准备自然需要在此之前开始。

  • 1:遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。生成这4条指令最常见的java代码场景是:使用new关键字实例化对象的时候、读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。

  • 2:使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。

  • 3:当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。

  • 4:当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。

以上四点我们一一用代码来验证,第一点里面说到了四种初始化的场景,分别是:

  • ①用new关键字实例化对象

  • ②读取类静态字段

  • ③设置类的静态字段

  • ④调用一个类的静态方法

在验证之前需要达成一个共识:虚拟机在初始化类时会执行static语句块中的操作,因此我们可以根据静态语句块中的代码是否执行了来判断类是否加载。为此我创建了一个SubClass类

package com.test.jvm.classloading;/** * @author fc */public class SubClass {    static{        System.out.println("子类初始化");    }    public static int a = 10;    public static int getA(){        return a;    }}

在main方法中分别执行(每次执行一条)以下四条代码来模拟上面四个场景

package com.test.jvm.classloading;/** * @author fc */public class Main {    public static void main(String[] args) {        SubClass subClass = new SubClass();        System.out.println(SubClass.a);        SubClass.getA();        SubClass.a = 30;    }}

结果不出所料,输出结果都包含"子类初始化",说明以上四种方式确实可以会触发类的初始化。

接下来看第二点,对类进行反射调用时会触发类的初始化

package com.test.jvm.classloading;/** * @author fc */public class Main {    public static void main(String[] args) throws ClassNotFoundException {        Class.forName("com.test.jvm.classloading.SubClass");    }}

以上的反射调用同样正常输出了"子类初始化"。

第三点如果父类没有进行初始化,则要先触发父类的初始化,再创建一个父类,并且让之前的子类继承父类

package com.test.jvm.classloading;/** * @author fc */public class SuperClass {    static {        System.out.println("父类初始化");    }    public static int b = 20;}
package com.test.jvm.classloading;/** * @author fc */public class SubClass extends SuperClass {    static{        System.out.println("子类初始化");    }    public static int a = 10;    public static int getA(){        return a;    }}

这时我们再次执行上面的main方法里面的任意一条测试语句,这时发现在原来的输出"子类初始化"前输出了"父类初始化",说明了两点:①父类同样会初始化;②父类会先于子类初始化。

第四点虚拟机会先初始化包含main方法的主类,这时我们在主类中加入静态代码块

package com.test.jvm.classloading;/** * @author fc */public class Main {    static {        System.out.println("初始化主类");    }    public static void main(String[] args) throws ClassNotFoundException {        SubClass subClass = new SubClass();    }}

可以看到输出结果如下,完全印证了第四点。

不主动进行初始化

而对于不会主动进行初始化的情况在该书中也有以下几种情况

第一种是通过子类类名调用父类静态代码(包括静态方法和静态变量)不会进行初始化,以下也通过代码进行说明

package com.test.jvm.classloading;/** * @author fc */public class Main {    public static void main(String[] args) throws ClassNotFoundException {        System.out.println(SubClass.b);    }}

输出如下,可以看到只初始化了父类而没有初始化子类。

第二种是通过数组来创建对象不会触发此类的初始化

package com.test.jvm.classloading;/** * @author fc */public class Main {    public static void main(String[] args) throws ClassNotFoundException {        SuperClass[] supers = new SuperClass[10];    }}

输出为空。

第三种是调用final修饰的常量不会触发类的初始化,为此我在父类中加了一个常量

package com.test.jvm.classloading;/** * @author fc */public class SuperClass {    static {        System.out.println("父类初始化");    }    public static int b = 20;    public final static String STATE = "常量";}
package com.test.jvm.classloading;/** * @author fc */public class Main {    public static void main(String[] args) {        System.out.println(SuperClass.STATE);    }}

可以看到输出结果只是打印了常量的值,并没有初始化这个类。

补充

到这里对于书中描述的类的加载时机都已经用例子说明了,接下来展示一个在博主Boblim那看到的一个例子

/** * @author fc */class SingleTon {    private static SingleTon singleTon = new SingleTon();    public static int count1;    public static int count2 = 0;    private SingleTon() {        count1++;        count2++;    }    public static SingleTon getInstance() {        return singleTon;    }}public class Test {    public static void main(String[] args) {        SingleTon.getInstance();        System.out.println("count1=" + SingleTon.count1);        System.out.println("count2=" + SingleTon.count2);    }}

输出count1=1,count2=0,关于为什么会输出这个结果在那篇链接的博客已经做了详细的说明,同时这个输出结果也很好地佐证了下面这句话

类构造器()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static块)中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的。

正是给类变量赋值时是按照顺序进行的,所以上面count2又会被重新赋值为0,才导致这个输出结果。

到此,相信大家对"Java类的加载时机是什么时候"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

静态 输出 方法 子类 时候 结果 代码 情况 语句 常量 时机 字段 变量 场景 对象 顺序 反射 编译 验证 主动 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 戴尔15代服务器和14代服务器 幼儿园网络安全工作领导小组名单 要怎么才能吧默认数据库 网络安全管理作业 密云区综合软件开发介绍 网站后台用的数据库是哪个 数据库2012如何复制表 网络安全对显卡要求 蚂蚁金服北京网络技术有限公司 爬取多个城市的天气数据库 爱共享深圳网络技术 软件开发中级职称 落户上海 sql数据库中的视图 tbc怀旧服推荐一个联盟服务器 服务器pwr灯一直闪 网络安全产品销售薪酬 手机APP连接本地数据库 宁河软件开发怎么选 公元图片数据库的使用 黑客大曝光工业控制网络安全 公安局网络安全工作自查报告 5g网络技术 对我们的影响 计算机网络技术中通信信道 高考数据库局 传统软件开发的优缺点 地铁显示连接不了服务器 普通公司用什么服务器 方舟手游怎么建服务器 机器学习从哪里寻找合适的数据库 网易邮箱手机服务器无法登录
0