• 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方法,子类初始化,接口也会一起被初始化。

  • 相关阅读:
    vue2中使用quill编辑器+表格功能
    2022一文了解科技特长生
    HTML静态网页作业html+css+javascript+jquery水果商城7页
    Python爬虫(8)
    基于大语言模型+RAG实现的日程管理
    车载软件架构——基础软件供应商&开发工具链(一)
    Windows与网络基础-21-计算机网络参考模型
    英语——分享篇——每日100词——601-700
    5G技术:革命性的网络速度和其对未来智能应用的推动
    利用MySQL主从复制延迟拯救误删数据
  • 原文地址:https://blog.csdn.net/qq_37771209/article/details/126318522