欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 培训 > 16 Junit单元测试框架、反射、注解、动态代理(黑马Java视频笔记)

16 Junit单元测试框架、反射、注解、动态代理(黑马Java视频笔记)

2025/4/2 2:23:30 来源:https://blog.csdn.net/m0_61625388/article/details/146602637  浏览:    关键词:16 Junit单元测试框架、反射、注解、动态代理(黑马Java视频笔记)

在这里插入图片描述

文章目录

    • 单元测试
      • `Junit单元测试`框架
        • 1、认识
        • 2、使用步骤
          • 1) 先定义一个工具类
          • 2)编写测试类
          • ⭐测试方法:
          • ⭐断言测试:
    • 反射
      • 1. 认识反射、获取类
        • 1.1 获取Class对象
      • 2. ⭐获取类中的成分、并对其进行操作
        • 2.1 获取构造器并操作
        • 2.2 获取类的成员变量并操作
        • 2.3 获取类的成员方法并操作
      • 3. 作用、应用场景
        • 3.1 作用
        • 3.2 反射做通用框架
          • 1)通用方法:接收任意对象,并存储对象信息
          • 2)定义学生类和老师类
          • 3)定义测试类:创建对象,并调用存储对象的方法
    • 注解
      • 1. 概述、自定义注解
        • 1.1 注解
        • 1.2 自定义注解
          • 1)使用
          • 2)原理
      • 2. 元注解
      • 3. 注解的解析
        • 3.1 如何解析注解
          • 1)注解MyTest4
          • 2)Demo类
          • 3)定义AnnotationTest3测试类
      • 4. 作用、应用场景
        • 4.1 作用
        • 4.2 应用场景
        • 4.3 理解注解的属性
    • 动态代理
      • 1. 什么是代理
        • 1.1 如何为Java对象创建一个代理对象?
          • 1) Star类和StarService接口
          • 2)代理类
      • 2. 解决实际问题、掌握使用代理的好处
        • 1)用户类和用户管理类(接口-实现)
        • 2)使用代理类,记录方法耗时情况
        • 3)使用代理执行方法

单元测试

单元测试

  • 针对最小的功能单元:方法,编写测试代码对其进行正确性测试

之前在main方法中测试代码,无法实现自动化测试,一个方法测试失败,可能影响其他方法的测试,也无法得到测试的报告

Junit单元测试框架

1、认识
  • 可以用来对方法进行测试,是第三方公司开源出来的
  • 可以灵活编写测试代码,可以针对某个方法执行测试,也支持一键完成对全部方法的测试
  • 独立测试,一个测试不会影响另一个方法的测试
2、使用步骤

需求

  • 某个系统,有多个业务方法,请使用Junit单元测试框架,编写测试代码,完成对这些方法的正确性测试

1) 先定义一个工具类
/*** 字符串工具类*/
public class StringUtil {public static void printNumber(String name){if(name == null){System.out.println("名字不能为空");return ;}System.out.println("名字长度是:"+name.length());}/*** 求字符串的最大索引*/public static int getMaxIndex(String data){if(data == null || "".equals(data)){return -1;}return data.length() -1;}
}
2)编写测试类
⭐测试方法:
  • 必须是公开public,无参,无返回值
  • 测试方法必须加上@Test注解
// 测试类:junit单元测试框架,对业务类中的业务方法进行正确性测试
public class StringUtilTest {@Testpublic void testPrintNumber() {// 测试步骤:StringUtil.printNumber("张无忌");  // 预期结果:3// 测试用例StringUtil.printNumber("");StringUtil.printNumber(null);}@Testpublic void testGetMaxIndex() {// 测试步骤:int index = StringUtil.getMaxIndex("abcdefg");  // 预期结果:6// 测试用例int index2 = StringUtil.getMaxIndex("");int index3 = StringUtil.getMaxIndex(null);System.out.println(index);System.out.println(index2);System.out.println(index3);// 断言测试:断言结果是否与预期结果一致Assert.assertEquals("本轮测试失败业务获取的最大索引有问题,请检查",6, index);Assert.assertEquals("本轮测试失败业务获取的最大索引有问题,请检查",-1, index2);Assert.assertEquals("本轮测试失败业务获取的最大索引有问题,请检查",-1, index3);}
}
⭐断言测试:

断言结果是否与预期结果一致,有时候没有报错,但是可能不是预期的结果 → 黄色警告


反射

反射

1. 认识反射、获取类

反射

  • 加载类,并允许以编程的方式解剖内中的成分(成员变量、方法、构造器等)
    • 访问有关已加载类的字段、方法和构造函数的信息,以及使用反射字段,方法和构造函数在封装和安全限制内对其底层对应项进行操作
1.1 获取Class对象

  • Student类
@Data
public class Student {private String name;private int age;private double score;private String sex;
}
  • 测试方法
public static void main(String[] args) throws Exception {// 目标:掌握反射第一步操作:获取Class对象(获取类本身)// 1、获取类本身:类.classClass cls = Student.class;System.out.println(cls);    // class com.study.demo2reflect.StudentString className = cls.getSimpleName(); // 获取类简称System.out.println(className);  // StudentString fullName = cls.getName();System.out.println(fullName);   // 获取类全名 com.study.demo2reflect.Student// 2、获取类本身:Class.forName("类全名")Class cls2 = Class.forName("com.study.demo2reflect.Student");System.out.println(cls2);// 3、获取类本身:对象.getClass()Student s = new Student();Class cls3 = s.getClass();System.out.println(cls3);System.out.println(cls == cls2);	// trueSystem.out.println(cls == cls3);	// true
}

2. ⭐获取类中的成分、并对其进行操作

💡关系反转

🤔个人理解:之前是拿着类对象去访问该类的成员变量、构造器以及成员方法;反射是获取类中成分后,就是拿着成分去找对象操作自己可以操作的部分

2.1 获取构造器并操作

  • Dog类
public class Dog {private String name;private int age;private Dog() {}public Dog(String name, int age) {this.name = name;this.age = age;}public void eat(String food) {System.out.println(this.name + "在吃" + food);}public void run() {System.out.println(this.name + "在跑");}@Overridepublic String toString() {return "Dog{" +"name='" + name + '\'' +", age=" + age +'}';}
}
  • 测试方法
// 2、获取类的构造器对象并对其进行操作
@Test
public void getConstructor() throws Exception {// 目标:获取类的构造器对象并对其进行操作// 1、反射第一步:获取Class对象Class cls = Dog.class;// 2、获取构造器对象Constructor[] cons = cls.getDeclaredConstructors(); //只要有就拿,不管权限for(Constructor con:cons){System.out.println(con.getName()+"("+con.getParameterTypes()+")");}// 3、获取单个构造器Constructor con = cls.getDeclaredConstructor(); // 获取无参构造器System.out.println(con.getName()+"("+con.getParameterCount()+")");Constructor con2 = cls.getConstructor(String.class,int.class);  // 获取有参构造器System.out.println(con2.getName()+"("+con2.getParameterCount()+")");System.out.println("====================================================");// 4、通过获取的构造器创建对象// 无参构造器是私有方法 → 暴力反射:可以绕过访问权限con.setAccessible(true);Dog d1 = (Dog) con.newInstance();System.out.println(d1.toString());Dog d2 = (Dog) con2.newInstance("旺财", 5);System.out.println(d2.toString());}

2.2 获取类的成员变量并操作

// 3、获取类的成员变量对象并对其进行操作
@Test
public void getFieldInfo() throws Exception {// 1、获取Class对象Class cls = Dog.class;// 2、获取成员变量对象Field[] fields = cls.getDeclaredFields();   // 只要有就拿,不管权限for(Field field:fields){System.out.println(field.getName() + "(" + field.getType().getName() + ")");}System.out.println("========================================");// 3、获取单个成员变量对象Field field = cls.getDeclaredField("name");System.out.println(field.getName() + "(" + field.getType().getName() + ")");// 4、通过获取的成员变量对象操作成员变量Dog d = new Dog("旺财", 5);System.out.println(d);field.setAccessible(true);      // 暴力反射:可以绕过访问权限field.set(d, "大黄");System.out.println(d);
}

在这里插入图片描述

2.3 获取类的成员方法并操作

在这里插入图片描述

// 4、获取类的成员方法对象并对其进行操作
@Test
public void getMethodInfo() throws Exception {// 1、获取Class对象Class cls = Dog.class;// 2、获取成员方法对象Method[] methods = cls.getDeclaredMethods();   // 只要有就拿,不管权限for(Method method:methods){System.out.println(method.getName() + "(" + method.getParameterCount() + ")");}System.out.println("========================================");// 3、获取单个成员方法对象Method eat = cls.getDeclaredMethod("eat", String.class);System.out.println(eat.getName() + "(" + eat.getParameterCount() + ")");Method run = cls.getDeclaredMethod("run");System.out.println(run.getName() + "(" + run.getParameterCount() + ")");// 4、通过获取的成员方法对象操作成员方法Dog d = new Dog("旺财", 5);eat.invoke(d, "骨头");run.invoke(d);
}

在这里插入图片描述

3. 作用、应用场景

3.1 作用
  • 可以得到一个类的全部成分然后操作
  • 可以破坏封装性(私有的那些)(单例不单例,私有不私有)
  • 可以绕过泛型的约束
public static void main(String[] args) throws Exception {// 目标:反射的基本作用ArrayList<String> list = new ArrayList<>();list.add("hello");list.add("world");list.add("java");
//        list.add(34);         // 报错,泛型类型,只能放指定的类型// 通过反射绕过泛型的约束Class cls = list.getClass();Method add = cls.getDeclaredMethod("add",Object.class);add.invoke(list,34);add.invoke(list,true);System.out.println(list);	// [hello, world, java, 34, true]}

在这里插入图片描述

  • 最重要:适合做Java的框架,基本上,主流的框架都会基于反射设计出一些通用的功能
3.2 反射做通用框架

在这里插入图片描述
在这里插入图片描述

1)通用方法:接收任意对象,并存储对象信息
public class SaveObjectFrameWork {// 保存任意对象的静态方法public static void saveObject(Object obj) throws Exception {// 使用打印流,包装可以追加字节输出流PrintStream ps = new PrintStream(new FileOutputStream("F:\\object.txt",true));// 1. 获取字节码文件对象Class cls = obj.getClass();String className = cls.getSimpleName();ps.println("==============="+className+"=========================");// 2. 获取全部成员变量Field[] fields = cls.getDeclaredFields();for(Field field:fields){// 获取成员变量的名字和值field.setAccessible(true);String fieldName = field.getName();Object fieldValue = field.get(obj) + ""; // 转换为字符串System.out.println(fieldName + "=" + fieldValue);ps.println(fieldName + "=" + fieldValue);}ps.close();}
}
2)定义学生类和老师类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {private String name;private int age;private double score;private String sex;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Teacher {private String name;private int age;private double salary;
}
3)定义测试类:创建对象,并调用存储对象的方法
public class ReflectDemo4 {public static void main(String[] args) {Student s1 = new Student("小明", 14, 90, "男");Teacher t1 = new Teacher("张老师",26, 5000);SaveObjectFrameWork sof = new SaveObjectFrameWork();try {sof.saveObject(s1);sof.saveObject(t1);} catch (Exception e) {e.printStackTrace();}}
}

在这里插入图片描述


注解

注解

1. 概述、自定义注解

1.1 注解
  • Java代码里的特殊标记,比如:@Override、@Test等

  • 让其他程序根据注解信息来决定怎么执行该程序

  • 可以用在类上、构造器上、方法上、成员变量上、参数上等

1.2 自定义注解
1)使用

在这里插入图片描述

  • 自定义注释
public @interface A {String value();// 特殊属性:默认在使用时,如果只有一个value属性,那么value属性可以省略不写
}public @interface B {String value();// 特殊属性:默认在使用时,如果只有一个value属性,那么value属性可以省略不写String hobby() default "打游戏";
}public @interface MyBook {String name();int age() default 18;String[] address();
}
  • 使用注释
@MyBook(name = "leo", age = 20, address = {"北京", "上海"})
//@A(value = "leo")
@A("leo") //  如果只有一个value属性,那么value属性可以省略不写
@B("lay")
public class AnnotationDemo1 {@MyBook(name = "lay", age = 28, address = {"北京", "上海"})public static void main(@A("why") String[] args) {// 目标:自定义注解@A("what")int a;}
}
2)原理

在这里插入图片描述

2. 元注解

指的是注解注解的注解:放在注解上的注解

@target:约束注解的范围

Retention:声明注解的保留周期,一般都是保留到编译运行时RUNTIME
在这里插入图片描述

@Retention(RetentionPolicy.RUNTIME)     // 注解的保留策略
@Target({ElementType.CONSTRUCTOR,ElementType.FIELD})    // 限制注解的声明位置
public @interface StudentLimit {int minAge() default 18;
}

3. 注解的解析

判断类上、方法上、成员变量上是否存在注解,并把注解里的内容给解析出来

3.1 如何解析注解
  • 要解析谁的注解,就应该先拿到谁
    • Class、Method、Field、Constructor、都实现了AnnotatedElement接口,他们都拥有解析注解的能力


1)注解MyTest4
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest4 {String value();double aaa() default 100;String[] bbb();
}
2)Demo类
@MyTest4(value = "aaa", aaa = 100, bbb = {"aaa","bbb"})
public class Demo {@MyTest4(value = "bbb",bbb={"aaa","bbb"})public void test1(){}}
3)定义AnnotationTest3测试类
  • 解析类的注释
public static void main(String[] args) throws Exception {// 目标:解析注释parseClass();parseMethod();
}public static void parseClass() {// 1. 获取类对象Class cls = Demo.class;// 2. 获取注解对象:先判断是否使用了该注解,再获取注解对象if (cls.isAnnotationPresent(MyTest4.class)) {// 3. 获取注解对象MyTest4 myTest4 = (MyTest4) cls.getAnnotation(MyTest4.class);// 4. 获取注解的属性值String value = myTest4.value();double aaa = myTest4.aaa();String[] bbb = myTest4.bbb();// 5. 打印System.out.println(value);      // aaaSystem.out.println(aaa);    // 100.0System.out.println(Arrays.toString(bbb));   // [aaa, bbb]}
}
  • 解析成员方法的注释
public static void parseMethod() throws Exception {// 1. 获取类对象Class cls = Demo.class;// 2、获取方法对象Method method = cls.getDeclaredMethod("test1");// 3、判断方法是否使用了指定的注解if(method.isAnnotationPresent(MyTest4.class)){// 4、获取注解对象MyTest4 myTest4 = method.getAnnotation(MyTest4.class);// 5、获取注解的属性值String value = myTest4.value();double aaa = myTest4.aaa();String[] bbb = myTest4.bbb();// 6、打印System.out.println(value);  // bbbSystem.out.println(aaa);    // 100.0System.out.println(Arrays.toString(bbb));   // [aaa, bbb]}
}

4. 作用、应用场景

4.1 作用
  • 对程序做特殊标记,以方便别人针对性的处理
4.2 应用场景

需求

  • 定义若干个方法,只要加了MyTest注解,就会触发该方法执行

分析

① 定义一个自定义注解MyTest,只能注解方法,存活范围是一直都在

② 定义若干个方法,部分方法加上@MyTest

public class AnnotationDemo4 {public static void main(String[] args) throws Exception{AnnotationDemo4 ad = new AnnotationDemo4();// 1.获取类对象Class cls = ad.getClass();// 2.获取存在的方法Method[] methods = cls.getDeclaredMethods();// 3.遍历方法,判断方法上是否使用了指定的注解for(Method method: methods){// 判断方法上是否使用了指定的注解if(method.isAnnotationPresent(MyTest4.class)){// 4.如果有该注解就执行方法method.invoke(ad);}}}@MyTest4(value = "aaa", aaa = 100, bbb = {"aaa","bbb"})public void test1(){System.out.println("test1方法执行啦~");}public void test2(){System.out.println("test2方法执行啦~");}public void test3(){System.out.println("test3方法执行啦~");}@MyTest4(value = "bbb",bbb={"aaa","bbb"})public void test4(){System.out.println("test4方法执行啦~");}
}// test1方法执行啦~
// test4方法执行啦~
4.3 理解注解的属性

对该成分做特殊处理:例如多次执行某一方法
在这里插入图片描述


动态代理

动态代理

1. 什么是代理

在这里插入图片描述

1.1 如何为Java对象创建一个代理对象?

在这里插入图片描述

1) Star类和StarService接口

在这里插入图片描述

2)代理类

通过需代理任务的接口连接代理对象实际执行任务的对象

  • 创建代理对象方法的返回类型:StarService接口对象
/*** 代理工具类:专门负责创建代理对象并返回给别人使用*/
public class ProxyUtil {// 创建一个明星对象的代理对象public static StarService createProxy(Star s) {/*** 参数一:用于执行用哪个类加载器去加载生成的代理类* 参数二:用于指定代理类需要实现的接口* 参数三:用于指定代理对象需要调用哪个类中的方法*/StarService proxy = (StarService) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),s.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 用来声明代理对象要干的事// 参数一:proxy接收到代理对象本身(暂时用处不大)// 参数二:method接收到代理对象调用的方法// 参数三:args接收到代理对象调用的方法的参数System.out.println("代理对象开始工作了...");String methodName = method.getName();if("sing".equals(methodName)){System.out.println("准备话筒,收费");} else if ("dance".equals(methodName)) {System.out.println("准备场地,收费");}// 找真正的明星对象来执行被代理的行为:method方法Object result = method.invoke(s, args);return result;}});return proxy;}
}

在这里插入图片描述

2. 解决实际问题、掌握使用代理的好处

在这里插入图片描述

1)用户类和用户管理类(接口-实现)

在这里插入图片描述

/*** 用户业务实现类*/
public class UserServiceImpl implements UserService{@Overridepublic void login(String loginName, String password) throws Exception {
//        long start = System.currentTimeMillis(); // 记录开始时间 1970年1月1日0时0分0秒0毫秒 走到此刻的总毫秒值if("admin".equals(loginName) && "123".equals(password)){System.out.println("登录成功");}else{System.out.println("登录失败");}Thread.sleep(1000);//        long end = System.currentTimeMillis();
//        System.out.println("login方法耗时:"+(end-start)/1000.0+"秒");}@Overridepublic void delete(String id) throws Exception {
//        long start = System.currentTimeMillis();System.out.println("删除用户成功");Thread.sleep(1500);//        long end = System.currentTimeMillis();
//        System.out.println("delete方法耗时:"+(end-start)/1000.0+"秒");}@Overridepublic String[] selectUsers() throws Exception{
//        long start = System.currentTimeMillis();System.out.println("查询用户成功");String[] names = {"张三","李四","王五","赵六","孙七"};Thread.sleep(500);//        long end = System.currentTimeMillis();
//        System.out.println("selectUsers方法耗时:"+(end-start)/1000.0+"秒");return names;}
}

🤔记录方法耗时情况的代码具有很大的重复性,且不太适合归类到用户管理类的方法中

2)使用代理类,记录方法耗时情况
public class ProxyUtil {public static UserService createProxy(UserService userService) {UserService proxy = (UserService) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),userService.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("代理对象开始工作了...");long start = System.currentTimeMillis();// 真正调用业务对象被代理的方法Object result = method.invoke(userService, args);long end = System.currentTimeMillis();System.out.println(method.getName() + "耗时:" + (end - start) / 1000.0 + "秒");System.out.println("代理对象结束了工作了...");return result;}});return proxy;}
}
3)使用代理执行方法
public static void main(String[] args) throws Exception {// 1、创建用户业务对象UserService userService = new UserServiceImpl();// 创建代理对象userService = ProxyUtil.createProxy(userService);// UserService userService = ProxyUtil.createProxy(new UserServiceImpl());// 2、调用用户业务的功能userService.login("admin", "123");userService.delete("1");String[] names = userService.selectUsers();System.out.println(Arrays.toString(names));
}

在这里插入图片描述

💡如果要让该代理可以接收任意对象,测试任意对象的方法运行耗时,只需将对象限定改为泛型即可
在这里插入图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词