zoukankan      html  css  js  c++  java
  • 第十四章 类字面常量 不会自动地初始化该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

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

    前进时,请别遗忘了身后的脚印。
  • 相关阅读:
    UVA 10462 Is There A Second Way Left?(次小生成树&Prim&Kruskal)题解
    POJ 1679 The Unique MST (次小生成树)题解
    POJ 2373 Dividing the Path (单调队列优化DP)题解
    BZOJ 2709 迷宫花园
    BZOJ 1270 雷涛的小猫
    BZOJ 2834 回家的路
    BZOJ 2506 calc
    BZOJ 3124 直径
    BZOJ 4416 阶乘字符串
    BZOJ 3930 选数
  • 原文地址:https://www.cnblogs.com/liudaihuablogs/p/9313549.html
Copyright © 2011-2022 走看看