反射 - 注解 - 动态代理
1 反射
1.1 理解反射
1.1.1 反射是什么
以编程的方式获取类中的各种成分 (成员变量, 方法, 构造器等)
1.1.2 好处
无视修饰符,功能更强大灵活
1.1.3 应用场景
在Java中,反射主要用于以下几个方面:
- 动态加载类:通过反射可以在运行时动态地加载类,而不需要在编译时就将类的信息确定下来。这样可以实现一些动态扩展的功能,例如插件系统
- 获取类的信息:通过反射可以获取类的构造方法、字段、方法等信息,可以用于实现一些通用的代码处理逻辑,例如序列化、对象拷贝等
- 动态创建对象:通过反射可以在运行时动态地创建对象,可以根据配置文件、用户输入等动态地决定创建哪个类的对象
- 动态调用方法:通过反射可以在运行时动态地调用对象的方法,可以根据配置文件、用户输入等动态地决定调用哪个方法
- 修改私有字段和方法的访问权限:通过反射可以绕过访问权限,访问和修改对象的私有方法
1.2 获取类对象
类对象相当于这个类的所有内容
1.2.1 获取类对象的方式
1 Class c1 = 类名.class
2 Object提供的方法: public Class getClass(),对象.getClass()
3 调用Class提供方法:public static Class forName(String package)
//String package = 类全名 = 包名+类名
//1 根据对象名 反射对象
Class c2 = Student.class;
//2 根据现有对象 反射对象
Student s = new Student();
Class c1 = s.getClass();
//3 /\_/\ 根据相对路径(类全名) 反射对象
Class c3 = Class.forName("com.itheima._02getClass.Student");
1.3 获取类构造器
1.3.1 获取构造器的方法
1 获取所有public构造器: getConstructors()
2 获取一个public构造器: getConstructor (Class<?>... parameterTypes)
3 获取所有存在的构造器: getDeclaredConstructors()
4 获取一个存在的构造器: getDeclaredConstructor (Class<?>... parameterTypes)
1.3.2 反射得到的构造器的作用
1) 创建对象
public newInstance(Object... initargs)
2) 如果是非public的构造器,需要打开权限(暴力反射),然后再创建对象
setAccessible(boolean)
反射可以得到私有构造器
1.4 获取类成员变量
1.4.1 获取成员变量的方法
1 返回所有成员变量对象的数组(只能拿public的): public Field[] getFields()
2 返回所有成员变量对象的数组, 存在就能拿到: public Field[] getDeclaredFields()
3 返回单个成员变量对象(只能拿public的): public Field getField(String name)
4 返回单个成员变量对象,存在就能拿到: public Field getDeclaredField(String name)
1.4.2 反射得到的成员变量的作用
1) 赋值: void set(Object obj, Object value):
2) 取值: Object get(Object obj)
3) 暴力反射 (然后在取值 赋值): setAccessible(boolean)
1.5 获取类成员方法
1.5.1 获取类成员的方法
1 返回所有成员方法对象的数组 (只能拿public的) : Method[] getMethods()
2 返回所有成员方法对象的数组,存在就能拿到: Method[] getDeclaredMethods()
3 返回单个成员方法对象(只能拿public的): Method getMethod (String name, Class<?>... parameterTypes)
4 返回单个成员方法对象,存在就能拿到: Method getDeclaredMethod(String name, Class<?>... parameterTypes)
1.5.2 反射得到的累的成员方法的作用
1) 触发某个对象的该方法执行: public Object invoke(Object obj, Object... args)
2) 设置为true,表示取消访问检查,进行暴力反射: public void setAccessible(boolean flag)
1.6 作用及应用场景
/\ 可以在运行时得到一个类的全部成分然后操作。 /\ 可以破坏封装性。(很突出) /\ 更重要的用途是适合:做Java高级框架 /\ 基本上主流框架都会基于反射设计一些通用技术功能。
1.7 总结

2 注解 Annotation
2.1 概述
注解就是代码里特殊标记 (@Override、@FunctionalInterface、@Test)
用于对Java中类、方法、成员变量做标记,然后根据业务需求进行特殊处理
2.2 自定义注解
2.2.1 格式
public @interface 注解名称 {
public 属性类型 属性名() default 默认值 ;
}
//基本数据类型
String
Class
注解
枚举
以上类型的一维数组
2.3 元注解
元注解是修饰注解的注解。
@Target: 约束自定义注解只能在哪些地方使用, @Retention:申明注解的生命周期
2.3.1 @Target
Source: 注解只作用于源码阶段, 生成的字节码文件中不存在
Class: 注解作用在源码阶段, 字节码阶段, 运行阶段不存在, 默认值
Runtime: 注解作用在 源码, 字节码, 运行阶段(开发常用)
2.4 注解的解析
2.4.1 定义
注解的操作中经常需要进行解析,注解的解析就是判断是否存在注解,存在注解就解析出内容。
2.4.2 相关接口
Annotation: 注解的顶级接口,注解都是Annotation类型的对象 AnnotatedElement:该接口定义了与注解解析相关的解析方法
2.4.3 常用方法
1) 获得当前对象上使用的所有注解,返回注解数组: Annotation[] getDeclaredAnnotations()
2) 根据注解类型获得对应注解对象: T getDeclaredAnnotation(Class<T> annotationClass)
3) 判断当前对象是否使用了指定的注解,如果使用了则返回true,否则false: boolean isAnnotationPresent(Class<Annotation> annotationClass)
2.5 应用场景
模拟Junit框架
3 动态代理
3.1 定义
动态代理主要是对被代理对象的行为进行代理。 对功能进行增强。
3.2 动态代理开发步骤
1) 必须定义接口,里面定义一些行为,用来约束被代理对象和代理对象都要完成的事情。 2) 定义一个实现类实现接口,这个实现类的对象代表被代理的对象。 3) 定义一个测试类,在里面创建被代理对象,然后为其创建一个代理对象返回。(重点) 4) 代理对象中,需要模拟收首付款,真正触发被代理对象的行为,然后接收尾款操作。 5) 通过返回的代理对象进行方法的调用,观察动态代理的执行流程。
3.3 创建代理对象
Java中代理的代表类是:java.lang.reflect.Proxy,它提供了一个静态方法,用于为被代理对象,产生一个代理对象返回。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
为被代理对象返回一个代理对象。
参数一:类加载器 加载代理类,产生代理对象。,。
参数二:真实业务对象的接口。(被代理的方法交给代理对象)
参数三:代理的核心处理程序。
3.4 通过代理对象调用方法的流程
1) 先走向代理 2) 代理中可以真正触发被代理对象的方法执行。 3) 回到代理中,由代理负责返回结果给调用者。
3.5 好处
/\ 可以在不改变方法源码的情况下,实现对方法功能的增强,提高了代码的复用。 /\ 简化了编程工作、提高了开发效率,同时提高了软件系统的可扩展性,。 /\ 可以为被代理对象的所有方法做代理。 /\ 非常的灵活,支持任意接口类型的实现类对象做代理。
3.6 代码实现栗子
public interface Show {
public void sing();
public void dance();
}
public class YuiYui implements Show {
@Override
public void sing() {
System.out.println("Yui在唱如果我不是偶像");
}
@Override
public void dance() {
System.out.println("Yui在跳无论如何都喜欢你");
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ConcertDay {
public static void main(String[] args) {
// 创建被代理对象
YuiYui yui = new YuiYui();
// 创建代理对象
Show o = (Show)Proxy.newProxyInstance(
// 指定代理类的类加载器。
ConcertDay.class.getClassLoader(),
// 指定代理类需要实现的接口数组。这里使用 yui.getClass().getInterfaces() 来获取 yui 对象所实现的接口数组。
yui.getClass().getInterfaces(),
// 指定处理方法调用逻辑的 InvocationHandler 接口实例
// InvocationHandler 是一个接口,用于定义代理对象的方法调用逻辑。
new InvocationHandler() {
@Override
// proxy:代理对象本身。如果需要对代理对象进行操作或者获取代理对象的信息,可以使用该参数。通常情况下不会直接使用 proxy 参数,
// 而是将其强制转换为特定的类型(如 (Show) proxy)来调用具体的方法。
// method:被调用的方法对象,可以获取关于被调用方法的信息,如方法名、参数列表、返回类型等
// args:方法的参数数组: 如果方法有多个参数,则 args 数组按照参数顺序存储了相应的参数值。
// 在 invoke() 方法中,可以根据需要使用 args 参数来访问和操作方法的参数。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 代理者做的事情
System.out.println("肥秋出场收门票了!");
method.invoke(yui);
System.out.println("肥秋出场卖周边了!");
return null;
}
}
);
// 调用代理实现类
o.dance();
o.sing();
}
}
// 通过这三个参数,我们可以在 invoke() 方法中根据被调用方法的不同,实现自定义的逻辑和行为。
// 可以通过 proxy 对象操作代理对象,通过 method 对象获取方法相关信息,通过 args 数组访问方法的参数值。
// 这样就可以在方法调用前后进行一些额外的操作,例如记录日志、执行权限验证、实施事务管理等。