楔子
类字面常量
Java还提供了另一种方法来生成对象的引用:类字面常量,对上述程序来说,就像这样FancyToy.class
。这样做不仅更简单,而且更安全,因为它在编译时就会受到检查。并且它根除了对forName
方法的调用,所以效率更高。
类字面量不可可以用于普通类,也可以应用于接口、数组以及基本数据类型。另外,对应基本数据类型包装类,还有一个标准字段TYPE
.
当使用.class
来创建对Class
对象的引用时,不会自动地初始化该Class对象
。为了使用类而做的准备工作实际包含了三个步骤
- 加载:这是类加载器执行的。该步骤检查字节码(通常在classpath所指定的路径中查找,但这并非是必须的),并从这些字节码 中创建一个Class对象。
- 链接:在链接阶段将验证类中的字节码,为
static
字段分配存储空间,并且如果需要的话,将解析这个类创建的对其他类的所有引用。 - 初始化:如果该类具有超类,则先初始化超类,执行static初始化器和static初始化块。
直到第一次引用一个static方法(构造器隐式地是static)或者非常量的static字段,才会进行类初始化。
package com.study.java8.char18;
/**
* @author study
* @version 1.0
* @description
* @date 2020/6/7 20:31
*/
// 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("com.study.java8.char18.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
的访问中所看到的那样。