深入理解JVM(12)——类加载(2)——类的主动使用和被动使用

目录

1.Java对类的主动使用和被动使用

1.1 主动使用(七种)

(1)上述主动使用规则的典型举例

(2)主动使用与final

(3)初始化时类与接口的区别

1.2 被动使用

2.Java虚拟机与程序的生命周期

3.数组创建本质分析

4.类加载的准备阶段和初始化阶段的重要意义


1.Java对类的主动使用和被动使用

所有Java虚拟机实现必须在每个类或接口被Java程序“首次主动使用”时才初始化

1.1 主动使用(七种)

  • 1.通过new关键字创建类的实例
  • 2.访问某个类或接口的静态变量(字节码助记符getstatic),或对该静态变量赋值(字节码助记符putstatic)
  • 3.调用类的静态方法(字节码助记符invokestatic)
    • 其实第3种和第二种本质上可以划分为同一种情况,因为它们反映到字节码助记符的层面上大体上是类似的含义
    • 注意:
      • 只有当程序访问的静态变量或静态方法确实在当前类或当前接口中定义时,才可以认为是对类或接口的主动使用
  • 4.反射(如Class.forName("com.test.Test")),通过反射的方式获得类的对象,也是对类的主动使用
  • 5.初始化一个类的子类
  • class Parent{}
    
    class Child extend Parent{}
    
    
    //当我去初始化Child类的时候,也会对Parent进行初始化
  • 6.Java虚拟机启动的时候被标记为启动类的类,即包含Main方法的类
  • 7.JDK1.7开始提供动态语言支持(如我们可以在JVM上使用脚本语言的引擎调用JavaScript(动态语言)代码):
  •     java.lang.invoke.MethodHandle实例的解析结果REF_getStastic、REF_putStastic、REF_invokeStastic句柄对应的类没有初始化,则进行初始化

(1)上述主动使用规则的典型举例

代码演示1:通过new关键字创建类的实例

public class Demo01 {
    public static void main(String[] args) {
        MyParent04 myParent04 =new MyParent04();
        System.out.println("==============");
        MyParent04 myParent04_1 = new MyParent04();
    }
}

class MyParent04{

    static {
        System.out.println("MyParent03 static code");
    }
}

运行结果:

分析1:

  • 类被首次主动使用时(上述代码为new出对象实例),进行初始化,可以看到第二次主动使用该类时,并未再次进行初始化

代码演示2:访问父类的静态变量

public class Demo01 {
    public static void main(String[] args) {
        System.out.println(MyChild.str1);
    }
}

class MyParent
{
    public static String str1="Hello,World!";

    static{
        System.out.println("MyParent static block");
    }
}

class MyChild extends MyParent
{
    static{
        System.out.println("MyChild static block");
    }
}

运行结果:

分析2:

  • 对于静态字段来说,只有直接定义了该字段的类才会被初始化
  • 上述静态字段str1,直接定义它的类为MyParent,所以在访问str1的时候,MyParent被主动使用,MyParent会被初始化,虽然使用了MyChild这个名字,但是没有主动使用MyChild中的静态方法或变量,所有Java虚拟机实现必须在每个类或接口被Java程序“首次主动使用”时才初始化,所以MyChild类不会被初始化,所以,最终只会调用MyParent中的静态代码块,而不会调用MyChild中的静态代码块

此处疑问:MyChild类没有被初始化,但有没有被加载呢?

我们可以使用JVM的参数-XX:+TraceClassLoading参数用于追踪类的加载信息并打印

   

我的查看结果:

  • 可以发现MyChild虽然没有被初始化,但被加载了

其他JVM参数:

//JVM的一些选项默认开启,一些默认关闭

-XX:+<option>     表示开启option选项

-XX:-<option>     表示关闭option选项

-XX:<option>=<value>  表示option选项的值设置为value

代码演示3:访问子类的静态变量

public class Demo01 {
    public static void main(String[] args) {
        System.out.println(MyChild.str2);
    }
}

class MyParent
{
    public static String str1="Hello,World!";

    static{
        System.out.println("MyParent static block");
    }
}

class MyChild extends MyParent
{
    public static String str2="Welcome";

    static{
        System.out.println("MyChild static block");
    }
}

运行结果:

分析3:

  • 对子类的静态字段str2的调用是对MyChild类的主动使用,所以子类MyChild会被初始化,由于当初始化一个子类时,要求父类全部都被初始化,所以父类MyParent也会被初始化,所以最终结果先执行了父类静态代码块,后执行了子类静态代码块

代码演示4:访问包含Main的类

package JVMDemo01;

class MyParent
{
    public static String str1="Hello,World!";

    static{
        System.out.println("MyParent static block");
    }
}

class MyChild extends MyParent
{
    public static String str2="Welcome";

    static{
        System.out.println("MyChild static block");
    }
}

public class Demo01 {

    static {
        System.out.println("Demo01 static block");
    }

    public static void main(String[] args) {
        System.out.println(MyChild.str2);
    }
}

分析4:

  • 访问包含Main方法的类,即主动使用Demo01,导致了Demo01先进行了初始化
  • 然后对子类的静态字段str2的调用是对MyChild类的主动使用,所以子类MyChild会被初始化,由于当初
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值