• Java类的主动加载和被动加载


    学习目标:

    学习类的主动加载和被动加载,类的主动加载和被动加载的区别就是是否激活了Clinit()函数。
    Clinit()函数的作用是对类的静态变量赋值,所以类是否激活Clinii()函数的外在表现就是是否触发了stattic代码块的内容。

    类的主动加载和被动加载是互斥的,因此学习一个就会知道另一个,所以先看情况比较好记的被动加载。


    类的被动加载情况:

    类被动加载情况总结
    在这里插入图片描述

    访问静态字段的时候,只有真正声明这个字段的类会被初始化

    class Parent{
        static{
            System.out.println("Parent的初始化过程");
        }
    
        public static int num = 1;
    }
    
    class Child extends Parent{
        static{
            System.out.println("Child的初始化过程");
        }
    }
    
    public class PassiveUse1 {
        @Test
        public void test1(){
            System.out.println(Child.num);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    执行结果只会输出Parent的初始化过程,而Child作为子类并没有被初始化

    通过数组定义类的引用,不触发类的初始化

    class Parent{
        static{
            System.out.println("Parent的初始化过程");
        }
    
        public static int num = 1;
    }
    
    class Child extends Parent{
        static{
            System.out.println("Child的初始化过程");
        }
    }
    
    public class PassiveUse1 {
        @Test
        public void test2(){
            Parent[] parents = new Parent[10];
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    执行后不会输出任何结果

    引用常量不会触发Clinit(),因为在链接阶段被赋值了已经。

    class Person{
        static{
            System.out.println("Person类的初始化");
        }
        public static final int NUM = 1;//在链接过程的准备环节就被赋值为1了。
        public static final String string = "qbc";//在链接过程的准备环节就被赋值为qbc了。
        public static final int NUM1 = new Random().nextInt(10);//此时的赋值操作需要在()中执行
    }
    
    
    public class PassiveUse2 {
        @Test
        public void test1(){
           System.out.println(Person.NUM);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    执行后不会输出Person类的初始化

    如果这个常量是一个对象,那就会触发初始化方法,因为在链接阶段不会进行赋值操作,除非常量是一个固定的数字在编译阶段已经赋值.静态变量和常量对象赋值操作都需要初始化操作(初始化阶段会真正执行代码)

    class Person{
        static{
            System.out.println("Person类的初始化");
        }
        public static final int NUM = 1;//在链接过程的准备环节就被赋值为1了。
        public static final int NUM1 = new Random().nextInt(10);//此时的赋值操作需要在()中执行
        public static final String string = "qbc";
    }
    public class PassiveUse2 {
        @Test
        public void test1(){
           System.out.println(Person.NUM1);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    执行结果会输出Person类的初始化

    Classloder的loader方法加载的类不会执行Clinit()

    class Person{
        static{
            System.out.println("Person类的初始化");
        }
        public static final int NUM = 1;//在链接过程的准备环节就被赋值为1了。
        public static final int NUM1 = new Random().nextInt(10);//此时的赋值操作需要在()中执行
        public static final String string = "qbc";
    }
    
    public class PassiveUse2 {
        @Test
        public void test3(){
            try {
                Class clazz = ClassLoader.getSystemClassLoader().loadClass("com.hhh.java1.Person");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    执行结果不会输出Person类的初始化

    基础的接口在子类初始的时候不会被初始化,只有调用接口静态变量的时候接口才会被clinit()

    class Father {
        static {
            System.out.println("Father类的初始化过程");
        }
    }
    
    class Son extends Father implements CompareB{
        static {
            System.out.println("Son类的初始化过程");
        }
    
        public static int num = 1;
    }
    
    interface CompareB {
        public static final Thread t = new Thread() {
            {
                System.out.println("CompareB的初始化");
            }
        };
        public static int NUM1 = new Random().nextInt();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    测试用例1

    public class ActiveUse3 {
        @Test
        public void test2() {
            System.out.println(Son.num);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    执行结果Father类的初始化过程 Son类的初始化过程,可以发现接口没执行clinit()

    测试用例2

    public class ActiveUse3 {
        @Test
        public void te(){
            System.out.println(Son.NUM1 );
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    执行结果CompareB的初始化,发现调用接口的静态方法会触发接口的Clinit()

    测试用例3

    // 修改CompareB接口,加入default方法
    interface CompareB {
    
        public static final Thread t = new Thread() {
            {
                System.out.println("CompareB的初始化");
            }
        };
        public static int NUM1 = new Random().nextInt();
        public default void method1(){
            System.out.println("你好!");
        }
    }
    public class ActiveUse3 {
        @Test
        public void test2() {
            System.out.println(Son.num);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    执行结果CompareB的初始化发现接口也初始化了,这说明了接口定义defaut方法,子类初始化,接口也会一起被初始化。

  • 相关阅读:
    【云原生】K8S--负载均衡详细介绍;什么是K8S的负载均衡?
    LuatOS-SOC接口文档(air780E)-- ftp - ftp 客户端
    算法通过村第七关-树(递归/二叉树遍历)白银笔记|递归实战
    图像处理技术与应用(二)
    第九章 APP项目测试(54) 测试工具
    判断一个数是否偶数(深度思考)
    Java中List去重和Stream去重的示例分析
    Arrays.asList() 使用说明
    302接口重定向进行接口测试(python+request)
    数据结构——排序算法——基数排序
  • 原文地址:https://blog.csdn.net/qq_37771209/article/details/126318522