Java单元测试、反射、注解、动态代理
单元测试
Junit常用注解
注解 | 说明 |
---|---|
@Test | 测试方法 |
@Before(Junit 4) @BeforeEach(Junit 5) |
用来修饰实例方法,该方法会在每一个测试方法执行之前执行一次。 |
@After(Junit4) @AfterEach(Junit 5) |
用来修饰实例方法,该方法会在每一个测试方法执行之后执行一次。 |
@BeforeClass(Junit 4) @BeforeAll(Junit 5) |
用来静态修饰方法,该方法会在所有测试方法之前只执行一次。 |
@After(Junit 4) @AfterAll(Junit 5) |
用来静态修饰方法,该方法会在所有测试方法之后只执行一次。 |
- 开始执行的方法:初始化资源
- 执行完之后的方法:释放资源
反射
反射概述
- 反射是指对于任何一个Class类,在"运行的时候"都可以直接得到这个类全部成分
- 在运行时,可以直接得到这个类的构造器对象:Constructor
- 在运行时,可以直接得到这个类的成员变量对象:Field
- 在运行时,可以直接得到这个类的成员方法对象:Method
- 这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制
反射获取类对象
方式一:Class c1 = Class.forName(“全类名”)
方式二:Class c2 = 类名.class
方式三:Class c3 = 对象.getClass()
反射获取构造器对象
方法 | 说明 |
---|---|
Constructor<?>[] getConstructors() | 返回所有构造器对象的数组(只能拿public的) |
Constructor<?>[] getDeclaredConstructors() | 返回所有构造器对象的数组,存在就能拿到 |
Constructor |
返回单个构造器对象(只能拿public的) |
Constructor |
返回单个构造器对象,存在就能拿到 |
Constructor类中用于创建对象的方法
符号 | 说明 |
---|---|
T newInstance(Object... initargs) | 根据指定的构造器创建对象 |
public void setAccessible(boolean flag) | 设置为true,表示取消访问检查,进行暴力反射 |
反射获取成员变量对象
Class类中用于获取成员变量的方法
方法 | 说明 |
---|---|
Field[] getFields() | 返回所有成员变量对象的数组(只能拿public的) |
Field[] getDeclaredFields() | 返回所有成员变量对象的数组,存在就能拿到 |
Field getField(String name) | 返回单个成员变量对象(只能拿public的) |
Field getDeclaredField(String name) | 返回单个成员变量对象,存在就能拿到 |
Field类中用于取值、赋值的方法
符号 | 说明 |
---|---|
void set(Object obj, Object value) | 赋值 |
Object get(Object obj) | 获取值 |
反射获取方法对象
Class类中用于获取成员方法的方法
方法 | 说明 |
---|---|
Method[] getMethods() | 返回所有成员方法对象的数组(只能拿public的) |
Method[] getDeclaredMethods() | 返回所有成员方法对象的数组,存在就能拿到 |
Method getMethod(String name, Class<?>... parameterTypes) | 返回单个成员方法对象(只能拿public的) |
Method getDeclaredMethod(String name, Class<?>... parameterTypes) | 返回单个成员方法对象,存在就能拿到 |
Method类中用于触发执行的方法
符号 | 说明 |
---|---|
Object invoke(Object obj, Object... args) | 运行方法 参数一:用obj对象调用该方法 参数二:调用方法的传递的参数(如果没有就不写) 返回值:方法的返回值(如果没有就不写) |
注意:如果某构造器、成员变量、方法是非public的,需要通过setAccessible(boolean)打开权限(暴力反射),然后再取值、赋值
绕过编译阶段为集合添加数据(泛型擦除)
反射是作用在运行时的技术,此时集合的泛型将不能产生约束了,此时是可以为集合存入其他任意类型的元素的。
泛型只是在编译阶段可以约束集合只能操作某种数据类型,在编译成Class文件进入运行阶段的时候,其真实类型都是ArrayList了,泛型相当于被擦除了
// 向集合添加非本集合类型的元素
public class Test {
public static void main(String[] args) throws Exception {
ArrayList<String> list = new ArrayList<>();
list.add("123");
Class c1 = list.getClass();
Method add = c1.getDeclaredMethod("add", Object.class);
add.invoke(list, 1);
add.invoke(list, true);
System.out.println(list);
}
}
注解
Java 语言中的类、构造器、方法、成员变量、参数等都可以被注解进行标注。
对Java中类、方法、成员变量做标记,然后进行特殊处理
自定义注解
public @interface 注解名称 {
public 属性类型 属性名() default 默认值;
}
特殊属性
- value属性,如果只有一个value属性的情况下,使用value属性的时候可以省略value名称不写
- 如果有多个属性, 且多个属性没有默认值,那么value名称是不能省略的
元注解
元注解:注解注解的注解
-
@Target: 约束自定义注解只能在哪些地方使用
@Target中可使用的值定义在ElementType枚举类中,常用值如下:
TYPE,类,接口
FIELD, 成员变量
METHOD, 成员方法
PARAMETER, 方法参数
CONSTRUCTOR, 构造器
LOCAL_VARIABLE, 局部变量 -
@Retention:申明注解的生命周期
@Retention中可使用的值定义在RetentionPolicy枚举类中,常用值如下:
SOURCE: 注解只作用在源码阶段,生成的字节码文件中不存在
CLASS: 注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值.
RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段(开发常用)
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
String value();
char sex();
int age();
}
注解解析
- Annotation: 注解的顶级接口,注解都是Annotation类型的对象
- AnnotatedElement:该接口定义了与注解解析相关的解析方法
方法 | 说明 |
---|---|
Annotation[] getDeclaredAnnotations() | 获得当前对象上使用的所有注解,返回注解数组。 |
T getDeclaredAnnotation(Class |
根据注解类型获得对应注解对象 |
boolean isAnnotationPresent(Class |
判断当前对象是否使用了指定的注解,如果使用了则返回true,否则false |
所有的类成分Class, Method , Field , Constructor,都实现了AnnotatedElement接口他们都拥有解析注解的能力
动态代理
条件:
-
必须有接口,实现类要实现接口(代理通常是基于接口实现的)
-
创建一个实现类的对象,该对象为业务对象,紧接着为业务对象做一个代理对象
优点:
- 非常的灵活,支持任意接口类型的实现类对象做代理,也可以直接为接本身做代理
- 可以为被代理对象的所有方法做代理
- 可以在不改变方法源码的情况下,实现对方法功能的增强
- 不仅简化了编程工作、提高了软件系统的可扩展性,同时也提高了开发效率。
案例:为所有方法加上性能分析功能
实现代码:
public class ProxyUtil {
public static <T> T getProxy(T obj) { // 为任意类的任意方法进行代理
return (T) Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis(); // 统计开始时间
Object s = method.invoke(obj, args); // 真正触发方法
long end = System.currentTimeMillis(); // 统计结束时间
System.out.println("耗时:" + (end - start) / 1000.0 + "s"); // 计算耗时
return s;
}
});
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南