• 第十四章 类字面常量 不会自动地初始化该Class对象?


    除了使用反射以外【Class.forName("类的全限定名")】,Java还提供了另一种方法来生成对Class对象的引用,即使用 类字面常量:

      类.class;

    使用这种方式确实比使用反射更简单,而且更安全,因为它在编译期就会受到检查【因此不需要至于try-catch块中】。

    在类被使用前实际做了以下三个步骤:

    第一:加载。这是由类加载器执行的。该步骤将查找这个字节码文件(通常在classpath所指定的路径中查找,但这并非是必需的),并创建该字节码文件对象。

    第二:链接。在链接阶段将验证类中的字节码,为静态域分配存储空间,并且如果必须的话,将解析这个类创建的对其他类的所有引用。

    第三:初始化。执行静态初始化器和静态初始化块,如果该类具有超类,则对其初始化。

    但是如果使用.class的方式创建对Class对象的引用时,不会自动地初始化该Class对象【也就是上述的步骤三】。使用这种方式,它的初始化步骤被延迟到了对静态方法(构造器隐式地是静态的)或者非常数(不被final修饰)静态域进行首次引用时才执行。

    使用类字面常量:

    package 类型信息;
    
    public class InitA {
        //非常数
        public static int initInt = 10;
        //常数
        public static final int initInt2 = 20;
        //静态域
        static {
            System.out.println("初始化了InitA");
        }
        //静态方法
        public static void method() {
            System.out.println("静态方法");
        }
    }

    测试:

    package 类型信息;
    
    public class Test {
    
        public static void main(String[] args) {
            Class<InitA> inita = InitA.class;
        }
    
    }

    运行测试程序控制台没有任何输出。这说明没有执行初始化。

    但是当我们首次调用InitA类里的非常数静态域时:

    package 类型信息;
    
    public class Test {
    
        public static void main(String[] args) {
            //1.加载   2.链接
            Class<InitA> inita = InitA.class;
            //3.初始化【被延迟】
            System.out.println(InitA.initInt);
        }
    
    }

    控制台:

    初始化了InitA
    10

     首次调用静态方法时:

    package 类型信息;
    
    public class Test {
    
        public static void main(String[] args) {
            //1.加载   2.链接
            Class<InitA> inita = InitA.class;
            //3.初始化【被延迟到非常数静态域】
            //System.out.println(InitA.initInt);
            //3.初始化【被延迟到非静态方法】
            InitA.method();
        }
    
    }

    控制台:

    初始化了InitA
    静态方法

    测试调用常数静态域会不会初始化?

    package 类型信息;
    
    public class Test {
    
        public static void main(String[] args) {
            //1.加载   2.链接
            Class<InitA> inita = InitA.class;
            //3.初始化【被延迟到非常数静态域】
            //System.out.println(InitA.initInt);
            //3.初始化【被延迟到非静态方法】
            //InitA.method();
            
            //调用常数静态域会初始化吗?
            System.out.println(InitA.initInt2);
        }
    
    }

    控制台:

    20

    可见:常数静态域的调用并不会使得其被初始化。【也说明:类不被初始化  常数静态域【编译时常量】  就可以被读取到,而非常数静态域就必须初始化后才能被读取,对非常数静态域总是要求其被读取之前,要先进行链接和初始化(初始化该存储空间)】

    以上说明了使用.class创建的对象引用时,初始化过程被延迟到非常量静态域或静态方法了。

    使用反射呢?

    package 类型信息;
    
    public class Test {
    
        public static void main(String[] args) {
            //使用反射
            try {
                Class.forName("类型信息.InitA");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    
    }

    控制台:

    初始化了InitA

    可见:使用反射时,类被使用之前所需的三个步骤都被执行了。

    前进时,请别遗忘了身后的脚印。
  • 相关阅读:
    pytest入门 及allure2报告生成
    java 常用集合list与Set、Map区别及适用场景总结
    通俗地解释脏读、不可重复读、幻读
    Serializable接口的意义和用法
    maven基础
    Class.forName()用法详解
    关于getClass(),Object.class,getClassLoader的理解
    maven(一) maven到底是个啥玩意~
    TCP/IP协议族体系结构:死也不能忘记的四个层
    HTML表单常用标签
  • 原文地址:https://www.cnblogs.com/liudaihuablogs/p/9313549.html
Copyright © 2020-2023  润新知