0


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

🏡个人主页 :@ 守夜人st
🚀系列专栏:Java
…持续更新中敬请关注…
🙉博主简介:软件工程专业,在校学生,写博客是为了总结回顾一些所学知识点

目录

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

单元测试

单元测试概述

单元测试就是针对最小的功能单位编写测试代码,Java程序最小功能单元是方法,因此,单元测试就是针对方法的测试,进而检查方法的正确性

我们平常采用的测试存在的弊端:

  • 只有一个main方法,如果一个方法的测试失败了,其他反复测试会受到影响
  • 无法得到测试的结果报告,需要程序员自己去观察测试是否成功
  • 无法实现自动化测试

JUnit单元测试框架:

  • JUnit是使用Java语言实现的单元测试框架,他是开源的,Java开发者都应当学习并使用JUnit编写单元测试
  • 此外,几乎所有的IDE工具都集成了JUnit,这样我们就直接可以在IDE中编写并运行JUnit单元测试JUnit优点:- JUnit可以灵活的选择执行哪些测试方法,可以一键执行全部测试方法- 可以生成全部方法的测试报告- 单元测试中的某个方法测试失败了,不影响其他测试方法的测试

单元测试快速入门

需求:使用单元测试进行业务方法预期结果、正确性测试的快速入门

  1. 将JUnit的jar包导入到项目中
  • IDEA通常整合好了JUnit框架,一般不需要导入
  • 如果IDEA没有整合好,需要自己手工导入JUnit的jar包到模块
  1. 编写测试方法:该方法必须是公共的无参数无返回值的非静态方法
  2. 在测试方法上使用@Test注解:标注该方法是一个测试方法
  3. 在测试方法中完成被测试方法的预期正确性测试
  4. 选中测试方法,选择“ JUnit运行 ”,如果测试良好是绿色,测试失败是红色
packagecom.shouyeren.test;publicclassUserService{publicStringloginName(String loginName,String passWord){if("admin".equals(loginName)&&"123456".equals(passWord)){return"登录成功";}else{return"用户名或者密码不正确";}}publicvoidselectNames(){System.out.println("查询所有用户名成功!");}}
packagecom.shouyeren.test;importorg.junit.Assert;importorg.junit.Test;publicclassTestUserService{@TestpublicvoidtestLoginName(){UserService userService =newUserService();String rs = userService.loginName("admin","123456");Assert.assertEquals("您的登录业务功能可能出BUG","登录成功",rs);}@TestpublicvoidtestSelectNames(){UserService userService =newUserService();
        userService.selectNames();}}

在这里插入图片描述

单元测试常用注解

注解说明(JUnit4)@Test测试方法@Before用来修饰实例方法,该方法会在每一个测试方法执行之前执行一次@After用来修饰实例方法,该方法会在每一个测试方法执行之后执行一次@BeforeClass用来修饰静态方法,该方法会在所有测试方法执行之前执行一次@AfterClass用来修饰静态方法,该方法会在所有测试方法执行之后执行一次

  • 开始执行的方法:初始化资源
  • 执行完之后的方法:释放资源

反射

反射概述

反射是指对于任何一个Class类,在“运行的时候”都可以直接得到这个类的全部成分

构造器对象:Constructor

成员变量对象:Field

成员方法对象:Method

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

反射的基本作用、关键?

  • 反射是在运行时获取类的字节码文件对象,解析获得类中全部成分
  • 反射的核心思想和关键就是:得到编译后的Class文件

反射获取类对象

获取Class类对象的三种方式:

  1. Class c1 = Class.forName(“全类名”);
  2. Class c2 = 类名.class;
  3. Class c3 = 对象.getClass();

Class类中用于获取构造器的方法
方法说明Constructor<?>[ ] getConstructors()返回所有构造器对象的数组(只能拿public的)Constructor<?>[ ] getDeclaredConstructors()返回所有构造器对象的数组Constructor<?>[ ] getConstructor(Class<?>…parmeterTypes)返回单个构造器对象(只能拿public的)Constructor<?>[ ] getDeclaredConstructors(getConstructor(Class<?>…parmeterTypes)返回单个构造器对象

packagecom.shouyeren.reflect;importorg.junit.Test;importjava.lang.reflect.Constructor;publicclassTestStudent{/**
     * 获取类中的全部构造器对象(public)
     */@TestpublicvoidgetConstructors(){Class<Student> c =Student.class;Constructor[] constructors = c.getConstructors();for(Constructor constructor : constructors){System.out.println(constructor.getName()+"==>"+ constructor.getParameterCount());}}/**
     * 获取类中的全部构造器对象
     */@TestpublicvoidgetDeclaredConstructors(){Class<Student> c =Student.class;Constructor[] constructors = c.getDeclaredConstructors();for(Constructor constructor : constructors){System.out.println(constructor.getName()+"==>"+ constructor.getParameterCount());}}/**
     * 获取某个构造器对象(public)
     */@TestpublicvoidgetConstructor()throwsNoSuchMethodException{Class<Student> c =Student.class;//可以添加参数Constructor cons = c.getConstructor(String.class,int.class);System.out.println(cons.getName()+"==>"+ cons.getParameterCount());}/**
     * 获取某个构造器对象
     */@TestpublicvoidgetDeclaredConstructor()throwsNoSuchMethodException{Class<Student> c =Student.class;//可以添加参数类型Constructor cons = c.getDeclaredConstructor();System.out.println(cons.getName()+"==>"+ cons.getParameterCount());Constructor cons1 = c.getDeclaredConstructor(String.class,int.class);System.out.println(cons.getName()+"==>"+ cons1.getParameterCount());}}
packagecom.shouyeren.reflect;publicclassStudent{privateString name;privateint age;privateStudent(){}publicStudent(String name,int age){this.name = name;this.age = age;}publicStringgetName(){return name;}publicvoidsetName(String name){this.name = name;}publicintgetAge(){return age;}publicvoidsetAge(int age){this.age = age;}}

使用反射技术获取构造器对象并使用

  • 获取构造器的作用依然是初始化一个对象返回
    符号说明T newInstance(Object… initargs)根据指定的构造器创建对象public void setAccessible(boolean flag)设置为true,表示取消访问检查,进行暴力反射
    packagecom.shouyeren.reflect;importorg.junit.Test;importjava.lang.reflect.Constructor;publicclassTestStudent1{@TestpublicvoidgetDeclaredConstructor()throwsException{Class<Student> c =Student.class;//可以添加参数类型Constructor cons = c.getDeclaredConstructor();System.out.println(cons.getName()+"==>"+ cons.getParameterCount());//私有构造器无法访问时,可以暴力反射
          cons.setAccessible(true);Student s =(Student) cons.newInstance();System.out.println(s);Constructor cons1 = c.getDeclaredConstructor(String.class,int.class);System.out.println(cons.getName()+"==>"+ cons1.getParameterCount());Student s1 =(Student) cons1.newInstance("叶文洁",58);System.out.println(s1.getName()+ s1.getAge());}}
    

反射获取成员变量

使用反射技术获取成员变量对象并使用

  • 反射的第一步是先得到类对象,然后从类对象中获取类的成分对象
  • Class类中用于获取成员变量的方法
    方法说明Field[] getFields()返回所有成员变量对象的数组(public)Field[] getDeclaredFields()返回所有成员变量对象的数组Field[] getField(String name)返回单个成员变量对象(public)Field[] getDeclaredField(String name)返回单个成员变量对象
    ```
    packagecom.shouyeren.reflect;importorg.junit.Test;importjava.lang.reflect.Field;/**
  • | Field[] getFields() | 返回所有成员变量对象的数组(public) |
  • | Field[] getDeclaredFields() | 返回所有成员变量对象的数组 |
  • | Field[] getField(String name) | 返回单个成员变量对象(public) |
  • | Field[] getDeclaredField(String name) | 返回单个成员变量对象 |

/publicclassFieldDemo{/*
* Field[] getDeclaredFields() | 返回所有成员变量对象的数组 |
/@TestpublicvoidgetDeclaredFields(){//定位Class对象Class<Student> s =Student.class;//定位所有成员变量对象Field[] fields = s.getDeclaredFields();for(Field field : fields){System.out.println(field.getName()+"==>"+ field.getType());}}/*
* Field[] getFields() | 返回所有成员变量对象的数组(public) |
/@TestpublicvoidgetFields(){//定位Class对象Class<Student> s =Student.class;//定位所有成员变量对象Field[] fields = s.getFields();for(Field field : fields){System.out.println(field.getName()+"==>"+ field.getType());}}/*
* Field[] getField(String name) | 返回单个成员变量对象(public)
* @throws Exception
/@TestpublicvoidgetField()throwsException{//定位Class对象Class<Student> s =Student.class;//成员变量对象Field field = s.getField("schoolName");System.out.println(field.getName()+"==>"+ field.getType());}/*
* Field[] getDeclaredField(String name) | 返回单个成员变量对象 |
* @throws Exception
*/@TestpublicvoidgetDeclaredField()throwsException{//定位Class对象Class<Student> s =Student.class;//成员变量对象Field field = s.getDeclaredField("age");//打开权限
field.setAccessible(true);Student student =newStudent("张三",20);//赋值
field.set(student,18);//取值System.out.println(field.get(student));}}


### 反射获取方法

**使用反射技术获取方法对象并使用**


- 反射的第一步是先得到类对象,然后从类对象中获取类的成分对象
- Class类中用于获取成员方法的方法
方法说明Method[ ] getMethods()返回所有成员方法对象的数组(public)Method[ ] getDeclaredMethods()返回所有成员方法对象的数组Method[ ] getMethod()返回单个成员方法对象的数组(public)Method[ ] getDeclaredMethod()返回单个指定成员方法对象的数组
### 反射的作用——绕过编译阶段为集合添加数据(泛型擦除)

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

ArrayList<Integer> list =newArrayList<>();
list.add(100);
list.add("字符串");//报错


泛型只是在**编译阶段**可以约束集合只能操作某种数据类型,在编译成Class文件进入**运行阶段**的时候,其真实类型都是ArrayList,**泛型相当于被擦除了**

packagecom.shouyeren.reflect;importjava.lang.reflect.Method;importjava.util.ArrayList;publicclassReflectDemo{publicstaticvoidmain(String[] args)throwsException{ArrayList<Integer> list1 =newArrayList<>();ArrayList<Integer> list2 =newArrayList<>();System.out.println(list1.getClass()== list2.getClass());System.out.println("--------------------------------------");ArrayList<Integer> list3 =newArrayList<>();
list3.add(18);
list3.add(20);//list3.add("字符串");Class c = list3.getClass();Method add = c.getDeclaredMethod("add",Object.class);boolean rs =(boolean) add.invoke(list3,"字符串");System.out.println(list3);System.out.println(rs);}}


![在这里插入图片描述](https://img-blog.csdnimg.cn/280887b50ee54deaa364c7824acf2729.png#pic_center)

### 反射的作用——通用框架的底层原理

> 需求:给你任意一个对象,在不清楚对象字段的情况下,可以把对象的字段名称和对应值存储到文件中去
> 
> 
> 1. 定义一个方法,可以接收任意类对象
> 2. 每次收到一个对象后,需要解析这个对象的全部成员变量名称。
> 3. 这个对象可能是任意的,那么怎么样才可以知道这个对象的全部成员变量名称呢
> 4. 使用反射获取对象的Class文件,然后获取全部成员变量信息
> 5. 遍历全部成员变量信息,然后提取本成员变量在对象中的具体值
> 6. 存入成员变量名称和值到文件中

packagecom.shouyeren.reflect.framework;importjava.io.FileOutputStream;importjava.io.PrintStream;importjava.lang.reflect.Field;publicclassMybatisUtil{/**
* 保存任意类型的对象
* @param obj
*/publicstaticvoidsave(Object obj){try(PrintStream ps =newPrintStream(newFileOutputStream("src/data.txt",true))){//获取这个对象的全部成员变量Class c = obj.getClass();
ps.println("----------------"+ c.getSimpleName()+"----------------");Field[] fields = c.getDeclaredFields();for(Field field : fields){String name = field.getName();
field.setAccessible(true);String value = field.get(obj)+"";
ps.println(name +" = "+ value);}}catch(Exception e){
e.printStackTrace();}}}


packagecom.shouyeren.reflect.framework;/**

  • 提供一个通用框架,支持保存所有对象的具体信息

*/publicclassReflectDemo{publicstaticvoidmain(String[] args){Student student =newStudent();
student.setName("张三");
student.setClassName("实验一班");
student.setAge(20);
student.setHobby("打球");
student.setSex('男');MybatisUtil.save(student);Teacher teacher =newTeacher();
teacher.setName("苍老师");
teacher.setSex('女');
teacher.setSalary(12000);MybatisUtil.save(teacher);}}


反射的作用:


- 可以在运行阶段得到一个类的全部成分然后操作
- 可以破坏封装性
- 可以破坏泛型约束
- 更重要的用途是适合:做Java高级框架

## 注解

### 注解概述

> Java注解(Annotation)又称Java标注,是JDK5.0引入的一种注释机制
> 
> Java语言中的类、构造器、方法、成员变量、参数等都可以被注解进行标注
> 
> 例如:JUnit框架中,标记了注解@Test的方法就可以被当成测试方法执行

### 自定义注解

自定义注解格式:

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


特殊属性:


- value属性,如果只有一个value属性的情况下,使用value属性的时候可以省略value名称不写
- 但是如果有多个属性,且多个属性没有默认值,那么value名称是不能省略的

### 元注解

> 元注解就是注解的注解
> 
> 元注解有两个:
> 
> 
> - @Target:约束自定义注解只能在哪些地方使用
> - @Retention:声明注解的生命周期

@Target:


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

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


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

### 注解解析

> 注解的操作中经常需要进行解析,注解的解析就是判断是否存在注解,存在注解就解析出内容
> 
> 与注解解析相关的接口:
> 
> 
> - Annotation:注解的顶级接口,注解都是Annotation类型的对象
> - AnnotatedElement:该接口定义了与注解解析相关的解析方法

注解解析的技巧:


- 注解在哪个成分上,就先拿哪个成分对象
- 比如注解作用成员方法,则要获得该成员方法对应的Method对象,再拿上面的注解

### 注解的应用场景一:JUnit框架

**模拟JUnit框架**

> 定义若干个方法,只要加了MyTest注解,就可以在启动时被触发执行
> 
> 分析:
> 
> 
> 1. 定义一个自定义注解MyTest,只能注解方法,存活范围是一直在
> 2. 定义若干个方法,只要有@MyTest注解的方法就能在启动时被触发执行,没有该注解则不能执行

packagecom.shouyeren.annotation;importjava.lang.annotation.*;@Target({ElementType.METHOD,ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)public@interfaceMyTest{}


packagecom.shouyeren.annotation;importjava.lang.reflect.InvocationTargetException;importjava.lang.reflect.Method;publicclassAnnotationTest{@MyTestpublicvoidtest1(){System.out.println("===test1===");}publicvoidtest2(){System.out.println("===test2===");}@MyTestpublicvoidtest3(){System.out.println("===test3===");}publicstaticvoidmain(String[] args)throwsInvocationTargetException,IllegalAccessException{AnnotationTest test =newAnnotationTest();Class c =AnnotationTest.class;Method[] methods = c.getDeclaredMethods();for(Method method : methods){if(method.isAnnotationPresent(MyTest.class)){
method.invoke(test);}}}}


## 动态代理

### 动态代理概述、快速入门

如何创建代理对象:


- Java中代理的代表类是:java.lang.reflect.Proxy
- Proxy提供了一个静态方法,用于为对象产生一个代理对象返回

### 动态代理的应用案例:做性能分析,代理的好处

> 需求:
> 
> 模拟某企业用户管理业务,需包含用户登录,用户删除,用户查询功能,并统计每个功能的耗时
> 
> 分析:
> 
> 
> 1. 定义一个UserService表示用户业务接口,规定必须完成用户登录,用户删除,用户查询功能
> 2. 定义一个UserServiceImpl实现UserService,并完成相关功能,且统计每个功能的耗时
> 3. 定义测试类,创建实现类对象,调用方法

packagecom.shouyeren.proxy;publicinterfaceUserService{Stringlogin(String loginName,String passWord);voiddeleteUsers();StringselectUsers();}


packagecom.shouyeren.proxy;publicclassUserServiceImplimplementsUserService{@OverridepublicStringlogin(String loginName,String passWord){String rs ="登录名和密码错误";if("admin".equals(loginName)&&"132456".equals(passWord)){
rs ="登录成功";}try{Thread.sleep(1000);}catch(Exception e){
e.printStackTrace();}return rs;}@OverridepublicvoiddeleteUsers(){System.out.println("正在删除用户数据。。。");try{Thread.sleep(3000);}catch(InterruptedException e){thrownewRuntimeException(e);}}@OverridepublicStringselectUsers(){String rs ="正在查询10000个用户数据。。。";try{Thread.sleep(2500);}catch(Exception e){
e.printStackTrace();}return rs;}}


packagecom.shouyeren.proxy;importjava.lang.reflect.Proxy;publicclassProxyUtil{publicstaticUserServicegetProxy(UserService obj){return(UserService)Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),(proxy, method, args)->{long startTime =System.currentTimeMillis();Object rs = method.invoke(obj, args);long endTime =System.currentTimeMillis();System.out.println(method.getName()+"方法耗时:"+(endTime-startTime)/1000+"S");return rs;});}}


packagecom.shouyeren.proxy;publicclassTest{publicstaticvoidmain(String[] args){UserService userService =ProxyUtil.getProxy(newUserServiceImpl());System.out.println(userService.login("admin","132456"));System.out.println(userService.selectUsers());
userService.deleteUsers();}}

```

动态代理的优点:

  • 可以在不改变方法源码的情况下,实现对方法功能的增加,提高了代码的复用。
  • 简化了编程 工作、提高了开发效率、同时提高了软件系统的可扩展性
  • 可以为被代理对象的所有方法做代理 - 非常的灵活,支持任意接口类型的实现类对象做代理,也可以直接为接口本身做代理

在这里插入图片描述

感觉不错的话,动手点个赞吧!

标签: 单元测试 junit java

本文转载自: https://blog.csdn.net/shouyeren_st/article/details/129403840
版权归原作者 守夜人st 所有, 如有侵权,请联系我们删除。

“单元测试、反射、注解、动态代理”的评论:

还没有评论