@author ixenos
关键字:RTTI、动态绑定、动态加载、获得Class引用、泛型Class引用、newInstance的坑、JVM中的泛型类型信息
RTTI和动态绑定
RTTI即运行时类型识别 Run-Time Type Identification 或 Run-Time Type Information
例如,当把Shape对象放入List<Shape>的数组时会向上转型,但在向上转型为Shape的时候也会丢失Shape对象的具体类型,对于数组而言,他们只是Shape对象。从List<Shape>数组中取出元素时,这种容器(实际上它将所有元素当作Object持有)会自动将结果转型回Shape,这是RTTI最基本的使用形式,因为在Java中所有的类型转换都是在运行时进行正确性检查的,而这也是RTTI名字的含义:在运行时,识别一个对象的类型。
此例中RTTI类型转换并不彻底,这是因为目前我们只知道这个List<Shape>保存的都是Shape,这在编译时由容器和泛型系统来保证,在运行时由类型转换操作来保证
而类型转换后就是之前写的多态(动态绑定)的事情了,因为这时候需要调用对象的方法,动态绑定确定域和方法的调用
总结:RTTI在运行时识别一个对象的类型,之后多态在运行时判断对象的实际类型来确定调用
Class对象和动态加载
要理解RTTI在Java中的工作原理,必须先知道类型信息在运行时是如何表示的,而这项工作由Class对象完成
Class对象就是用来创建类的所有的常规对象的,每个类都有一个Class对象,每当编译了一个新类就会产生一个Class对象(被保存在一个.class文件中),如下图所示:
1.类加载器子系统:
为了生成类的对象,JVM使用类加载器子系统(Classloader)来加载.class文件(内容是字节码)
类加载器链:该子系统通常可以包含一条类加载器链,但只有一个原生类加载器,它(指子系统)是JVM实现的一部分
原生类加载器:原生类加载器加载的是所谓的可信类,他们通常从本地盘加载的(包括Java API类)
额外的类加载器:在这条链中通常不需要添加额外的类加载器,但如果由特殊要求(以某种特殊的方式加载类,以支持Web服务器应用,或者在网络中下载类)
2.动态加载:
所有类(XXX.class)都是在对其第一次使用时,动态加载到JVM中的。当程序创建第一个对类的静态对象成员的引用时,就会加载这个类(比如调用静态方法、静态域)
1 Class Candy { 2 static{ System.out.println("Loading Candy"); } 3 } 4 5 Class Gum { 6 static{ System.out.println("Loading Gum"); } 7 } 8 9 Class Cookie{ 10 static{ System.out.println("Loading Cookie"); } 11 } 12 13 Public class SweetShop { 14 public static void main(String[] args){ 15 System.out.println("main"); 16 new Candy(); 17 System.out.println("After creating Candy"); 18 try{ 19 Class.forName("Gum"); 20 }catch(ClassNotFoundException e){ 21 System.out.println("Gum not founded"); 22 } 23 System.out.println("After creating Gum"); 24 new Cookie(); 25 System.out.println("After creating Cookie"); 26 } 27 } 28 29 ---------------------------------- 30 输出: 31 main 32 Loading Candy 33 After creating Candy 34 Loading Gum 35 After creating Gum 36 Loading Cookie 37 After creating Cookie