您的位置:首页 > 编程语言 > Java开发

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

2017-08-31 22:14 603 查看
运行时类型信息使得你可以在程序运行时发现和使用类型信息

如何让我们在运行时识别对象和类的信息的?

主要有两种方式:一种是“传统的”RTTI (运行时类型识别),它假定我们在编译时已经知道了所有的类型;另一种是反射机制,它允许我们在运行时发现和使用类的信息。

1 为什么需要RTTI?

对于一个类层次结构,基类是Shape,而派生出的具体类有Circle、Square和Triangle。

面向对象编程中基本的目的是:让代码只操纵对基类的引用。

List<Shape> shapeList = Arrays.asList(
new Circle(), new Square(),new Triangle()
);
for(Shape shape : shapeList)
shape.draw();  // Shape对象实际上执行什么样的代码,是由引用所指向的具体对象Circle、Square或者Triangle决定的——多态机制。


当把Shape对象放入List< Shape >时,会向上转型。但在向上转型为Shape的时候也丢失了Shape对象的具体类型。对于容器而言,它们只是Shape类的对象。

当从容器中取出元素时,实际上它将所有的事物都当做 Object 持有——会自动将结果转型为Shape。这是RTTI最基本的使用形式,因为在Java中所有的类型转换都是在运行时进行正确性的检查的。这也是RTTI名字的含义:在运行时,识别一个对象的类型。

使用RTTI,可以查询某个Shape引用所指向的对象的确切类型,然后选择或剔除特例。

2 Class对象

要理解RTTI在Java中的工作原理,首先必须知道类型信息在运行时是如何表示的。这项工作是由称为Class对象的特殊对象完成的,它包含了与类有关的信息。

类是程序的一部分,每个类都有一个Class对象。换而言之,每当编写并且编译了一个新类,就会产生一个Class对象(更恰当地说,是被保存在一个同名的.class文件中)。为了生成这个类的对象,运行这个程序的JVM将使用被称为“类加载器”的子系统。

所有的类都是在对其第一次使用时,动态加载到JVM中的。当程序创建第一个对类的静态成员的引用时,就会加载这个类(这个证明构造器也是类的静态方法)。因此,Java程序在它开始运行之前并非被完全加载,其各个部分是在必需时才加载的。类加载器首先检查这个类的Class对象是否已经加载。如果尚未加载,默认的类加载器就会根据类名查找.class文件。这个类的字节码被加载时,它们会接受验证,以确保其没有被破坏,并且不包含不良的Java代码。

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

Class.forName(类的全限定名);


无论何时,只要你想在运行时使用类型信息,就必须首先获得对恰当的Class对象的引用。

Class.forName() 就是实现此功能的便捷途径,因为你不需要为了获得Class引用而持有该类型的对象。但是,如果你已经拥有了一个感兴趣的类型的对象,那就可以通过调用getClass()方法来获取Class引用了,这个方法属于根类Object的一部分,它将返回表示该对象的实际类型的Class引用。

Class的newInstance()方法是实现“虚拟构造器”的一种途径,虚拟构造器允许你声明:“我不知道你的确切类型,但无论如何要正确地创建你自己。”使用newInstance()来创建的类,必须带有
4000
默认的构造器。

类字面常量

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

类名.class;


这样做不仅简单,而且更安全,因为它在编译时就会受到检查(因此不需要置于try语句块中)。类字面常量不仅可以应用于普通的类,也可应用于接口、数组以及基本数据类型。

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

1. 加载,这是由类加载器执行的。查找字节码,并从这些字节码中创建一个Class对象。

2. 链接 ,验证类中的字节码,为静态域分配存储空间,并且如果必需的话,将解析这个类创建 的对其它类的所有引用。

3. 初始化 ,如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块。

初始化被延迟到了对静态方法或者非常数静态域进行首次引用时才执行。

类型转换前先做检查

我们已知的RTTI形式包括:

1. 传统的类型转换,如”(Shape)“,由RTTI确保类型转换的正确性,如果执行了一个错误的类型转换,就会抛出一个ClassCastException异常。

2. 代表对象的类型的Class对象。通过查询Class对象可以获取运行时所需的信息。

3. 关键字instanceof,它返回一个布尔值,已判定对象是不是某个特定类型的实例。

x instanceof 类名 //检查对象x是否从属于Dog类,只可将其与命名类型进行比较,而不能与Class对象作比较。


动态的instanceof :Class.isInstance方法提供了一种动态地测试对象的途径。

类名.class.isInstance(x) //你是这个类或者你是这个类的派生类吗?


在编译时,编译器必须要知道所有要通过RTTI来处理的类;RTTI允许通过匿名基类的引用来发现类型信息

面向对象编程语言的目的是让我们在凡是可以使用的地方都使用多态机制,只在必须的时候使用RTTI。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: