概念
反射机制是动态语言的关键。反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息,并能操作任意对象的内部属性和方法。包括
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的成员变量和方法
- 生成动态代理
反射相关主要API
- java.lang.Class //类
- java.lang.reflect.Method //类方法
- java.lang.reflect.Field //类成员变量
- java.lang.reflect.Constructor //类构造方法
简单示例
先定义一个Unit类
public class Unit {
public int a;
private String b;
public void show(){
System.out.println("A = "+a+" ,B = "+b);
}
}
public class reflectionTest { @Test public void test1() throws Exception { Class<Unit> clazz = Unit.class; //Unit u = (Unit)clazz.newInstance(); Java9之后已被废弃 Unit u = clazz.getDeclaredConstructor().newInstance(); Field f1 = clazz.getField("a"); //private使用getDeclaredField Field f2 = clazz.getDeclaredField("b"); f1.set(u,1); //修改范围外属性需要设置可达性 f2.setAccessible(true); f2.set(u,"one"); Method m1 = clazz.getMethod("show"); //Method m1 = clazz.getDeclaredMethod("m1",String.class,Integer.class) //m1.setAccessible(true); //m1.invoke(obj,args); m1.invoke(u); } }
反射与Class类的联系
java.lang.Class是反射的源头。
我们创建的类通过编译(javac.exe),生成对应的.class文件,之后使用jvm的类加载器加载。此.class文件加载到内存后,就是一个运行时类,储存在缓存区。这个运行时类本身就是Class的实例。
每一个运行时类只加载一次。
有了Class的实例后,我们可以进行:
- 创建对应的运行时类的对象
- 获取对应的运行时类的完整结构(属性、方法、构造器、内部类、父类、所在包、异常、注解。。。)
- 调用对应的运行时类的指定结构(属性、方法、构造器)
获取Class实例的方式
调用运行时类本身的.class属性
Class clazz = Unit.class;
通过运行时类的对象获取
Unit u = new Unit();
Class clazz = u.getClass();
通过Class的静态方式获取
Class clazz = Class.forName("Unit");
通过类的加载器
ClassLoader classLoader = this.getClass().getClassLoader();
Class clazz = classLoader.loadClass("Unit");
Class对象的作用
getDeclaredConstructor(Class…parameterTypes)能够获取到本类的指定形参类型构造器。
向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
使用Constructor类中的newInstance()方法。
通过反射获取类的结构
Field
Class clazz = Unit.class;
//获取属性
//getFields()只能获取运行时类中声明为public的属性
Field[] fields = clazz.getDeclaredFields();
for(Field f:fields)
//获取属性的权限修饰符
Modifier.toString(f.getModifiers());
//获取属性的类型
f.getType().getName();
Method
Class clazz = Unit.class
//获取运行时类的方法
//getMethods()获取运行时类及其父类中所有声明为public的方法
//getDeclaredMethods()本身声明的所有方法
Method[] ms = clazz.getDeclaredMethods();
for(Method m:ms)
//获取注解
Annotation[] anns = m.getAnnotations();
//获取返回值类型
Class returnType = m.getReturnType();
//获取方法名
m.getName();
//形参列表
Class[] params = m.getParameterTypes();
//异常类型
m.getExceptionTypes();
Constructor
Class clazz = Unit.class
//获取构造器
Constructor[] cons = clazz.getDeclaredConstructors();
其他
Class clazz = Unit.class
//获取运行时类的父类
Class superClass = clazz.getSuperclass();
//获取带泛型的父类
Type type = clazz.getGenericSuperclass();
//获取父类的泛型
ParameterizedType param = (ParameterizedType)type;
Type[] types = param.getActuallyTypeArguments();
types[i].getName();
//获取实现的接口
Class[] interfaces = clazz.getInterfaces();
//获取所在的包
Package package = clazz.getPackage();
动态代理
静态代理的特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发过程中必然存在过多的代理。
动态代理可以通过一个代理类完成全部的代理功能。是指客户通过代理类来调用其他对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
代理设计模式的原理
使用一个代理将对象包装起来,然后用该代理对象取代原对象。任何原始对象的调用都要通过代理,代理对象决定是否以及何时将方法调用转到原始对象上。
静态代理模式示例
/** * 静态代理模式 */ interface ClothFactory{ void productCloth(); } //被代理类 class NikeClothFactory implements ClothFactory{ @Override public void productCloth() { System.out.println("Nike"); } } //代理类 class ProxyFactory implements ClothFactory{ ClothFactory cf; //创建代理类的对象时,实际传入一个被代理类的对象 public ProxyFactory(ClothFactory cf){ this.cf = cf; } @Override public void productCloth() { System.out.println("代理类开始执行"); cf.productCloth(); } } public class ClothProduct { public static void main(String[] args) { NikeClothFactory nike = new NikeClothFactory(); ProxyFactory proxy = new ProxyFactory(nike); proxy.productCloth(); } }
动态代理模式示例
/** * 动态代理模式 */ interface ClothFactory{ void productCloth(); } //被代理类 class NikeClothFactory implements ClothFactory{ @Override public void productCloth() { System.out.println("Nike"); } } //代理类 class MyInvocationHandler implements InvocationHandler { //实现了接口的被代理类的声明 Object obj; /* 1.给被代理类的对象实例化 * 2.返回一个代理类的对象 */ public Object bind(Object obj){ this.obj = obj; return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),this); } //当通过代理类的对象发起对被重写的方法的调用时,都会转化为对如下的invoke方法的调用 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //returnVal就是method方法的返回值 Object returnVal = method.invoke(obj,args); return returnVal; } } public class ClothProduct { public static void main(String[] args) { //1.创建被代理类的对象 NikeClothFactory nike = new NikeClothFactory(); //2.创建一个实现了InvotationHandler接口的类的对象 MyInvocationHandler handler = new MyInvocationHandler(); //3.调用bind方法,动态返回一个同样实现了nike所在类实现的接口ClothFactory的代理类的对象 Object obj = handler.bind(nike); //此时的clothFactory就是代理类的对象 ClothFactory clothFactory = (ClothFactory)obj; //转到对InvacationHandler接口的实现类的invoke()方法的调用 clothFactory.productCloth(); } }