前言:
反射机制是java的高级特性之一,而且也是基础,那么反射有什么应用场景呢,当然平常写业务逻辑开发一般是用不到的,它的应用是在框架里面,所以看源码,学习框架不懂这个代码都看不懂,所以还是要掌握它。
反射是什么?
java反射是在运行状态下,对于任意一个类,都能知道这个类的所有属性和方法,对于任意一个对象,都能调用它的任意一个方法和属性,这种动态获取信息以及动态调用队形的功能成为java语言的反射机制。
Class
提到反射就不能不提Class了,Class可以说是反射能够实现的基础,这里说的Class和class关键字不是一个东西,class关键字是在声明一个java类的时候用的,而Class是javaJDK提供的一个类
对于每一个类,Java虚拟机都会初始化一个Class类型的实例,每当我们编写并且编译一个新创建的类就会产生一个对象的Class对象,并且这个Class对象会被报存到同名的.class文件中,当我们new出一个实例或者引用静态变量时,Java虚拟机中类加载器会将对应的Class对象加载到JVM中,然后JVM会根据这个类型信息的Class对象来创建我们需要的实力对象或者提供静态变量的引用值。
类的结构信息组成
AnnotatedElement接口:该接口提供了获取注解信息的一些方法,即所有的参数、方法成员变量、构造方法都能通过反射获取到注解的相关信息
package类:对应Java包 的一些信息。
Parameter类:对应构造方法或者成员方法的参数类型,如参数访问限制,参数类型等
AccessibleObject类:该类是成员变量。成员方法、构造方法的父类。提供了一些public private等修饰符的入口检查
GenericDeclaration类:提供泛型的一些信息
Field类:对应的是成员变量
Executable类:可执行的、由两个子类、成员方法和构造方法,提供了两种方法公共的必要进行的一些操作,比如获取方法参数、获取方法名称、获取方法访问修饰符等等。
Method类:对应方法的类,提供了累的一些方法信息
Constructor类:对应构造方法的类,提供了构造方法的一些信息
-
Class类:包含了类的所有信息
一般我们常用的一般都是Field、Method Constructor这三个类
反射的使用
获取到java中要反射类的字节码有三种方法
-
任意一个类都有一个隐士的静态成员变量class
Class c=Statudent.class;
-
通过getClass方法获得
Class c2=mStatudent.getClass();
-
通过Class.forname();
Class c=Class.forName("包名+类名");
得到Class对象 我们就可以获取这个类中任意一个方法或者属性
-
获取该类中指定的属性
Class<?> studentClass=student.getClass(); Fiele ageField=studentClass.getDeclaredField("age); ageField.setAccessible(true); System.out.println("student的年龄"+student.getAge());
-
获取类中所有的字段包括父类的
Class<?> studentClass=student.getClass(); Field[] fieldArray=studentClass.getFields(); for(int i=-;i<fieldArray.length();i++){ System.out.println("当前方法名字是"+fieldArray[i]); }
-
获取该类所有的字段不包括父类的
Class<?> studentClass=student.getClass(); Field[] fieldArray=studentClass.getDeclaredFields();//主要是这个方法的区别 for(int i=-;i<fieldArray.length();i++){ System.out.println("当前方法名字是"+fieldArray[i]); }
-
修改非静态的方法和属性需要一个要修改的类的对象
Student student=new Student(20); Class<?> studentClass=student.getClass(); Field ageField=studentClass.getDeclaredField("age); ageField.setAccessible(trye); ageField.set(student,30); System.out.println("当前学生通过反射修改后的年龄是"+student.getAge());
上面需要注意的是如果字段是非public的 需要在访问该字段之前设置setAccessible(true),那么对于一个final字段是否可以通过反射修改它的值呢,答案是肯定的,前提是在访问该字段之前要取消该字段的访问权限,但是如果该字段即被static修饰又被final修饰,那么是无法修改的。
-
修改静态的字段
和修改非静态字段相比,修改类的静态字段就要轻松的多,因为静态字段是属于类所有的,所以在修改静态字段的时候就不需要在传递一个该类的实例对象了
Field nameField=studentClass.getDeclaredField("name); nameField.setAccessible(true); nameField.set(null,"demo"); System.out.println("反射修改静态字段 修改的名字是"+nameField.get(null));
通过反射获取并且调用类中的方法
Class类提供了4中方式获取类中的方法,其实和获取Field一样
- getMethod(String name,Class[] params) 使用特定的参数类型,获得name名字相同的公共方法
- Method[] getMethods() 获取类的所有公共方法 有父类的
- Method getDeclaredMethod(String name,Class[] params);可以获取私有的方法通过特定的参数类型。
- Method[] getDeclaredMethods() 获得类声明的所有方法 不包括父类的
和前面设置属性一样,method 有invoke(Object obj,Object ...args)这个方法,通过这个方法,可以调用任意一个类的任意一个方法,当然 如果要是调用的静态方法,那么第一个参数就直接填null。
反射机制的优缺点
- 优点:就是可以实现动态创建对象和编译,强大的灵活性,并且代码简洁明了,
- 缺点:对性能有影响,使用反射是一种解释操作,而且还绕过了源码,会干扰原来的内部逻辑
代理
代理是一种常见的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问,代理类负责委托类预处理,以及进行被委托类执行后的处理。
如果需要委托类来处理某一个业务,那么我就可以先在代理类中统一处理然后在调用具体实现类。
分类:
- 静态代理:由开发人员创建代理类,在对其进行编译,代理类内部是持有真实类的对象的。在程序运行钱代理类的.class文件就已经存在了
- 动态代理:在程序运行时使用反射机制动态创建一个代理类。根据jdk的源码可以看到
动态代理:
//先定义接口
interface Subject{
void say(String name);
}
//这是真实处理对象
class RealSubject implements Subject{
public void say(String name){
System.out.println("我的姓名是"+name);
}
}
//下面是一个动态代理类,
class CustomInvocationHandler implements Invocationhandler{
Object subObj;
public CustomInvocationHandler(Object obj){
this.subObj=obj;
}
public Object getProxyInstance(){
rerutn Proxy.newProxyInstance(subObj.getClass.getClassLoader(),subObj.getClass.getInterfaces,this);
}
public Object invoke(Object proxy,Method method,Object[] args){
return method.invoke(subObj,args);
}
}
// 下面开始使用动态代理类
RealSubject realObj=new RealSubject();
CustomInvocationHandler mInvocation=new CustomInvocationHandler(realObj);
Subject proxyObj=mInvocation.getProxyInstance();//得到代理对象 转成
proxyObj.say();
jdk中生成动态代理的源码:
ProxyUtils.generateClassFile(aafactory.getClass(),employee1.getClass().getSimpleName();ProxyUtils.generateClassFile(bbToolsFactory.getClass(),employee2.getClass().getSimpleName());
Method[] methods = aafactory.getClass().getMethods();
for(Method method:methods) {
System.out.println(method.getName());//打印方法名称
}
public class ProxyUtils {
public static void generateClassFile(Class clazz,String proxyName){
/*ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);*/
byte[] proxyClassFile =ProxyGenerator.generateProxyClass(
proxyName, new Class[]{clazz});
String paths = clazz.getResource(".").getPath();
System.out.println(paths);
FileOutputStream out = null;
try {
out = new FileOutputStream(paths+proxyName+".class");
out.write(proxyClassFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}