Fork me on GitHub

Java编程思想———类型信息之Class对象

运行时类型信息(Runtime Type InformationRTTI)使得你可以在程序运行时发现和使用类型信息。Java在运行时识别对象和类的信息,主要有两种方式:一种是传统的RTTI,它假定我们在编译时已经知道了所有的类型;另一种是反射机制,它允许我们在运行时发现和使用类的信息。

RTTI的形式包含:

  1. 传统的类型转换,如“(Shape)”,由RTTI确保类型转换的正确性,如果执行了一个错误的类型转换,就会抛出一个ClassCastException异常。
  2. 代表对象的类型的Class对象。通过查询Class对象可以获取运行时所需的信息。
  3. 关键字instanceof。它返回一个布尔值,告诉我们对象是不是某个特定类型的实例。

Class对象

所有的类都是在对其第一次使用时,动态加载到JVM中的,当程序创建第一个对类的静态成员的引用时,就会加载这个类。这个证明构造函数也是类的静态方法,即使在构造器之前并没有使用static关键字。因此,使用new操作符创建类的新对象也会被当做对类的静态成员的引用。

因此,Java程序在它开始运行之前并非被完全加载,其各个部分是在必需时才加载的。

类加载器首先检查这个类的CLass对象是否已经加载。如果尚未加载,默认的类加载器就会根据类名查找.class文件。在这个类的字节码被加载时,它们会接受验证,以确保其没有被破坏,并且不包含不良Java代码。

一旦某个类的Class对象被载入内存,它就被用来创建这个类的所有对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Candy {
static { System.out.println("Loading Candy"); }
}

class Gum {
static { System.out.println("Loading Gum"); }
}

class Cookie {
static { System.out.println("Loading Cookie"); }
}

public class SweetShop {
public static void main(String[] args) {
System.out.println("inside main");
new Candy();
System.out.println("After creating Candy");
try {
Class.forName("Gum");
} catch(ClassNotFoundException e) {
System.out.println("Couldn't find Gum");
}
System.out.println("After Class.forName(\"Gum\")");
new Cookie();
System.out.println("After creating Cookie");
}
}

//output
inside main
Loading Candy
After creating Candy
Loading Gum
After Class.forName("Gum")
Loading Cookie
After creating Cookie

从输出中可以看到,Class对象仅在需要的时候才被加载,static初始化是在类加载时进行的。

类字面常量

Java还提供了另外一种方法来生成对Class对象的引用,即使用类字面常量。
如: Gum.class

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

当使用.class来创建对Class对象的引用时,不会自动地初始化该Class对象。为了使用类而做的准备工作实际包含三个步骤:

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

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

泛化的Class引用

Class引用总是指向某个Class对象,它可以制造类的实例,并包含可作用于这些实例的所有方法代码。它还包含该类的静态成员,因此,Class引用表示的就是它所指向的对象的确切类型,而该对象便是Class类的一个对象。

Java SE5允许你对Class引用所指向的Class对象的类型进行限定,用到了泛型语法。

1
2
3
4
5
6
7
8
9
public class GenericClassReferences {
public static void main(String[] args) {
Class intClass = int.class;
Class<Integer> genericIntClass = int.class;
genericIntClass = Integer.class;
intClass = double.class;
// genericIntClass = double.class; // Illegal
}
}

instanceof与Class的等价性

在查询类信息时,以instanceof的形式(即以instanceof的形式或isInstance()的形式,它们产生相同的结果)与直接比较Class对象有一个很重要的差别。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package typeinfo

class Base {}
class Derived extends Base {}

public class FamilyVsExactType {
static void test(Object x) {
System.out.println("Testing x of type " + x.getClass());
System.out.println("x instanceof Base " + (x instanceof Base));
System.out.println("x instanceof Derived " + (x instanceof Derived));
System.out.println("Base.isInstance(x) " + Base.class.isInstance(x));
System.out.println("Derived.isInstance(x) " + Derived.class.isInstance(x));
System.out.println("x.getClass() == Base.class " + (x.getClass() == Base.class));
System.out.println("x.getClass() == Derived.class " + (x.getClass() == Derived.class));
System.out.println("x.getClass().equals(Base.class) " + x.getClass().equals(Base.class));
System.out.println("x.getClass().equals(Derived.class) " + x.getClass().equals(Derived.class));
}
public static void main(String[] args) {
test(new Base());
test(new Derived());
}
}

//output
Testing x of type typeinfo.Base
x instanceof Base true
x instanceof Derived false
Base.isInstance(x) true
Derived.isInstance(x) false
x.getClass() == Base.class true
x.getClass() == Derived.class false
x.getClass().equals(Base.class) true
x.getClass().equals(Derived.class) false
Testing x of type typeinfo.Derived
x instanceof Base true
x instanceof Derived true
Base.isInstance(x) true
Derived.isInstance(x) true
x.getClass() == Base.class false
x.getClass() == Derived.class true
x.getClass().equals(Base.class) false
x.getClass().equals(Derived.class) true

instanceofisInstance()生成的结果完全一样,equals()==生成的结果完成一样。instanceof判断某个对象是否是指定类或者指定类的子类。而equals()比较的是实际的Class对象,不考虑继承。

求鼓励,求支持!