反射:运行时的类信息
如果不知道某个对象的确切类型,RTTI可以告诉你,但是前提是在编译时必须已知,这样才能使用RTTI识别它,并利用这些信息做一些有用的事情。也就是在编译时,编译器必须知道所有要通过RTTI来处理的类。
考虑这种情况:假设你获取了一个指向某个并不在你程序空间中的对象的引用,这个时候,在编译时,程序就无法获取到这个对象所属的类。
这个时候反射就登场啦。反射提供了一种机制,用来检查可用的方法,并返回方法名。另外,在这种情况下我们也会需要在运行时获取类的信息:希望提供在跨网络的远程平台上创建和运行对象的能力。
反射机制的支持类:Class类与java.lang.reflect类库一起对反射的概念进行了支持,这个类库包含了Field,Method以及Constructor类(每个类都实现了Member接口)。这些类都是JVM运行时创建的,用于表示未知类里对应的成员。
使用Constructor创建新对象,用get()和set()方法读取和修改与Field对象关联的字段,用invoke()方法调用与Method对象关联的方法。另外,还可以调用getFields(),getMethods()和getConstructors()等方法,返回表示字段,方法以及构造器的对象数组。
这里推荐几篇反射的文章:
RTTI与反射的区别:
RTTI:编译器在编译时打开和检查.class文件,也就是说我们可以用普通的方式调用对象的所有方法。
反射机制:.class文件在编译时是不可获取的,所以是在运行时打开和检查.class文件的。
反射机制的更多用途:通常你是不需要使用反射的,但是它们在你需要创建更加动态的代码时会很有用。反射机制用来支持其他的特性如对象序列化,JavaBean。
动态代理:
代理是基本的设计模式之一。关于代理模式,这里有一篇文章:
设计模式笔记 – Proxy 代理模式 (Design Pattern)
Java的动态代理比代理的思想更向前迈进了一步,因为它可以动态的创建代理并动态的处理对所代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器上,它的工作是揭示调用的类型并确定相应的策略。
下面来看一个Java中实现动态代理的例子:
首先创建抽象角色:
public interface Subject {
void doSomething();
void somethingElse(String arg);
}
接下来创建具体角色:
public class RealSubject implements Subject{
@Override
public void doSomething() {
System.out.println("doSomething");
}
@Override
public void somethingElse(String arg) {
System.out.println("somethingElse: " + arg);
}
}
接下来是创建一个InvocationHandler接口的实现:
public class DynamicProxyHandler implements InvocationHandler{
private Object proxied;
public DynamicProxyHandler(Object proxied){
this.proxied = proxied;
}
/**
* invoke()方法中传递进来了代理对象,以防你需要区分请求的来源
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("*** proxy ***" + proxy.getClass());
System.out.println("*** method ***" + method);
System.out.println("*** args ***" + args);
// 可以在这里做一些处理,如查看方法名,查看方法签名的其他方面,甚至可以搜索特定的参数值
if(method.getName().equals("interesting"))
System.out.println("Proxy detected the interesting method.");
// 将请求转发给被代理对象,并传入必须的参数
return method.invoke(proxied, args);
}
}
最后在main方法中创建动态代理对象,并通过动态代理调用真实角色的方法:
public class SimpleDynamicProxy {
public static void consumer(Subject subject){
subject.doSomething();
subject.somethingElse("haha~~");
}
public static void main(String[] args){
// 创建真实的对象
RealSubject real = new RealSubject();
consumer(real);
// 通过调用该方法创建动态代理
Subject proxy = (Subject)Proxy.newProxyInstance(
Subject.class.getClassLoader(), // 类加载器
new Class[]{Subject.class}, // 你希望该代理实现的接口列表
new DynamicProxyHandler(real)); // InvocationHandler接口的一个实现
consumer(proxy);
}
}
第一个consumer(real)输出为:
doSomething
somethingElse: haha~~
第二个consumer(proxy)输出为:
*** proxy ***class $Proxy0
*** method ***public abstract void com.itzhai.javanote.entity.Subject.doSomething()
*** args ***null
doSomething
*** proxy ***class $Proxy0
*** method ***public abstract void com.itzhai.javanote.entity.Subject.somethingElse(java.lang.String)
*** args ***[Ljava.lang.Object;@1a1c887
somethingElse: haha~~
动态代理可以将所有调用重定向到调用处理器,因此通常会向调用处理器传递一个真实对象的引用,从而使得调用处理器在执行其中介任务时,可以将请求转发。如上面的创建DynamicProxyHandler对象时就传入了真实的角色。
下面是一篇关于Java动态代理的文章: