浅析Java RTTI 和 反射的概念
一、概念
RTTI(Run-Time Type Identification,运行时类型识别)的含义就是在运行时识别一个对象的类型,其对应的类是Class对象,怎么理解这个Class对象呢?如果说类是所有对象方法、属性的集合,那就可以把这个Class对象理解成是所有class的集合,然后利用这个Class对象动态的解析出相关类,并可以获得其构造器和方法等,甚至实例化这个类的对象。开始文章前,先提重要的一点:无论是RTTI还是反射,其本质都是一样的,都是去动态的获取类的信息,他们唯一的区别仅是:
- RTTI 在编译期知道要解析的类型。
- 反射 在运行期知道要解析的类型。
二、RTTI
有以下两种方式可以获取到Class对象:
public static void main(String[] args) { try { //第一种方式 Class rtti = Class.forName("com.jomoo.test.rtti.RTTI"); //第二种方式 Class type=RTTI.class; } catch (ClassNotFoundException e) { e.printStackTrace(); } }
可以看到,采用RTTI的方式必须在写程序的时候就知道了类的名字,才能获取到Class对象对这个类的引用,并利用这个引用,得到大量关于这个类的信息,包括接口,父类,方法,静态成员,甚至是像newInstance()方法这样的一个实现“虚拟构造器”的一种方式。
RTTI r =(RTTI)rtti.newInstance();//newInstance的类必须要有一个缺省构造器
另外需要提一个经常用到 instanceof 该关键字的调用其实就是使用了Class对象,并且返回一个布尔值。
Object o = rtti.newInstance(); if (o instanceof RTTI){ System.out.println(true);//这里需要注意的是,如果 o 是 RTTI的子类的话,返回的也会true; }
三、反射
Java中有时候在编译器为程序生成代码很久之后才会出现要处理的那个类,那么这个时候怎么才能处理这个类呢,即在编译的时候根本无法获知这个对象所属的类。答案就是利用Java的反射机制。废话不多说,看完下面这个代码你就很清楚的明白 RTTI 和 反射的区别在哪里了。
public class RTTI { private static final String usage="usage"; private static Pattern pattern=Pattern.compile("\\w+\\."); public static void main(String[] args) { if (args.length<1){ System.out.println(usage); System.exit(0); } int lines=0; try { Class c = Class.forName(args[0]);//看这里类的名字在编译的时候是无法得知的,只有在运行的时候动态传进去 Method[] method = c.getMethods(); Constructor[] constructors = c.getConstructors(); if (args.length==1){ for (int i=0;i<method.length;i++){ System.out.println(pattern.matcher(method[i].toString()).replaceAll("")); } for (int i=0;i<constructors.length;i++){ System.out.println(pattern.matcher(constructors[i].toString()).replaceAll("")); } lines=method.length+constructors.length; }else { for (int i=0;i<method.length;i++){ if (method[i].toString().indexOf(args[1])!=-1){ System.out.println(pattern.matcher(method[i].toString()).replaceAll("")); } lines++; } for (int i=0;i<constructors.length;i++){ if (constructors[i].toString().indexOf(args[1])!=-1){ System.out.println(pattern.matcher(constructors[i].toString()).replaceAll("")); } lines++; } } } catch (ClassNotFoundException e) { System.out.println("NO!!!!"); } } }
如何在main方法中带入参数可以参考这篇博客,我用原生的 javac -encoding UTF-8 RTTI.java 和 java RTTI com.jomoo.test.seven.ActionCharacter来运行 竟然报错了:Error: Could not find or load main class RTTI,我的JDK有毒!无奈借助了强大的IDE工具:
四、总结
其实 RTTI 的概念只是在《Thinking in Java》中提到的,才引来这么多人的讨论,原生的Java中并没有这个概念的说法。所以勒,我们根本不必纠结是 RTTI 还是反射,他们无论用法还是本质都是一样的,都是为了实现一样的目的——动态的获取类的信息,我们应该把重点放在使用上,而不要过多在纠结在差异上。