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

1 单元测试

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

@Test

测试方法

@Before

用来修饰实例方法,该方法会在每一个测试方法执行之前执行一次。

@After

用来修饰实例方法,该方法会在每一个测试方法执行之后执行一次。

@BeforeClass

用来静态修饰方法,该方法会在所有测试方法之前只执行一次。

@AfterClass

用来静态修饰方法,该方法会在所有测试方法之后只执行一次

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

@Test

测试方法

@BeforeEach

用来修饰实例方法,该方法会在每一个测试方法执行之前执行一次。

@AfterEach

用来修饰实例方法,该方法会在每一个测试方法执行之后执行一次。

@BeforeAll

用来静态修饰方法,该方法会在所有测试方法之前只执行一次。

@AfterAll

用来静态修饰方法,该方法会在所有测试方法之后只执行一次

2 反射

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

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

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

反射的关键:

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

// 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();
// 获取类的全名,即包名.类名
String name = Class.forName("包名.类名").getName();
// 只获取类名
String name = Class.forName("包名.类名").getSimpleName();

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

获取构造器

方法 说明

Constructor[] getConstructors​()

返回所有构造器对象的数组(只能拿public的)

Constructor[] getDeclaredConstructors​()

返回所有构造器对象的数组,存在就能拿到

Constructor getConstructor​(Class... parameterTypes)

返回单个构造器对象(只能拿public的)

Constructor 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对象调用该方法

参数二:调用方法的传递的参数(如果没有就不写)

返回值:方法的返回值(如果没有就不写)

反射的作用

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

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

ArrayList 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中类、方法、成员变量做标记,然后进行特殊处理,到底做何种处理由业务需求来决定

自定义注解

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

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 annotationClass)

根据注解类型获得对应注解对象

boolean isAnnotationPresent(Class annotationClass)

判断当前对象是否使用了指定的注解,如果使用了则返回true,否则false

4 动态代理

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

关键步骤

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

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

优点

  • 非常灵活,支持任意接口类型的实现类对象做代理,也可以直接为本身做代理
  • 可以为被代理对象的所有方法做代理
  • 可以在不改变方法源码的情况下,实现对方法功能的增强
  • 不仅简化了编程工作、提高了软件系统的可扩展性,同时也提高了开发效率
// 返回是代理对象
public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h);
// loader 是定义代理类的类加载器
// interfaces 代理类要实现的接口列表
// h 代理对象的核心处理逻辑
public class ProxyUtil {
    public static  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、返回结果。
                    }
                });
    }
}
/**
    动态代理需要接口配合。
 */
public interface UserService {
    String login(String loginName, String passWord);
    void selectUsers();
    void deleteUsers();
}
// 创建业务对象
UserService userService = ProxyUtil.getProxy(new UserServiceImpl());
// 调用方法
String time = userService.login("aaa", "1111")

你可能感兴趣的:(java,开发语言)