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

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

    前进时,请别遗忘了身后的脚印。
  • 相关阅读:
    待解决问题集锦
    蓝桥杯--- 历届试题 连号区间数(水题)
    蓝桥杯---历届试题 翻硬币 (水题)
    nyoj 678 最小K个数之和
    hdoj 2 括号配对问题【数组模拟实现+STL实现】
    hdoj 1016 Prime Ring Problem
    nyoj 62 笨小熊
    nyoj 47 过河问题
    nyoj 456 邮票分你一半
    nyoj 171 聪明的kk
  • 原文地址:https://www.cnblogs.com/liudaihuablogs/p/9313549.html
Copyright © 2011-2022 走看看