类加载时机

五种会触发初始化的场景,有且只有以下五种,这五种场景中的行为称为对一个类进行主动引用

一、new、getstatic、putstatic或invokestatic
遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。
生成这4条指令的最常见的Java代码场景是:使用new关键字 实例化对象的时候、读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常 量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。
这四种命令的描述中均有:One successful resolution of the field,the class or interface that declared the resolved field is initized if that class or interface has not already been initial.

  • new:Create new object

  • getstatic: get static field from class(The value of the class or – interface field is fetched and pushed onto the operand stack)

  • putstatic:set static field in class.(The value is popped from the operand stack)

  • invokestatic:Invoke a class(static)method

public class Test  {
    public static int i=10;
    static {
        System.out.println("test static");}
    public Test(){
        System.out.println("test constructor");}
    public static void f(){
        System.out.println("static method");}

}

//测试:
public class Father   {

    public static void main(String[] args) {
      //以下语句单独执行
        Test t=new Test();①②
       System.out.println(Test.i);① 并不会触发实例构造方法执行,只有new会触发构造方法执行
       Test.f();①②③
       -----------华丽的分割线---------------
    //若以下两条语句一起执行   
      System.out.println(Test.i)//①
      Test t=new Test()//② 静态块已执行,不会再初始化,new执行实例构造方法
      
    }

}

二、反射

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

三、当初始化一个类时,但发现父类未初始化

public class Test2 {
    static {
        System.out.println("test2");}
}
//Test继承Test2
public class Test extends Test2 { ...}
public class Father   {

    public static void main(String[] args) {
       System.out.println(Test.i)//④①
    }
 }

四、虚拟机启动,用户需要指定一个要执行的主类(包含main)方法的那个类)。虚拟机会初始化这个主类。
因为main方式是static方法,所以调用static方法先会加载此类

依然使用Test继承Test2例子
public class Father   {
static {
        System.out.println("Test2");}
  public Father(){
        System.out.println("Father constructor");}
    public static void main(String[] args) {
       System.out.println(Test.i)//⑤④①  【并没有new,所以⑥不执行】
    }
 }

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

除了以上五种,其他引用类的方式均不会触发初始化,称为被动引用

一、通过子类引用父类的静态字段,不会导致子类初始化

public class Main  {

    public static  int i=0;
    static {
        System.out.println("main static");
    }
}
public class Test2 extends Main {
    static {
        System.out.println("test2");
    }
}

public class Test  {
        public static void main(String[] args) {
           System.out.println(Test2.i);
        }
    }
 结果:main static 0

对于静态字段,只有直接定义这个字段的类才会被初始化,因此通过其子类引用父类中定义的静态字段,只会触发父类的初始化而不会触发子类的初始化。
二、通过数组定义来引用类,不会触发此类的初始化

复用上面的Main代码
public class Test  {
        public static void main(String[] args) {
          Main[] ta=new Main[10];并不会初始化这个类
        }
    }

在这里插入图片描述
anewarray
A new array with components of that type,and a reference arrayref to this new array object is pushed onto the operand stack.All components of the new array are initialized to null,the default value for reference types.
创建一个引用型(如类, 接口, 数组)的数组, 并将其引用值压入栈顶
三、常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化

public class Main  {

    public static final int i=0;
    static {
        System.out.println("main static");
    }
}
public class Test  {


        public static void main(String[] args) {
            int c=Main.i;
        }
    }

结果中并不会初始化Main类。因为被final修饰的常量在“准备”阶段已经被赋值。

接口的初始化方式,< clinit>参考“类加载机制class loader”的五Initialization

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值