概念
反射机制是动态语言的关键。反射机制允许程序在执行期间借助于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();
}
}