Type information 反射信息 Type指一个对象的种类,某种自定义的class,某个interface或string等,都是type的一种。
(本文参考了 Thinking in java中的type information这章)
- 什么是Type information
Type指一个对象的种类,某种自定义的class,某个interface或string等,都是type的一种。
而type information即是指关于这个对象的信息。例如后面会提到,Class.forName()将返回class的名称,就是所谓的type information之一。
在两个阶段可以得知一个对象的类型:
1, 在编译阶段就已经可以得到所有的type。 例如,定义的一个String对象,在编译阶段就可得知该对象是一个String。
2, 另外一种反射机制,它允许你在运行时发现和使用对象的类型信息,即type information.
而RTTI(runtime type information)就指在运行时,获得type information的机制。
大家熟知的多态,就是RTTI的一种典型情况。而在后面的内容中我们慢慢的就能知道,可以利用runtime type information做些什么有趣的事情。
现阶段我们只需要知道利用RTTI我们可以build more powerful program(现在听起来很虚是吧~~)
- RTTI的意义:
一个简单的例子,一个基类 Shape和它的三个子类:三角形,圆形和方形。
public class Shapes {
public static void main(String[] args) {
List<Shape> shapeList = Arrays.asList(
new Circle(), new Square(), new Triangle()
);
for(Shape shape : shapeList)
shape.draw();
}
}
从上例可以看出,这些子类的实例被放入了List<Shape>中。此时the upcast occurs when the shape is placed into the List<Shape>. During the upcast to Shape, the fact that the objects are specific types of Shape is lost. To the array, they are just Shapes.
当你从List<Shape>中将每个形状取出并draw()时,就使用了典型的RTTI。因为所有的对象从List中取出时,这些对象被自动从基类shape转换成了具体的形状,并执行特定形状的draw()。而这正是RTTI的意义所在:在运行时,一个对象的type被确认。这是实现多态的基础。
- type information 在runtime是如何被表示的。
要理解RTTI在java中是如何工作的,你必须先知道type information在run time是如何被表示的。
我们知道一个类有两种成员变量:用static修饰的变量,被称为类变量或静态变量。静态变量是属于类本身的。没有被static修饰的变量是实例变量,是属于一个具体的类实例的。
因此我们可以得出这样一个结论:一个类在JVM中应该被表现成两部分:类本身,以及类的实例。
事实也是如此:在JVM中,一个类有两个部分:
1,表现这个类本身的对象Class。
2,以及这个类的诸多实例对象。
在java中,有一个特殊的类,叫Class类。在一个类第一次被使用时(我们在后面解释什么叫“被使用时”),JVM中产生了两个东西:首先创建一个代表类本身的对象,这个对象是Class类的实例。接着创建类的实例。
而这个Class类的实例,就存储了这个类的type infomation。
注意在JVM中,一个类只会有一个Class类的实例与之对应,但是可以有任意多个类的实例。也就是说,你可以实例化一个类任意多次,但是在JVM中对应的Class类的实例只会被创建一个。
再换句话说:一个类的任意多个实例是共享一个Class类实例的。这非常合理,因为一个类的实例再多,但是属于类本身的信息有一份就够了。
1,Class对象是怎么产生的
That is, each time you write and compile a new class, a single Class object is also created (and stored, appropriately enough, in an identically named .class file).
.class文件(即jar文件)在运行时,会被JVM中的一个重要组件:class loader装载并创建出对应的Class object。
2,Class object如何工作
所有的类都是在第一次被调用时,动态的被load到JVM的. 所谓的“第一次被调用”,就是类中的静态成员被调用。而构造函数也是类的静态方法,因此load在以下情况发生:
1,类的static成员变量或方法被调用时。
2,或new一个类时。
因此,一个java程序并不是在运行前被完全load到JVM中,而且分多次,一小段一小段的load。这导致了和c++这种statically loaded 语言在行为上的不同。
Once the Class object for that type is in memory, it is used to create all objects of that type。
上句意思是:一旦一个类的Class object在内存中了,它就会被用来创建所有该类的对象。非常合理,因为如果一个类对应的Class对象已经在JVM存在了,且我们仅需要一份,那么就不需要再创建了。
3,如何使用Class object。
要使用type information,我们必须先得到一个Class类实例的引用,然后使用这个类提供的方法,得到我们想要的information:
Anytime you want to use type information at run time, you must first get a reference to the appropriate Class object.
Class.forName()可以返回一个类对应的Class对象:Returns the
Class
object associated with the class or interface with the given string name.
例如,class.forName("TestClass"); 就返回了TestClass这个类对应的那个Class类实例,也就是代表了TestClass这个类的type information的那个对象。
还有一种方法就是使用Class literals:
类名+.class就是class literral。
我常常在代码中看到的,log4j就会使用:
private final static Logger LOGGER = Logger.getLogger(ClassName.class);
但是使用.class并不会导致立即实例化对应的class。
得到了引用后,我们想获得任何关于类的type information就非常简单了:其实就是使用Class这个类提供的方法。
打开java.lang.Class的java doc,浏览一遍这个类提供的所有方法,心里就有数了。下面以最常用的几个方法举例。
getName()得到当前的这个类的名字。
其他有用的方法:
getClass():继承于Object的Returns the runtime class of an object. 其实返回的就是一个Class的实例。
Class.islnstance( ): Determines if the specified Object
is assignment-compatible with the object represented by thisClass
.
instanceof。
getClassLoader()可以知道这个类是被哪个class loader load到JVM的。