第十四章 类字面常量 不会自动地初始化该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

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

猜你喜欢

转载自www.cnblogs.com/liudaihuablogs/p/9313549.html