Java让我们在运行时识别对象和类的信息,主要有2种方式:
一种是传统的RTTI,它假定我们在编译时已经知道了所有的类型信息;
另一种是反射机制,它允许我们在运行时发现和使用类的信息。
## class对象
理解RTTI在Java中的工作原理,首先需要知道类型信息在运行时是如何表示的,这是由Class对象来完成的,它包含了与类有关的信息。Class对象就是用来创建所有“常规”对象的,Java使用Class对象来执行RTTI,即使你正在执行的是类似类型转换这样的操作。
每个类都会产生一个对应的Class对象,也就是保存在.class文件。所有类都是在对其第一次使用时,动态加载到JVM的,当程序创建一个对类的静态成员的引用时,就会加载这个类。Class对象仅在需要的时候才会加载,static初始化是在类加载时进行的。
此处不做过多赘述。
##反射机制
类加载器首先会检查这个类的Class对象是否已被加载过,如果尚未加载,默认的类加载器就会根据类名查找对应的.class文件。
想在运行时使用类型信息,必须获取对象(比如类Base对象)的Class对象的引用,使用功能Class.forName(“Base”)可以实现该目的,或者使用base.class。
注意,使用功能”.class”来创建Class对象的引用时,不会自动初始化该Class对象,使用forName()会自动初始化该Class对象。
使用类而做的准备工作一般有以下3个步骤:
- 加载:由类加载器完成,找到对应的字节码,创建一个Class对象
- 链接:验证类中的字节码,为静态域分配空间
- 初始化:如果该类有超类,则对其初始化,执行静态初始化器和静态初始化块
public class Base { static int num = 1; static { System.out.println("Base " + num); } } public class Main { public static void main(String[] args) { // 不会初始化静态块 Class clazz1 = Base.class; System.out.println("------"); // 会初始化 Class clazz2 = Class.forName("zzz.Base"); } }
##
类型转换前先做检查
编译器将检查类型向下转型是否合法,如果不合法将抛出异常。向下转换类型前,可以使用instanceof判断。
class Base { } class Derived extends Base { } public class Main { public static void main(String[] args) { Base base = new Derived(); if (base instanceof Derived) { // 这里可以向下转换了 System.out.println("ok"); } else { System.out.println("not ok"); } } }