1) 介绍
JAVA反射机制是在虚拟机运行状态中:
-
对于任意一个类,都能够知道这个类的所有属性和方法;
-
对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
反射缺点
反射功能虽然强大,但不能随便使用,在能不用的情况下尽量不要使用,使用反射应该考虑以下问题:
-
性能开销 反射操作比非反射要慢,由于涉及动态解析,某些操作虚拟机是不优化的,应该尽量避免在频繁调用的代码中使用
-
安全 执行反射需要权限,在安全管理器下运行时可能不存在该权限。对于必须在受限安全上下文(例如 Applet)中运行的代码
-
封装暴露
-
反射十分强大,可以访问私有(private)字段和方法,这用可能会导致代码功能失效并且破坏可移植性与封装性
-
反射打破了抽象,因此可能会随着平台的升级而改变行为。
-
教程文档:https://docs.oracle.com/javase/tutorial/reflect/index.html
使用文档:Core Java Reflection
2) Classes
Class对象是所有反射API的入口,不可以自己创建,每个类或该类的实例对象,虚拟机都会实例化一个不可变的Class对象,该对象提供在运行时获取类的信息(属性名,方法名)的一组方法,通过类的对象实例可以访问属性数据,执行该类的对象方法,还有很多方法,如创建新的类和对象能力。
获取Class对象方式:
Class a = 类名.class
Class b = 对象.getClass();
Class c = 数组.getClass();
Class d = Class.forName("org.lls.User");
Class API
-
字段
-
class.getFields() 获取所有公有的字段
-
class.getDecalreFields() 获取所有的字段(包括私有、受保护、默认的)
-
class.getField(param) 获取公有字段
-
Field.set(Obje,value); 反射设置一个对象属性值
-
Field 该类提供对字段一系列操作
-
setAccessible() 访问检查,true表示禁止访问检查,false表示限制执行访问检查
-
-
-
方法
-
class.getConstructors() 获取所有公有构造方法
-
class.getDeclaredConstructors() 获取所有构造方法(包括:私有、受保护、默认、公有)
-
class.getConstructor(param) 获取公有、无参的构造方法
-
class.getDeclaredConstructor(param) 获取私有构造方法
-
newInstance(); 反射执行一个对象方法
-
setAccessible(boolean) 暴力访问(忽略掉访问修饰符),true打开权限,false关闭权限
-
Constructor 该类提供对方法与构造函数一系列操作
-
setAccessible() 访问检查,true表示禁止访问检查,false表示限制执行访问检查
-
-
-
注解
-
getAnnotation() 用于获取(对象的注解,方法注解,字段注解)
-
Annotation 该类提供注解一系列操作
-
实例
/***
* 1、获取属性上的指定类型的注释
* 2、获取属性上的指定类型的注释的指定方法
* 3、获取属性上的所有注释
* 4、获取类上的所有注释
* 5、获取方法上的所有注释
* @author 2014-11-10 下午02:18:24
* @param args
*/
@SuppressWarnings("rawtypes")
public static void main(String[] args) {
Field[] fields = User.class.getDeclaredFields();
for(Field f : fields){
String filedName = f.getName();
System.out.println("属性名称:【"+filedName+"】");
//1、获取属性上的指定类型的注释
Annotation annotation = f.getAnnotation(XmlElement.class);
//有该类型的注释存在
if (annotation!=null) {
//强制转化为相应的注释
XmlElement xmlElement = (XmlElement)annotation;
//3、获取属性上的指定类型的注释的指定方法
//具体是不是默认值可以去查看源代码
if (xmlElement.name().equals("##default")) {
System.out.println("属性【"+filedName+"】注释使用的name是默认值: "+xmlElement.name());
}else {
System.out.println("属性【"+filedName+"】注释使用的name是自定义的值: "+xmlElement.name());
}
}
//2、获取属性上的所有注释
Annotation[] allAnnotations = f.getAnnotations();
for(Annotation an : allAnnotations){
Class annotationType = an.annotationType();
System.out.println("属性【"+filedName+"】的注释类型有: " + annotationType);
}
System.out.println("----------华丽的分割线--------------");
}
//4、获取类上的所有注释
Annotation[] classAnnotation = User.class.getAnnotations();
for(Annotation cAnnotation : classAnnotation){
Class annotationType = cAnnotation.annotationType();
System.out.println("User类上的注释有: " +annotationType);
}
System.out.println("----------华丽的分割线--------------");
// 5、获取方法上的所有注释
Method method;
try {
method = User.class.getMethod("setPwd",String.class);
Annotation[] methodAnnotations = method.getAnnotations();
for(Annotation me : methodAnnotations){
Class annotationType = me.annotationType();
System.out.println("setPwd方法上的注释有: " + annotationType);
}
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
@XmlElement
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getPwd() {
return pwd;
}
}
3) 动态代理
利用反射机制在运行时创建代理对象,JDK提供了java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy类,这两个类相互配合,入口是Proxy
Proxy主要静态方法
-
Proxy.getProxyClass(classLoader,class..) 获取代理类
-
Proxy.newProxyInstance(classLoader,class[],InvocationHandler) 创建代理对象
-
代理类的类加载器
-
要被代理的接口,只能是接口
-
InvocationHandler代理的处理类,执行代理对象时,调用invoke进行代理拦截
-
-
Proxy.getInvocationHandler(Object proxy) 根据代理对象获取代理处理类
-
Proxy.isProxyClass(class) 判断是否代理类
代理实例
public class DebugProxy implements InvocationHandler {
private Object obj;
public static <T> T newInstance(T obj) {
return (T) java.lang.reflect.Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
new DebugProxy(obj)
);
}
private DebugProxy(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method m, Object[] args)throws Throwable
{
Object result;
try {
System.out.println("前方法 " + m.getName());
result = m.invoke(obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (Exception e) {
throw new RuntimeException("意外调用异常: " +e.getMessage());
} finally {
System.out.println("后方法 " + m.getName());
}
return result;
}
}
@Test
public void t2(){
Foo foo = DebugProxy.newInstance(new FooImpl());
System.out.println(foo.say("你好"));
}