【JAVA】单元测试、反射、注解、动态代理

1 单元测试

Junit常用注解(4.几版本)

|--------------|--------------------------------|
| @Test | 测试方法 |
| @Before | 用来修饰实例方法,该方法会在每一个测试方法执行之前执行一次。 |
| @After | 用来修饰实例方法,该方法会在每一个测试方法执行之后执行一次。 |
| @BeforeClass | 用来静态修饰方法,该方法会在所有测试方法之前只执行一次。 |
| @AfterClass | 用来静态修饰方法,该方法会在所有测试方法之后只执行一次。 |

Junit常用注解(5.几版本)

|-------------|--------------------------------|
| @Test | 测试方法 |
| @BeforeEach | 用来修饰实例方法,该方法会在每一个测试方法执行之前执行一次。 |
| @AfterEach | 用来修饰实例方法,该方法会在每一个测试方法执行之后执行一次。 |
| @BeforeAll | 用来静态修饰方法,该方法会在所有测试方法之前只执行一次。 |
| @AfterAll | 用来静态修饰方法,该方法会在所有测试方法之后只执行一次。 |

2 反射

反射是在运行时获取类的字节码文件对象,然后可以解析类中的全部成分

  • 反射的核心思想和关键:得到编译之后的class文件对象
  • 在运行时,可以直接得到这个类的构造器对象Constructor,类的成员变量对象Field,类的成员方法对象Method

这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制

反射的关键:

反射的第一步都是先获取Class类对象

java 复制代码
// HelloWorld.java -> javac -> HelloWorld.class
// 方式一:类名.class
Class c= HelloWorld.class;
// 方式二:Class.forName("全类名")
Class c2 = Class.forName("包名.类名");
// 方式三:对象.getClass();
HelloWorld h = new HelloWorld();
Class c3 = h.getClass();
java 复制代码
// 获取类的全名,即包名.类名
String name = Class.forName("包名.类名").getName();
// 只获取类名
String name = Class.forName("包名.类名").getSimpleName();

获取类对象后,我们可以类对象中获取类的成分对象

获取构造器

|-----------------------------------------------------------------------|--------------------------|
| 方法 | 说明 |
| Constructor<?>[] getConstructors() | 返回所有构造器对象的数组(只能拿public的) |
| Constructor<?>[] getDeclaredConstructors() | 返回所有构造器对象的数组,存在就能拿到 |
| Constructor<T> getConstructor(Class<?>... parameterTypes) | 返回单个构造器对象(只能拿public的) |
| Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) | 返回单个构造器对象,存在就能拿到 |

Constructor类中用于创建对象的方法

|-----------------------------------------|--------------------------------------------------------|
| T newInstance(Object... initargs) | 根据指定的构造器创建对象 |
| public void setAccessible(boolean flag) | 设置为true,表示取消访问检查,进行暴力反射 非public的构造器,反射可以破坏其封装性,私有也可以执行 |

获取成员变量

作用:在某个对象中取值、赋值

|-------------------------------------|---------------------------|
| 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) | 获取值。 |

获取方法对象

作用:在某个对象中进行执行此方法

|---------------------------------------------------------------------|---------------------------|
| 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对象调用该方法 参数二:调用方法的传递的参数(如果没有就不写) 返回值:方法的返回值(如果没有就不写) |

反射的作用

绕过编译阶段为集合添加数据

反射是在运行时的技术,此时集合的泛型将不能产生约束了,此时可以为集合存入其它任意类型的元素

java 复制代码
ArrayList<Integer> lists = new ArrayList<>();
list.add(100);
// list.add("ABC"); // 报错
list.add(99);
// 但是下面方法可以添加字符串
Class l = lists.getClass();
Method add = l.getDeclaredMethod("add", Object.class);
add.invoke(lists, "ABC");

反射的作用:

  • 可以在运行时得到一个类的全部成分
  • 可以破坏封装性
  • 破坏泛型的约束性
  • 做Java高级框架

基本主流框架都会基于反射设计一些通用技术功能

3 注解

对Java中类、方法、成员变量做标记,然后进行特殊处理,到底做何种处理由业务需求来决定

自定义注解

自定义注解就是自己做一个注解来使用

java 复制代码
public @interface 注解名称 {
    public 属性类型 属性名() default 默认值 ;
}

元注解

注解的注解,放在注解上的注解

@Target:约束自定义注解只能在哪些地方使用

@Retention:申明注解的生命周期

@Target中可使用的值定义在ElementType枚举类中,常用值如下

  • TYPE,类,接口
  • FIELD, 成员变量
  • METHOD, 成员方法
  • PARAMETER, 方法参数
  • CONSTRUCTOR, 构造器
  • LOCAL_VARIABLE, 局部变量

@Retention中可使用的值定义在RetentionPolicy枚举类中,常用值如下

  • SOURCE: 注解只作用在源码阶段,生成的字节码文件中不存在
  • CLASS: 注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值.
  • RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段(开发常用)

注解解析

注解的操作中通常需要进行解析,注解的解析就是判断是否存在注解,存在注解就解析出内容

  • Annotation:注解的顶级接口,注解都是Annotation类型的对象
  • AnnotatedElement:该接口定义了与注解解析相关的解析方法

|------------------------------------------------------------------|---------------------------------------|
| Annotation[] getDeclaredAnnotations() | 获得当前对象上使用的所有注解,返回注解数组。 |
| T getDeclaredAnnotation(Class<T> annotationClass) | 根据注解类型获得对应注解对象 |
| boolean isAnnotationPresent(Class<Annotation> annotationClass) | 判断当前对象是否使用了指定的注解,如果使用了则返回true,否则false |

4 动态代理

代理就是被代理者没有能力或者不愿意去完成某件事情,需要找个人代替自己去完成这件事,动态代理就是用来对业务功能(方法)进行代理

关键步骤

必须有接口,实现类要实现接口(代理通常是基于接口实现)

创建一个实现类的对象,该对象为业务对象,紧接着为业务对象做一个代理对象

优点

  • 非常灵活,支持任意接口类型的实现类对象做代理,也可以直接为本身做代理
  • 可以为被代理对象的所有方法做代理
  • 可以在不改变方法源码的情况下,实现对方法功能的增强
  • 不仅简化了编程工作、提高了软件系统的可扩展性,同时也提高了开发效率
java 复制代码
// 返回是代理对象
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
// loader 是定义代理类的类加载器
// interfaces 代理类要实现的接口列表
// h 代理对象的核心处理逻辑
java 复制代码
public class ProxyUtil {
    public static <T> T getProxy(T obj) {
        // 返回一个代理对象,给别人使用。
        // Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
        // 参数一:类加载器,负责加载代理类的
        // 参数二:需要被代理的方法在哪些接口中。
        // 参数三:代理对象的核心处理逻辑
        return (T)Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                obj.getClass().getInterfaces(), new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 核心处理逻辑
                        // proxy  代理对象:一般不需要理会。
                        // method 代表正在被代理的方法对象。
                        // args  被代理方法的参数值。
                        // 1、记录开始时间
                        long startTime = System.currentTimeMillis();
                        // 2、触发真正的业务对象的方法执行。
                        Object result = method.invoke(obj, args);
                        // 3、记录结束时间,统计耗时
                        long endTime = System.currentTimeMillis();
                        System.out.println(method.getName() + "方法耗时:" + (endTime - startTime) / 1000.0 + "s!");
                        return result; // 4、返回结果。
                    }
                });
    }
}
java 复制代码
/**
    动态代理需要接口配合。
 */
public interface UserService {
    String login(String loginName, String passWord);
    void selectUsers();
    void deleteUsers();
}
java 复制代码
// 创建业务对象
UserService userService = ProxyUtil.getProxy(new UserServiceImpl());
// 调用方法
String time = userService.login("aaa", "1111")
相关推荐
摇光9310 分钟前
js高阶-async与事件循环
开发语言·javascript·事件循环·宏任务·微任务
沐泽Mu14 分钟前
嵌入式学习-QT-Day09
开发语言·qt·学习
蓝染-惣右介15 分钟前
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
java·设计模式
小猿_0019 分钟前
C语言实现顺序表详解
c语言·开发语言
余~~1853816280040 分钟前
NFC 碰一碰发视频源码搭建技术详解,支持OEM
开发语言·人工智能·python·音视频
秋恬意1 小时前
IBatis和MyBatis在细节上的不同有哪些
java·mybatis
GOATLong1 小时前
c++智能指针
开发语言·c++
Dola_Pan1 小时前
C语言:随机读写文件、实现文件复制功能
c语言·开发语言