文章目录
概述1.为什需要RTTI2.Class对象类字面常量(类.class)泛化的Class引用新的转型语言
3.类型转换前先做检查RTTI形式包括isInstance()和instaceofisAssignableFrom()
4.注册工厂5.instanceof和Class的等价性6.反射:运行时的类信息java.lang.reflect包含:Field、Method、以及Constructor类RTTI和反射的区别
7.动态代理Proxy类就是用来创建一个代理对象的类例题:
8.空对象模拟对象与桩
9.接口与类型信息
概述
Java在运行时识别类和对象的信息
RTTI :假定我们在 编译时 已经知道了所有的类型反射 :允许我们在 运行时 发现和使用类的信息
1.为什需要RTTI
RTTI(run-time type information):在运行时,识别一个对象的类型使用RTTI可以查询某个基类应用所指向的对象的确切类型,然后选择或剔除特例
2.Class对象
类型信息在运行是由Class对象完成
Class对象用来创建类的所有“常规”对象
每当编写并编译了一个新类就会产生一个Class对象
为了生成这个类的对象,运行JVM将使用“类加载器”的子系统
使用new方法创建类的新对象也会被当作类的静态成员引用
Class对象仅是在必须才加载,static初始化实在类加载时进行的
Class对象加载
Class.forName() : 取得Class对象的引用(使用全限定名)
class.getName() : 产生全限定名
class.getSimpleName() : 不含 包名 的类名
class.getCanonicalName() : 全限定的类名
class.isInterface() : 该class对象是不是接口
class.getInterfaces() : 该class对象所包含的接口
class.getSuperclass() : 查询直接基类
class.newInstance() : 虚拟构造器,只能调用无参的构造函数
类字面常量(类.class)
(1) 包装器类,标准字段TYPE(例:char.class – Character.TYPE)(2) 获取Class类引用的方式
i.Class.forName 引起类加载ii.XXXX.class 类字面常量,不引起类加载 (3) 使用类的三个步骤
i.加载:查找字节码,创建一个Class对象ii.链接:分配存储空间iii.初始化:先初始化超类,执行静态初始化器和静态初始化块
泛化的Class引用
(1) 如果需要Class类型变得更具体些,就通过允许Class引用所指向Class对象的类型进行限定实现。这里用了泛型。如: Class intClass = int.class;(2) 如果不确定限定类型,则使用Class<?> 优与 Class(3) 如果要扩大限定类型范围(提供编译器类型检查),如:Class<? extends Number> numClass = int.class(4) 使用泛化Class引用 创建的对象(newInstance()),得到的不再是Object,而是具体的限定类型,如: Integer intObj = intClass.newInstance()
新的转型语言
(1) Class.cast() : 使用Class引用做类型转换
如:
Number numObj
= new Integer(1);
Class
<Integer> intClass
= Integer
.class;
Integer intObj
= intClass
.cast(numObj
);
(2) Class.asSubclass() : 将一个类对象转型为更加具体的类型
动态的instanceof
使用instaceof判断对象是否属于某个类、子类、接口实现,也可以使用Class引用的isInstance()方法【String.class.isInstance(new Test())】Class.isInstance不会出现instanceof中编译报错的情况。
3.类型转换前先做检查
RTTI形式包括
(1) 传统的类型转换(向上、向下),RTTI保证类型正确性,错误则抛出ClassCastException(2) 传统的类型转换(向上、向下),RTTI保证类型正确性,错误则抛出ClassCastException。
i.Class c = Class.forName(“RTTITest”);ii.Object o = c.newInstance(); (3) 类型检查(instanceof)
boolean result
= obj
instanceof Class
a、obj 必须是引用类型,基本类型编译报错。
b、obj 为null,返回
false,因此使用
instanceof时不用先判断不为空。
c、obj 为Class的实例对象,子类对象,或接口实现对象时,返回
true。
d、obj的声明类 和 Class 必须有父子关系(存在转换的可能),否则编译报错。如:
"abc" instanceof Integer 编译报错
isInstance()和instaceof
使用instaceof判断对象是否属于某个类、子类、接口实现,也可以使用Class引用的isInstance()方法。
String
.class.isInstance(new Test())
Class
.isInstance不会出现
instanceof中编译报错的情况。
isAssignableFrom()
使用aClass.isAssignableFrom(bClass) 可以判断aClass引用是否为bClass引用的超类
如:
Exception
.class.isAssignableFrom(IOException
.class) 结果为
true
4.注册工厂
5.instanceof和Class的等价性
“instanceof” :保持了类型的概念。“你是这个类吗,你是这个类的派生类吗?”“==” :比较实际的Class对象,等号左侧是不是右侧这个确切的类型
6.反射:运行时的类信息
RTTI限制:这个类型在编译时必须已知
反射机制:用来检查可用的方法,并返回方法名
java.lang.reflect包含:Field、Method、以及Constructor类
(1) 使用Constructor:创建新对象(2) 使用get()和set()方法读取和修改与Field对象关联的字段(3) 使用invoke()方法调用与Method对象关联的方法(4) 也可以调用getFields()、getMethod()和getConstructors()以返回表示字段、方法及构造器的对象的数组
RTTI和反射的区别
(1) RTTI : 编译器在 “编译时” 打开和检查.class文件(2) 反射 : 编译器在 “运行时” 打开和检查.class文件
7.动态代理
它可以动态的创建代理并动态的处理对所有代理的方法的调用在动态代理上所做的所有调用都会被重定向到单一的调用处理器上每一个动态代理类的调用处理程序都必须实现InvocationHandler接口,并且每个代理类的实例都关联到了实现该接口的动态代理类调用处理程序中,当我们通过动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现InvocationHandler接口类的invoke方法来调用
public Object
invoke(Object proxy
, Method method
, Object
[] args
)throws Throwable
;
(1) proxy
:代理类代理的真实代理对象
(2) method
:我们所要调用某个对象真实的方法的Method对象
(3) args
:指代代理对象方法传递的参数
Proxy类就是用来创建一个代理对象的类
Proxy
.newProxyInstance(ClassLoader loader
, Class
<?>[] interfaces
, InvocationHandler h
)
(1) loader:一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载
(2) interfaces:一个
interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口
(如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。
)
(3) h:一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。
在Spring中的两大核心IOC和AOP中的AOP(面向切面编程)的思想就是动态代理,在代理类的前面和后面加上不同的切面组成面向切面编程。
例题:
package Chapter14
.Test22
;
import java
.lang
.reflect
.InvocationHandler
;
import java
.lang
.reflect
.Method
;
public class DynamicProxyHandler implements InvocationHandler {
private Object proxied
;
public DynamicProxyHandler(Object proxied
) {
this.proxied
= proxied
;
}
@Override
public Object
invoke(Object proxy
, Method method
, Object
[] args
) throws Throwable
{
System
.out
.println("**** proxy: " + proxy
.getClass() +
", method: " + method
+ ", args: " + args
);
if (args
!= null
)
for (Object arg
: args
)
System
.out
.println(" " + arg
);
System
.out
.println("before invoke。。。");
Object invoke
= method
.invoke(proxied
, args
);
System
.out
.println("after invoke。。。");
return invoke
;
}
}
------------------------------------------------------------------------------------------------------------------
package Chapter14
.Test22
;
import Chapter14
.Test21
.Interface
;
import Chapter14
.Test21
.RealObject
;
import Chapter14
.Test21
.SimpleProxy
;
import java
.lang
.reflect
.Proxy
;
public class Test22 {
public static void consumer(Interface
iface) {
iface
.doSomething();
iface
.somethingElse("bonobo");
}
public static void main(String
[] args
) {
RealObject real
= new RealObject();
DynamicProxyHandler handler
= new DynamicProxyHandler(real
);
SimpleProxy sim
=new SimpleProxy(real
);
consumer(sim
);
Interface
proxy = (Interface
) Proxy
.newProxyInstance(Interface
.class.getClassLoader(), new Class[]{Interface
.class}, new DynamicProxyHandler(sim
));
Interface
proxy2 = (Interface
) Proxy
.newProxyInstance(Interface
.class.getClassLoader(), real
.getClass().getInterfaces(), handler
);
consumer(proxy
);
System
.out
.println("---------------------------------");
proxy2
.doSomething();
proxy2
.somethingElse("Proxy2");
System
.out
.println("---------------------------------");
}
}
8.空对象
空对象可以接受传递给它的所代表的对象的消息,但是返回表示为实际上并不存在任何“真实”对象的值
空对象更靠近数据,因为对象表示的时问题空间的实体
创建一个名为Null的标识接口,然后针对不同的业务类创建不同的空对象
模拟对象与桩
(1) 模拟对象:是轻量级的,自检测的(2) 桩:是重量级的,因为要返回桩数据,所以有很多复杂的操作(3) 相比之下,空对象的目的明确的多,也巧妙、智能得多
9.接口与类型信息
接口并不能保护客户端不调用实现类中除public以外的其他修饰符的方法 哪怕是private的方法或字段,都可以使用Method.setAccessible(true)之后访问。利用反射可以无视包访问权限、私有内部类、匿名类,调用其中接口外的方法