反射
反射是作用于类型信息的,从字面上理解容易想到有反必有正,对于类型信息的操作,常规情况当然是直接拿到这个类,从而创建对象,使用对象。那么相对的,反其道而行之会怎么样呢?
首先我们可以在编译时不知道具体的类,甚至是类的原始信息,其次我们应该是通过某种通用工具在运行时去获取类的信息,进行分析拆解和使用。
反射所使用的工具 Java 类库中已经提供,在 java.lang.reflect
包下,结合 Class
类就可以实现反射操作了。
package array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
class Simple{
public Simple() {
}
public void print() {
System.out.println("test");
}
}
public class TestReflect {
public static void main(String[] args) throws Exception {
String className = "array.Simple";
String methodName = "print";
//得到类的Class对象
Class<?> c = Class.forName(className);
//通过Class对象得到类的构造方法对象
Constructor<?> con = c.getDeclaredConstructor();
//通过构造方法对象间接创建类的对象
Object obj = con.newInstance();
//通过Class对象获取类的具体方法
Method m = c.getDeclaredMethod(methodName);
//通过反射组件Method对象,执行它所代表的方法
m.invoke(obj);
}
}
对反射提供支持的除了上面例子中提到的Constructor
、Method
外,还有代表成员变量的Field
,这三个类均实现了Member
接口。通过类的Class对象和反射组件,可以在运行时编译解析一个类,并且得到其对象。
动态代理
在设计模式中有一种典型的模式叫做代理模式,其思想跟现在要说的动态代理基本类似,所谓代理,也就是中介的意思,例如对象 A
有一个代理 B
,那么我们可以理解为代理 B
可以全权负责我们和 A
之间的任何事情,也就是代理 B
可以与A
通信,也可以做额外的事情。
简单的举个例子:
interface Person {
public void eat();
public void run();
}
/**
* 被代理的实体类
* @author Alan
*
*/
class xiaoming implements Person{
@Override
public void eat() {
System.out.println("xiaoming eat");
}
@Override
public void run() {
System.out.println("xiaoming run");
}
}
/**
* 代理Person接口所有子类
* @author Alan
*
*/
class PersonProxy implements Person {
private Person proxy;
public PersonProxy(Person proxy) {
this.proxy = proxy;
}
@Override
public void eat() {
System.out.println("Person Proxy");
proxy.eat();
}
@Override
public void run() {
System.out.println("Person Proxy");//额外工作
proxy.run();//代理与被代理实体对象通信
}
}
public class TestProxy {
public static void todo(Person person) {
person.eat();
person.run();
}
public static void main(String[] args) {
todo(new xiaoming());
todo(new PersonProxy(new xiaoming()));//通过代理来实现
}
}
在知道了代理的基本实现方式后,思考一下代理可以带给我们什么呢?从例子里可以看到代理中可以添加额外的功能并且不影响被代理的实体类的逻辑。因此,如果我们想要测试被代理类某些方法的效率开销等,当然不能直接将这部分写入到实体类中,代理是一种合适的方式。
上面只是讲了代理的基本思想和简单实现方式,那么动态代理又是什么呢?
动态代理是 Java
自带的一种实现,我们可以直接拿来使用,简单的说就是 Java
的动态代理可以实现动态的创建代理和处理代理方法的调用。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface Person{
public void eat(String s);
public void run();
}
/**
* 被代理的实体类
* @author Alan
*
*/
class xiaoming implements Person{
@Override
public void eat(String s) {
System.out.println(s);
}
@Override
public void run() {
System.out.println("xiaoming run");
}
}
/**
* 代理类
* @author Alan
*
*/
class DynamicProxyHandler implements InvocationHandler {
private Object proxy;
public DynamicProxyHandler(Object proxy) {
this.proxy = proxy;
}
@Override
public Object invoke(Object obj, Method method, Object[] aobj) throws Throwable {
System.out.println("to do something");//额外工作
return method.invoke(proxy, aobj);//通过反射来调用obj中的某个方法,aobj是方法参数
}
}
public class TestProxy {
public static void todo(Person person) {
person.eat("aabbcc");
person.run();
}
public static void main(String[] args) {
todo(new xiaoming());
//通过Proxy类创建Person对象,其中原理是基于反射
Person person = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(),
new Class<?>[]{Person.class}, new DynamicProxyHandler(new xiaoming()));
todo(person);
}
}
接口与类型信息
反射技术可以通过 Class 对象间接得到类的所有信息并且可以使用这些信息,我们知道一个类的 Class
对象包括了类的所有信息,不论是 public
的还是private
的都在,事实上反射技术可以绕过这些权限定义,来直接操纵这个类。
还是以上文中的反射例子为主,现在我们把类中的方法改为 private
:
package array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
class Simple{
public Simple() {
}
private void print() {
System.out.println("test");
}
}
public class TestReflect {
public static void main(String[] args) throws Exception {
String className = "array.Simple";
String methodName = "print";
Class<?> c = Class.forName(className);
Constructor<?> con = c.getDeclaredConstructor();
Object obj = con.newInstance();
Method m = c.getDeclaredMethod(methodName);
m.setAccessible(true);//设置为true时,可以调用私有方法或获取私有成员变量
m.invoke(obj);
}
}
即便是私有内部类,匿名类等,均可以被反射拿到私有权限的方法。除了标记为 final
的变量,运行时它是不可变的。
通常情况我们不必要考虑这么多,所有这些违反访问权限的操作并不是没有意义的。如果有人这么使用的话,那么他必须要对自己的行为负责,而且存在这种类的后门,有时候也可以解决一些特定的问题。