类字面常量

Java还提供了另一种方法来生成对Class对象的引用,好使用类字面常量。对上述程序来说就像下面这样:

FancyToy.class

这样做不仅简单,而且更安全,因为它在编译时就会受到检查(因此不需要置于try语句块中)。并且它根除了对forName()方法的调用,所以也更高效。

类字面常量不仅可以用于普通的类,也可以用于接口,数组及基本数据类型。另外,对于基本数据数据类型的包装器类,还有一个标准字段TYPE。TYPE字段是一个引用,指向对就的基本数据类型的Class对象,如下所示:

boolean.class Boolean.TYPE
char.class Character.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对象。为了使用类而做的准备工作实际包含三个步骤:

  • 加载,这是由类加载器执行的。该步骤将查询字节码(通常在classpath所指定的路径中查找,但这并非是必需的),并从这些字节码中创建一个Class对象。
  • 链接。在链接阶段将验证类中的字节码,为静态域分配存储空间,并且如果必须的话,将解析这个类创建的对其他类的所有引用。
  • 初始化。如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块。

初始化被延迟到了对静态方法(构造器隐式地是静态的)或者非常数静态域进行首次引用时才执行:

class Initable{
    public static final int staticFinal = 47;
    public static final int staticFinal2 =
            ClassInitialization.rand.nextInt(1000);

    static {
        System.out.println("Initializing Initable");
    }
}

class Initabl2{
    public static int staticNonFinal = 147;
    static {
        System.out.println("Initializing Initable2");
    }
}

class Initabl3{
    public static int staticNonFinal = 74;

    static {
        System.out.println("Initailizing Initable3");
    }
}
public class ClassInitialization {
    public static Random rand = new Random(System.currentTimeMillis());

    public static void main(String[] args) throws ClassNotFoundException {
        Class<Initable> initableClass = Initable.class;
        System.out.println("After creating Initable ref");
        //Does not trigger initialization;
        System.out.println(Initable.staticFinal);
        //Does trigger initialization;
        System.out.println(Initable.staticFinal2);

        //Does trigger initialization;
        System.out.println(Initabl2.staticNonFinal);

        Class<?> initabl3 = Class.forName("com.jianglei.typeinfo.Initabl3");
        System.out.println("After creating Initable3 ref");
        System.out.println(Initabl3.staticNonFinal);
    }
}

初始化有效地实现了尽可能的“惰性”。从对initable引用的创建中可以看到,仅使用.class语法来获得对类的引用不会引发初始化。但是,为了产生Class引用,Class.forName()立即就进行了初始化就像在对initable3引用的创建中所看到的

如果一个static final值是“编译期常量”,就像Initable.staticFinal那样,那么这个值不需要对Initable类进行初始化就可以被读取。但是,如果只是将一个域设置为staticfinal的,还不足以确保这种行为,例如,对Initable.staticFinal2的访问将强制进行类的初始化,因为它不是一个编译期常量。

如果一个static域不是final的,那么在对它访问时,总是要求在它被读取之前,要先进行链接(为这个域分配存储空间)和初始化(初始化该存储空间),就像在对Initable2.staticNonFinal的访问中所看到的那样。

results matching ""

    No results matching ""