🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### [类字面常量](https://lingcoder.gitee.io/onjava8/#/book/19-Type-Information?id=%e7%b1%bb%e5%ad%97%e9%9d%a2%e5%b8%b8%e9%87%8f) Java 还提供了另一种方法来生成类对象的引用:**类字面常量**。对上述程序来说,就像这样:`FancyToy.class;`。这样做不仅更简单,而且更安全,因为它在编译时就会受到检查(因此不必放在`try`语句块中)。并且它根除了对`forName()`方法的调用,所以效率更高。 类字面常量不仅可以应用于普通类,也可以应用于接口、数组以及基本数据类型。另外,对于基本数据类型的包装类,还有一个标准字段`TYPE`。`TYPE`字段是一个引用,指向对应的基本数据类型的`Class`对象,如下所示: | ...等价于... | | --- | | boolean.class | Boolean.TYPE | | char.class | Character.TYPE | | byte.class | Byte.TYPE | | short.class | Short.TYPE | | int.class | Integer.TYPE | | long.class | Long.TYPE | | float.class | Float.TYPE | | double.class | Double.TYPE | | void.class | Void.TYPE | 我的建议是使用`.class`的形式,以保持与普通类的一致性。 注意,有一点很有趣:当使用`.class`来创建对`Class`对象的引用时,不会自动地初始化该`Class`对象。为了使用类而做的准备工作实际包含三个步骤: 1. **加载**,这是由类加载器执行的。该步骤将查找字节码(通常在 classpath 所指定的路径中查找,但这并非是必须的),并从这些字节码中创建一个`Class`对象。 2. **链接**。在链接阶段将验证类中的字节码,为`static`字段分配存储空间,并且如果需要的话,将解析这个类创建的对其他类的所有引用。 3. **初始化**。如果该类具有超类,则先初始化超类,执行`static`初始化器和`static`初始化块。 直到第一次引用一个`static`方法(构造器隐式地是`static`)或者非常量的`static`字段,才会进行类初始化。 ~~~ // typeinfo/ClassInitialization.java import java.util.*; class Initable { static final int STATIC_FINAL = 47; static final int STATIC_FINAL2 = ClassInitialization.rand.nextInt(1000); static { System.out.println("Initializing Initable"); } } class Initable2 { static int staticNonFinal = 147; static { System.out.println("Initializing Initable2"); } } class Initable3 { static int staticNonFinal = 74; static { System.out.println("Initializing Initable3"); } } public class ClassInitialization { public static Random rand = new Random(47); public static void main(String[] args) throws Exception { Class initable = Initable.class; System.out.println("After creating Initable ref"); // Does not trigger initialization: System.out.println(Initable.STATIC_FINAL); // Does trigger initialization: System.out.println(Initable.STATIC_FINAL2); // Does trigger initialization: System.out.println(Initable2.staticNonFinal); Class initable3 = Class.forName("Initable3"); System.out.println("After creating Initable3 ref"); System.out.println(Initable3.staticNonFinal); } } ~~~ 输出结果: ~~~ After creating Initable ref 47 Initializing Initable 258 Initializing Initable2 147 Initializing Initable3 After creating Initable3 ref 74 ~~~ 初始化有效地实现了尽可能的“惰性”,从对`initable`引用的创建中可以看到,仅使用`.class`语法来获得对类对象的引用不会引发初始化。但与此相反,使用`Class.forName()`来产生`Class`引用会立即就进行初始化,如`initable3`。 如果一个`static final`值是“编译期常量”(如`Initable.staticFinal`),那么这个值不需要对`Initable`类进行初始化就可以被读取。但是,如果只是将一个字段设置成为`static`和`final`,还不足以确保这种行为。例如,对`Initable.staticFinal2`的访问将强制进行类的初始化,因为它不是一个编译期常量。 如果一个`static`字段不是`final`的,那么在对它访问时,总是要求在它被读取之前,要先进行链接(为这个字段分配存储空间)和初始化(初始化该存储空间),就像在对`Initable2.staticNonFinal`的访问中所看到的那样。