第14章 类型信息

来源:互联网 发布:淘宝号怎么实名认证app 编辑:程序博客网 时间:2024/05/21 08:58

14.1 为什么需要RTTI

数组此前把所有的对象当做Object持有,当从数组中取出元素的时候会自动将Object强转成Shape(自定义基类),在JAVA中,所有的类型转换都是在运行时进行正确性检查的,这也是RTTI名字的含义:在运行时识别一个对象的类型
在编译时期由容器和JAVA泛型系统来强制确保这一点,而在运行时则由类型转换操作来确保这一点;
但是,如果想使用某个具体的子类来完成独一无二的操作时,使用RTTI则非他莫属;

14.2 Class对象

类加载器子系统实际上可以包含一条类加载器链,但是只有一个原生类加载器,它是JVM实现的原生类加载器加载的是所谓的可信类,包括JAVA API类,它们通常是从本地加载的,在这条链中,通常不需要添加额外的类加载器,但是
如果你有特殊需求,例如:从某种特殊方式来加载类或从网络中下载类,那么你必须有一种方式可以挂接额外的类加载器;
所有的类都是在对其第一次使用时动态加载到JVM中的,当程序创建第一个对类的静态成员引用时就会加载这个类,构造器也是类的静态方法即使在构造器之前并没有使用static关键字,因此使用new操作符创建类的新对象也会被当做
对类的静态成员的引用;因此,JAVA程序在它开始运行之前并非被完全加载,其各个部分是在必须时才加载的;
类加载器首先检查这个类的Class对象是否已经加载,如果未加载则默认的类加载器就会根据类名去查找.class文件(有可能会在数据库中查找字节码),在这个类的字节码被加载时,它们会接受验证从而确保其没有被破坏并且不包含
不良JAVA代码,一旦某个类的Class对象被载入内存,它就被用来创建这个类的所有对象;
Class对象仅仅在需要的时候才被加载且static初始化时在类加载时进行的;

14.2.1 类字面常量

JAVA还提供了另一种方法来生成对Class对象的引用即使用类字面常量,例如:Test.class,对于包装类还有一个标准字段TYPE,例如:Boolean.TYPE/Byte.TYPE/Float.TYPE/Void.TYPE
注意,在使用.class来创建对Class对象的引用时,不会自动初始化该Class对象,
Class.forName("Test");则会加载class文件并进行初始化
Test.class仅仅加载class文件并分配空间,并未进行初始化
Class对象初始化包括3个阶段:
1、加载,这里指的是class文件;
2、链接,验证class文件中的字节码,为静态域分配存储空间,如果必须的话则解析这个类创建的对其他类的所有引用
3、初始化,执行静态初始化构造器和静态初始化代码块,而这一阶段只有在调用静态方法、构造器、静态变量、静态方法进行首次引用时才执行初始化;

14.2.2 泛化的Class引用

Class引用总是指向某个Class对象,它可以制造类的实例并包含可作用于这些实例的所有方法代码,同样还包含该类的静态成员,因此,Class引用表示的就是它所指向的对象的确切类型而该对象便是Class类的一个对象;
这里指的是泛型,例如:
Class myInt = int.class;
myInt = double.class;
这样编写,编译不会报错,为了强制检查改成这样
Class<Integer> myInt = int.class;
但是,如果这样编写的话,可能太固定,可以使用通配符
Class<?> myInt = int.class;
Class<?>好处就是你告诉系统,你并非碰巧或疏忽使用了一个非具体的类引用,我非常认证的选择了非具体的版本;
但这里你还想限定为某种类型或该类型的任何子类型的话,需要将通配符与extends结合来创建一个范围,例如:
Class<? extends Number> myInt = int.class;
这里一句话总结:向Class引用添加泛型语法的原因仅仅是为了提供编译期类型检查,因此如果操作有误,则可以在编译期就立即发现这一点,防止在运行时才发现错误;
当你将泛型用于Class对象后,newInstance()将返回该对象的确切类型而不仅仅只是基本的Object对象;
getSuperClass()方法返回的是基类(即便是接口),因此,用通配符表示就是:Class<? super 子类> parent = 子类.class.getSuperClass();

14.3 类型转换前先做检查

使用RTTI地方有3个:强制类型转换、代表对象类型的Class对象、instanceof

14.7 动态代理

代理是基本的设计模式之一,它是你为了提供额外的或不同的操作而插入的用来代替实际对象的对象
JAVA的动态代理比代理的思想更向前迈进一步,因为它可以动态创建代理并动态处理对被代理方法的调用;
InvocationHandler接口,接口方法invoke(参数1,参数2,参数3)
参数1:代理对象
参数2:方法对象
参数3:方法参数对象
Proxy的newProxyInstance(参数1,参数2,参数3)
参数1:一个类加载器,通常可以从已经被加载的对象中获取其类加载并传入该方法
参数2:一个该代理需要实现的接口列表(必须是接口,而不是类或抽象类)
参数3:InvocationHandler接口实现类
0 0
原创粉丝点击