Day13、单元测试-动态代理
Day13、单元测试-动态代理
课程安排
单元测试
单元测试
-
单元测试就是针对最小的功能单元编写测试代码, 归姹程序最小的功能单元是方法, 因此,单元测试就是针对归java方法的测试,进而检查方法的正确性。
目前测试方法是怎么进行的, 存在什么问题
-
只有一个main 方法, 如果一个方法的测试失败了, 其他方法测试会受到影响。
-
无法得到测试的结果报告, 需要程序员自己去观察测试是否成功。
-
无法实现自动化测试。
Junit单元测试框架
-
JUnit 是使用归va 语言实现的单元测试框架, 它是开源的, 扫va 开发者都应当学习并使用Junit 编写单元测试。
-
此外,几乎所有的IDE工具都集成了JUnit , 这样我们就可以直接在IDE 中编写并运行JUnit 测试,JUnit 目前最新版本是5 。
JUnit 优点
-
JUnit 可以灵活的选择执行哪些测试方法, 可以一键执行全部测试方法。
-
Junit 可以生成全部方法的测试报告。
-
单元测试中的某个方法测试失败了, 不会影响其他测试方法的测试。
总结:
单元测试快速入门
Junit常用注解(Junit 4.xxxx版本)
| 注解 | 说明 |
|---|---|
| @Test | 测试方法 |
| @Before | 用来修饰实例方法, 该方法会在每一个测试方法执行之前执行一次。 |
| @After | 用来修饰实例方法, 该方法会在每一个测试方法执行之后执行一次。 |
| @BeforeClass | 用来静态修饰方法, 该方法会在所有测试方法之前只执行一次。 |
| @AfterClass | 用来静态修饰方法, 该方法会在所有测试方法之后只执行一次。 |
-
开始执行的方法: 初始化资源。
-
执行完之后的方法: 释放资源。
Junit常用注解(Junit 5.xxxx版本)
| 注解 | 说明 |
|---|---|
| @Test | 测试方法 |
| @BeforeEach | 用来修饰实例方法, 该方法会在每一个测试方法执行之前执行一次。 |
| @AfterEach | 用来修饰实例方法, 该方法会在每一个测试方法执行之后执行一次。 |
| @BeforeAll | 用来静态修饰方法, 该方法会在所有测试方法之前只执行一次。 |
| @AfterAll | 用来静态修饰方法, 该方法会在所有测试方法之后只执行一次。 |
-
开始执行的方法: 初始化资源。
-
执行完之后的方法: 释放资源。
反射
反射概述
-
反射是指对于任何一个Class 类, 在" 运行的时候" 都可以直接得到这个类全部成分。
-
在运行时, 可以直接得到这个类的构造器对象: Constructor
-
在运行时, 可以直接得到这个类的成员变量对象: Field
-
在运行时, 可以直接得到这个类的成员方法对象: Method
-
这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制。
反射的关键:
-
反射的第一步都是先得到编译后的Class类对象, 然后就可以得到Class的全部成分。
HelloWorld.java 一> Javac 一> HelloWorld.class
Class c = HelloWorld.class;
总结:
反射的第一步:获取Class类的对象
总结:
//方法一:class类中的一个静态方法。forname(全限名:包名+类名)
Class c1 = Class.forName("com.cafune.Day02_reflect_class.Student");
System.out.println(c1);
//方法二:类名.class
Class c2 = Student.class;
System.out.println(c2);
//方法三:对象.getClass(),获取对象对应类的Class对象
Student s = new Student();
Class c3 = s.getClass()
System.out.println(c3);
使用反射技术获取构造器对象并使用
反射的第一步是先得到类对象, 然后从类对象中获取类的成分对象。
CIass 类中用于获取构造器的方法
| 方法 | 说明 |
|---|---|
| Constructor <?> [] getConstructors() | 返回所有构造器对象的数组( 只能拿public的) |
| Constructor<?> [] getDeclaredConstructors() | 返回所有构造器对象的数组, 存在就能拿到 |
| Constructor |
返回单个构造器对象( 只能拿public的) |
| Constructor |
返回单个构造器对象, 存在就能拿到 |
@Test
public void getConstructors() throws Exception {
Class c1 = Class.forName("com.cafune.Day03_reflect_constructor.Student");
/*
Class c2 = Student.class;
Student s = new Student();
Class c3 = s.getClass();
*/
Constructor[] constructors = c1.getConstructors();//只能拿punlic的构造器
for (Constructor constructor : constructors) {
System.out.println(constructor.getName() + "===>"
+ constructor.getParameterCount());
}
}
@Test
public void getDeclaredConstructors() throws Exception {
Class c1 = Class.forName("com.cafune.Day03_reflect_constructor.Student");
/*
Class c2 = Student.class;
Student s = new Student();
Class c3 = s.getClass();
*/
Constructor[] declaredConstructors = c1.getDeclaredConstructors();//我全都要
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor.getName() + "===>"
+ declaredConstructor.getParameterCount());
}
}
@Test
public void getConstructor() throws Exception{
Class c1 = Class.forName("com.cafune.Day03_reflect_constructor.Student");
Constructor con = c1.getConstructor();
System.out.println(con.getName() + "===>"
+ con.getParameterCount());
}
@Test
public void getDeclaredConstructor() throws Exception{
Class c1 = Class.forName("com.cafune.Day03_reflect_constructor.Student");
Constructor dcons = c1.getDeclaredConstructor();
System.out.println(dcons.getName() + "===>"
+ dcons.getParameterCount());
}
@Test
public void getDeclaredConstructor1() throws Exception{
Class c1 = Class.forName("com.cafune.Day03_reflect_constructor.Student");
Constructor dcons1 = c1.getDeclaredConstructor(String.class,int.class);
System.out.println(dcons1.getName() + "===>"
+ dcons1.getParameterCount());
}
使用反射技术获取构造器对象并使用
-
获取构造器的作用依然是初始化一个对象返回。
Constructor类中用于创建对象的方法
| 符号 | 说明 |
|---|---|
| T newInstance(Object... lnitargs) | 根据指定的构造器创建对象 |
| public void setAccessible(boolean flag) | 设置为true , 表示取消访问检查, 进行暴力反射 |
public class TestStudentDemo02 {
//调用无参构造器得到一个类的对象返回
@Test
public void getDeclaredConstructor1() throws Exception{
//a.获取类对象
Class c1 = Class.forName("com.cafune.Day03_reflect_constructor.Student");
//b.定位单个构造器对象(按照参数定位无参数构造器)
Constructor cons = c1.getDeclaredConstructor();
System.out.println(cons.getName() + "===>"
+ cons.getParameterCount());
//如果遇到私有构造器可以暴力反射
cons.setAccessible(true);//权限被打开
Student s = (Student) cons.newInstance();
System.out.println(s);
System.out.println("-------------------");
//定位某个有参构造器
Constructor cons1 = c1.getDeclaredConstructor(String.class, int.class);
System.out.println(cons1.getName() + "===>"
+ cons1.getParameterCount());
Student s1 = (Student) cons1.newInstance("cafune", 22);
System.out.println(s1);
}
}
总结:
使用反射技术获取成员变量对象并使用
-
反射的第一步是先得到类对象, 然后从类对象中获取类的成分对象。
-
Class 类中用于获取成员变量的方法
| 方法 | 说明 |
|---|---|
| FieId[] getFields() | 返回所有成员变量对象的数组( 只能拿public 的) |
| Field[] getDeclaredFieIds() | 返回所有成员变量对象的数组, 存在就能拿到 |
| Field getField(String name) | 返回单个成员变量对象( 只能拿public的) |
| Field getDeclaredField(String name) | 返回单个成员变量对象, 存在就能拿到 |
使用反射技术获取成员变量对象并使用
-
获取成员变量的作用依然是在某个对象中取值、赋值
Field类中用于取值、赋值的方法
| 符号 | 说明 |
|---|---|
| void set(Object obj, Object value) | 赋值 |
| Object get(Object obj) | 取值 |
public void getDeclaredField() throws Exception {
//a.定位class对象
Class c = Student.class;
//b.定位某个成员变量
Field ageF = c.getDeclaredField("age");
System.out.println(ageF.getName() + "==>" + ageF.getType());
ageF.setAccessible(true);//暴力取值
//c.赋值
Student s = new Student();
ageF.set(s, 18);
System.out.println(s);
//d.取值
int age = (int) ageF.get(s);
System.out.println(age);
}
使用反射技术获取方法对象并使用

-
反射的第一步是先得到类对象, 然后从类对象中获取类的成分对象。
-
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对象调用该方法 参数二:调用方法的传递的参数(没有就不写) 返回值:方法的返回值(没有就不写) |
//1.获取类中的所有成员方法对象
@Test
public void getDEclareMethods() {
//a.获取类对象
Class c = Dog.class;
//b.获取全部方法,包括私有的
Method[] methods = c.getDeclaredMethods();
//c.遍历全部方法
for (Method method : methods) {
System.out.println(method.getName() + "返回值类型:" +
method.getReturnType() + "参数个数:" +
method.getParameterCount());
}
}
//2.获取类中的某个方法对象
@Test
public void getDEclareMethod() throws Exception {
//a.获取类对象
Class c = Dog.class;
//b.获取特定方法,包括私有的
Method m = c.getDeclaredMethod("eat");
Method m1 = c.getDeclaredMethod("eat",String.class);
m.setAccessible(true);
m1.setAccessible(true);
//c.触发方法的执行
Dog d = new Dog();
//如果方法没有结果回来,那么返回的是null
Object result = m.invoke(d);
System.out.println(result);
Object result1 = m1.invoke(d,"🥩");
System.out.println(result1);
}
总结:
1.利用反射技术获取成员方法对象的方法
获取类中成员方法对象
-
Method[] getDeclaredMethods()
-
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
2.反射得到成员方法可以做什么?
依然是在某个对象中触发该方法执行
-
Object invoke(Object obj,Object...args)
如果某成员方法是非public的,需要打开权限(暴力反射),然后再触发执行
-
xx.setAccessible(boolean);
反射的作用:绕过编译阶段为集合添加数据
-
反射是作用在运行时的技术, 此时集合的泛型将不能产生约束了, 此时是可以为集合存入其他任意类型的元素的。
ArrayList<Integer> list = new ArrayList<>();
list.add(100);
//list.add( "黑马" );//报错
list.add(99);
-
泛型只是在编译阶段可以约束集合只能操作某种数据类型, 在编译成Class 文件进入运行阶段的时候, 其真实类型都是ArrayList 了, 泛型相当于被擦除了。
总结:

案例:反射做通用框架


总结:

注解
注解概述、作用
-
Java 注解(Annotation) 又称Java 标注, 是JDK5.0 引入的一种注释机制。
-
Java 语言中的类、构造器、方法、成员变量、参数等都可以被注解进行标注。
public class UserServiceTest {
@Test //->注解
public void testLogin(){
@Test //->注解
public void testChu(){
}
}
注解的作用是什么呢?
-
对JAVA中类、方法、成员变量做标记, 然后进行特殊处理, 至于到底做何种处理由业务需求来决定。
-
例如: JUnit 框架中, 标记了注解@Test 的方法就可以被当成测试方法执行, 而没有标记的就不能当成测试方法执行。
自定义注解---格式
-
自定义注解就是自己做一个注解来使用
public @interface 注解名称{
public 属性名称 属性名() default 默认值;
}
特殊属性
-
value 属性, 如果只有一个value 属性的情况下, 使用value 属性的时候可以省略value 名称不写! !
-
但是如果有多个属性, 且多个属性没有默认值, 那么value 名称是不能省略的。
元注解
元注解: 就是注解注解的注解。
元注解有两个:
@Target: 约束自定义注解只能在哪些地方使用,
@Retention : 申明注解的生命周期

总结:

注解的解析
-
注解的操作中经常需要进行解析, 注解的解析就是判断是否存在注解, 存在注解就解析出内容。
与注解解析相关的接囗
Annotation: 注解的顶级接口, 注解都是Annotation 类型的对象
AnnotatedElement : 该接口定义了与注解解析相关的解析方法
| 方法 | 说明 |
|---|---|
| Annotation[] getDeclaredAnnotations() | 获得当前对象上使用的所有注解, 返回注解数组。 |
| T getDecIaredAnnotation(CIass |
根据注解类型获得对应注解对象 |
| boolean isAnnotationPresent(Class |
判断当前对象是否使用了指定的注解, 如果使用了则返回true, 否则false |
-
所有的类成分Class , Method Field ,Constructor, 都实现了AnnotatedE [ ement 接口他们都拥有解析注解的能力:
-
![image-20220525160251523]()
案例:注解解析的案例

public class AnnotationDemo04 {
@Test
public void parseMethod() throws Exception {
//1.获取类对象
Class c = BookStores.class;
Method m = c.getDeclaredMethod("test");
//2.判断这个类上是否存在这个注释
if (m.isAnnotationPresent(BOOKS.class)){
//3.直接获取该注解对象
//获取m方法里面的注解 定为book
BOOKS book = (BOOKS) m.getDeclaredAnnotation(BOOKS.class);
System.out.println(book.value());
System.out.println(book.price());
System.out.println(Arrays.toString(book.authors()));
}
}
}
@BOOKS(value = "《情深深雨蒙蒙》",price = 99.9,authors = {"琼瑶","xiaoding"})
class BookStores{
@BOOKS(value = "《三少爷的🗡》",price = 10000,authors = {"古龙","熊耀华"})
public void test(){}
}
案例:模拟Junit框架

public class AnnotationDemo05 {
@Mytest
public void test1(){
System.out.println("======test1======");
}
public void test2(){
System.out.println("======test2======");
}
@Mytest
public void test3(){
System.out.println("======test3======");
}
//启动菜单:有注解的才被调用
public static void main(String[] args) throws Exception{
AnnotationDemo05 a5 = new AnnotationDemo05();
//a.获取类对象
Class c = AnnotationDemo05.class;
//b.提取全部方法
Method[] methods = c.getDeclaredMethods();
//c.遍历方法,看是否有mytest注解,有就跑它
for (Method method : methods) {
if (method.isAnnotationPresent(Mytest.class)){
method.invoke(a5);
}
}
}
}
动态代理(重难点)
如何创建代理对象
-
java中代理的代表类是:Java.lang.reflect.Proxy
-
Proxy提供了一个静态方法,用于为对象产生一个代理对象返回
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
/*为对象返回一个代理对象。
参数一:定义代理类的类加载器
参数二:代理类要实现的接口列表
参数三:将方法调用分派到调用的处理程序(代理对象的核心处理程序)
*/
JAVA中如何生成代理,并指定代理干什么事


总结:


案例:模拟企业业务功能开发,并完成每个功能的性能统计


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

浙公网安备 33010602011771号