在java中,我们在如何识别类和对象的信息?这这是RTTI的作用,即运行时类型识别(RTTI, Run-Time Type Identification)。
RTTI是Java中非常有用的机制,在Java运行时,RTTI维护类的相关信息。多态(polymorphism)是基于RTTI实现的。RTTI的功能主要是由Class类实现的。
Class类
Class类是"类的类"(class of classes)。如果说类是对象的抽象和集合的话,那么Class类就是对类的抽象和集合。
事实上,每一个类都持有其对应的Class
类的对象的引用(Object
类中的getClass()
能让我们获取到它),其中包含着与类相关的信息。非常容易注意到,针对每一个类,编译Java文件会生成一个二进制.class
文件,这其中就保存着该类对应的Class
对象的信息。
- 加载。由类加载器执行,查找字节码,创建一个
Class
对象。 - 链接。验证字节码,为静态域分配存储空间,如果必需的话,会解析这个类创建的对其他类的所有引用(比如说该类持有
static
域)。 - 初始化。如果该类有超类,则对其初始化,执行静态初始化器[注]和静态初始化块。
所有的类都是在对其第一次使用时,动态加载到JVM中去的。当程序创建第一个对类的静态成员的引用时,JVM会使用类加载器来根据类名查找同名的.class
——一旦某个类的Class
对象被载入内存,它就被用来创建这个类的所有对象。
public class Test
{
public static void main(String[] args)
{
Human aPerson = new Human();
Class c1 = aPerson.getClass();
System.out.println(c1.getName());
Human anotherPerson = new Woman();
Class c2 = anotherPerson.getClass();
System.out.println(c2.getName());
}
}
class Human
{
/**
* accessor
*/
public int getHeight()
{
return this.height;
}
/**
* mutator
*/
public void growHeight(int h)
{
this.height = this.height + h;
}
private int height;
}
class Woman extends Human
{
/**
* new method
*/
public Human giveBirth()
{
System.out.println("Give birth");
return (new Human());
}
}
当我们调用对象的getClass()方法时,就得到对应Class对象的引用。
在c2中,即使我们将Women对象的引用向上转换为Human对象的引用,对象所指向的Class类对象依然是Woman。
Java中每个对象都有相应的Class类对象,因此,我们随时能通过Class对象知道某个对象“真正”所属的类。无论我们对引用进行怎样的类型转换,对象本身所对应的Class对象都是同一个。当我们通过某个引用调用方法时,Java总能找到正确的Class类中所定义的方法,并执行该Class类中的代码。由于Class对象的存在,Java不会因为类型的向上转换而迷失。这就是多态的原理。
当Java创建某个类的对象,比如Human类对象时,Java会检查内存中是否有相应的Class对象。
如果内存中没有相应的Class对象,那么Java会在.class文件中寻找Human类的定义,并加载Human类的Class对象。
在Class对象加载成功后,其他Human对象的创建和相关操作都将参照该Class对象。
反射:运行时的类信息
反射就是把Java的各种成分映射成相应的Java类。
Class类的构造方法是private,由JVM创建。
反射是java语言的一个特性,它允程序在运行时(注意不是编译的时候)来进行自我检查并且对内部的成员进行操作。例如它允许一个java的类获取他所有的成员变量和方法并且显示出来。Java 的这一能力在实际应用中也许用得不是很多,但是在其它的程序设计语言中根本就不存在这一特性。
JavaBean 是 reflection 的实际应用之一,它能让一些工具可视化的操作软件组件。这些工具通过 reflection 动态的载入并取得 Java 组件(类) 的属性。
Java反射机制提供如下功能:
在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判段任意一个类所具有的成员变量和方法
在运行时调用任一个对象的方法
在运行时创建新类对象
在使用Java的反射功能时,基本首先都要获取类的Class对象,再通过Class对象获取其他的对象。
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。获取类的Class对象有多种方式:
Person p1 = new Person();
//下面的这三种方式都可以得到字节码
CLass c1 = Date.class();
p1.getClass();
//若存在则加载,否则新建,往往使用第三种,类的名字在写源程序时不需要知道,到运行时再传递过来
Class.forName("java.lang.String");
RTTI与反射的区别
不知道你注意到了没有,以上使用的RTTI都具有一个共同的限制:在编译时,编译器必须知道所有要通过RTTI来处理的类。
但有的时候,你获取了一个对象引用,然而其对应的类并不在你的程序空间中,怎么办?
对RTTI来说,编译器在编译时打开和检查.class文件,而对于反射机制,.class文件在编译时是不可获取的,所以是在运行时打开和检查.class文件的。