Junit_反射_注解

总结:

junit--导入包--@Test,允许运行不用main

反射就是拆分类,建立框架只对类操作

单元测试

概述

单元测试

        单元测试就是针对最小的功能单元(方法),编写测试代码对该功能进行正确性测试。

当前测试方法存在问题:

        只能编写main方法,并在main方法中再去调用其他方法进行测试。

Junit单元测试框架

        JUnit是使用Java语言实现的单元测试框架,它是第三方公司开源出来的,很多开发工具已经集成了Junit框架,比如IDEA。

优点:编写的测试代码很灵活,可以指某个测试方法执行测试,也支持一键完成自动化测试。 不需要程序员去分析测试的结果,会自动生成测试报告出来。 提供了更强大的测试能力。

Junit单元测试快速入门

        需求:某个系统,有多个业务方法,请使用Junit框架完成对这些方法的单元测试。

        具体步骤:

                ①将Junit框架的jar包导入到项目中(注意:IDEA集成了Junit框架,不需要我们自己手工导入了)

                ②编写测试类、测试类方法(注意:测试方法必须是公共的,无参数,无返回值的非静态方法)

                ③必须在测试方法上使用@Test注解(标注该方法是一个测试方法)

                ④在测试方法中,编写程序调用被测试的方法即可。

                ⑤选中测试方法,右键选择“JUnit运行” ,如果测试通过则是绿色;如果测试失败,则是红色

                代码:

public class StringUtilJunit {

    //步骤②编写测试类、测试类方法(注意:测试方法必须是公共的,无参数,无返回值的非静态方法)
    @Test//步骤③必须在测试方法上使用@Test注解(标注该方法是一个测试方法)
    public void testPrintNumber(){
        //步骤④在测试方法中,编写程序调用被测试的方法即可。
        StringUtil.printNumber("admin");
        StringUtil.printNumber(null);
    }
    @Test
    public void testGetMaxIndex(){
        int index1 = StringUtil.getMaxIndex(null);
        System.out.println(index1);

        int index2 = StringUtil.getMaxIndex("admin");
        System.out.println(index2);
        //断言机制:可以通过预测业务方法的结果
//        Assert.assertArrayEquals("错误",4,index2);
        Assert.assertEquals("错误",4,index2);
    }
    
}

junit框架 常见注解

  开始执行的方法:初始化资源。

  执行完之后的方法:释放资源

反射(主要用于做框架)

认识反射、获取类

反射

        反射指的是允许以编程方式访问已加载类的成分(成员变量、方法、构造器等)。

1、反射第一步:获取类:Class

        获取Class对象的三种方式:

                ①、Class c1 = 类名.class

                ②、调用Class提供方法: public static Class forName(String package);

                ③、Object提供的方法: public Class getClass();Class c3 = 对象.getClass();

2、获取类的构造器:Constructor

3、获取类的成员变量:Field

4、获取类的成员方法:Method

代码:

①Class c1 = 类名.class

Class c1 = Student.class;
System.out.println(c1.getName()); // 全类名
System.out.println(c1.getSimpleName()); // 简名:Student

②、调用Class提供方法: public static Class forName(String package);

Class c2 = Class.forName("com.itheima.d2_reflect.Student");
System.out.println(c1 == c2);//true

③、Object提供的方法: public Class getClass();Class c3 = 对象getClass();

Student s = new Student();
Class c3 = s.getClass();
System.out.println(c3 == c2);

获取类的构造器

Class提供获取构造器的方法:

作用:初始化一个对象返回。

代码演示:

获取所有构造器:

@Test
public void testGetConstructors(){
    // 1、反射第一步:必须先得到这个类的Class对象
    Class c = Cat.class;
    // 2、获取类的全部构造器
    // Constructor[] constructors = c.getConstructors();
    Constructor[] constructors = c.getDeclaredConstructors();
    // 3、遍历数组中的每个构造器对象
    for (Constructor constructor : constructors) {
        System.out.println(constructor.getName() + "--->"
            + constructor.getParameterCount());
    }
}   

获取单个构造器:

@Test
public void testGetConstructor() throws Exception {
    // 1、反射第一步:必须先得到这个类的Class对象
    Class c = Cat.class;
    // 2、获取类的某个构造器:无参数构造器
    Constructor constructor1 = c.getDeclaredConstructor();
    System.out.println(constructor1.getName() + "--->"
                + constructor1.getParameterCount());
    constructor1.setAccessible(true); // 禁止检查访问权限,防止构造器私有导致无法创建对象
    Cat cat = (Cat) constructor1.newInstance();
    System.out.println(cat);

    AtomicInteger a;

    // 3、获取有参数构造器
    Constructor constructor2 =c.getDeclaredConstructor(String.class, int.class);//记得加.class,class指类型
    System.out.println(constructor2.getName() 
        + "--->"+ constructor2.getParameterCount());
    constructor2.setAccessible(true); // 禁止检查访问权限
    Cat cat2 = (Cat) constructor2.newInstance("叮当猫", 3);
    System.out.println(cat2);

    }

获取类的成员变量

Class提供获取成员变量方法

获取成员变量作用:赋值、取值。

代码演示:

public class Test3Field {
    @Test
    public void testGetFields() throws Exception {
        // 1、反射第一步:必须是先得到类的Class对象
        Class c = Cat.class;
        // 2、获取类的全部成员变量。
        Field[] fields = c.getDeclaredFields();
        // 3、遍历这个成员变量数组
        for (Field field : fields) {
            System.out.println(field.getName() +  "---> "+ field.getType());
        }
        // 4、定位某个成员变量
        Field fName = c.getDeclaredField("name");
        System.out.println(fName.getName() + "--->" + fName.getType());

        Field fAge = c.getDeclaredField("age");
        System.out.println(fAge.getName() + "--->" + fAge.getType());

        //成员变量不可以单独存在,所以需要先创建cat对象
        // 赋值
        Cat cat = new Cat();
        fName.setAccessible(true); // 禁止访问控制权限
        fName.set(cat, "卡菲猫");
        System.out.println(cat);

        // 取值
        String name = (String) fName.get(cat);
        System.out.println(name);
    }
}

获取类的成员方法

Class提供从类中获取成员方法的API

作用:依然是执行。

代码演示:

public class Test4Method {
    @Test
    public void testGetMethods() throws Exception {
        //  1、反射第一步:先得到Class对象。
        Class c = Cat.class;
        // 2、获取类的全部成员方法。
        Method[] methods = c.getDeclaredMethods();
        // 3、遍历这个数组中的每个方法对象
        for (Method method : methods) {
            System.out.println(method.getName() + "--->"
                    + method.getParameterCount() + "---->"
                    + method.getReturnType());
        }
        //  4、获取某个方法对象
        Method run = c.getDeclaredMethod("run"); // 拿run方法,无参数的
        System.out.println(run.getName() + "--->"
                + run.getParameterCount() + "---->"
                + run.getReturnType());

        Method eat = c.getDeclaredMethod("eat", String.class);
        System.out.println(eat.getName() + "--->"
                + eat.getParameterCount() + "---->"
                + eat.getReturnType());

        Cat cat = new Cat();
        run.setAccessible(true); // 禁止检查访问权限
        Object rs = run.invoke(cat); // 调用无参数的run方法,用cat对象触发调用的。
        System.out.println(rs);

        eat.setAccessible(true); // 禁止检查访问权限
        String rs2 = (String) eat.invoke(cat, "鱼儿");
        System.out.println(rs2);
    }
}

作用、应用场景

反射的作用

        基本作用:可以得到一个类的全部成分然后操作。

        可以破坏封装性。

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

使用反射做一个简易版框架

        需求:对于任意一个对象,该框架都可以把对象的字段名和对应的值,保存到文件中去。

 实现步骤:

        ①定义一个方法,可以接收任意对象。(老师、学生)

        ②每收到一个对象后,使用反射获取该对象的Class对象,然后获取全部的成员变量。

        ③遍历成员变量,然后提取成员变量在该对象中的具体值。

        ④把成员变量名、和其值,写出到文件中去即可。

public class ObjectFrame {
    // 目标:保存任意对象的字段和其数据到文件中去
    public static void saveObject(Object obj) throws Exception {
        PrintStream ps = new PrintStream(new FileOutputStream("junit-reflect-annotation-proxy-app\\src\\data.txt", true));
        // obj是任意对象,到底有多少个字段要保存。
        Class c = obj.getClass();
        String cName = c.getSimpleName();
        ps.println("---------------" + cName + "------------------------");
        // 2、从这个类中提取它的全部成员变量
        Field[] fields = c.getDeclaredFields();
        // 3、遍历每个成员变量。
        for (Field field : fields) {
            // 4、拿到成员变量的名字
            String name = field.getName();
            // 5、拿到这个成员变量在对象中的数据。
            field.setAccessible(true); // 禁止检查访问控制
            String value = field.get(obj) + "";
            ps.println(name + "=" + value);
        }
        ps.close();
    }
}
public class Test5Frame {
    @Test
    public void save() throws Exception {
        Student s1 = new Student("黑马吴彦祖", 45, '男', 185.3, "蓝球,冰球,阅读");
        Teacher t1 = new Teacher("播妞", 999.9);

        // 需求:把任意对象的字段名和其对应的值等信息,保存到文件中去。
        ObjectFrame.saveObject(s1);
        ObjectFrame.saveObject(t1);
    }
}

注解

概述、自定义注解

注解:

        就是Java代码里的特殊标记,比如: @Override、@Test等,作用是: 让其他程序根据注解信息来决定怎么执行该程序。

        Java 语言中的类、构造器、方法、成员变量、参数等都可以被注解进行标注。

自定义注解

        自己定义注解(定义的属性,使用时一定要赋值,特殊属性value可以不赋值)

注解原理

本质是一个接口,继承annotation

元注解

        修饰注解的注解。

        

元注解只有两个:

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

         TYPE,类,接口  

        FIELD, 成员变量

        METHOD, 成员方法

        PARAMETER, 方法参数

        CONSTRUCTOR, 构造器

        LOCAL_VARIABLE, 局部变量

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

        SOURCE: 注解只作用在源码阶段,生成的字节码文件中不存在  

        CLASS:  注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值.  

        RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段(开发常用)

注解的解析

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

Class、Method、Field,Constructor、都实现了AnnotatedElement接口,它们都拥有解析注解的能力。

解析注解案例,具体需求如下:

①定义注解MyTest4,要求如

                包含属性: String value()

                包含属性: double aaa(),默认值为 100

                包含属性: Stringll bbb()

                限制注解使用的位置:类和成员方法上

                指定注解的有效范围:一直到运行时

②定义一个类叫:Demo,在类中定义一个test1方法,并在该类和其方法上使用MyTest4注解

③定义AnnotationTest3测试类,解析Demo类中的全部注解。

MyTest4注解:

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest4 {
    String value();
    double aaa() default 100;
    String[] bbb();
}

Demo:

@MyTest4(value = "蜘蛛精", aaa=99.5, bbb = {"至尊宝", "黑马"})
@MyTest3
public class Demo {
    @MyTest4(value = "孙悟空", aaa=199.9, bbb = {"紫霞", "牛夫人"})
    public void test1(){
    }
}

注解解析:

 

public class AnnotationTest3 {
    @Test
    public void parseClass(){
        // 1、先得到Class对象
        Class c = Demo.class;
        // 2、解析类上的注解
        // 判断类上是否包含了某个注解
        if(c.isAnnotationPresent(MyTest4.class)){
            MyTest4 myTest4 =(MyTest4) c.getDeclaredAnnotation(MyTest4.class);
            System.out.println(myTest4.value());
            System.out.println(myTest4.aaa());
            System.out.println(Arrays.toString(myTest4.bbb()));
        }
    }

    @Test
    public void parseMethod() throws Exception {
        // 1、先得到Class对象
        Class c = Demo.class;
        Method m = c.getDeclaredMethod("test1");
        // 2、解析方法上的注解
        // 判断方法对象上是否包含了某个注解
        if(m.isAnnotationPresent(MyTest4.class)){
            MyTest4 myTest4 =
                    (MyTest4) m.getDeclaredAnnotation(MyTest4.class);
            System.out.println(myTest4.value());
            System.out.println(myTest4.aaa());
            System.out.println(Arrays.toString(myTest4.bbb()));
        }
    }
}

应用场景

        结合反射做框架

模拟Junit框架。

需求:

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

分析:

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

        ②定义若千个方法,部分方法加上@MyTest注解修饰,部分方法不加。

        ③模拟一个junit程序,可以触发加了@MyTest注解的方法执行。

注解:

@Target(ElementType.METHOD) // 注解只能注解方法。
@Retention(RetentionPolicy.RUNTIME) // 让当前注解可以一直存活着。
public @interface MyTest {
}

测试:

public class AnnotationTest4 {
    // @MyTest
    public void test1(){
        System.out.println("===test1====");
    }

    @MyTest
    public void test2(){
        System.out.println("===test2====");
    }

    @MyTest
    public void test3(){
        System.out.println("===test3====");
    }

    @MyTest
    public void test4(){
        System.out.println("===test4====");
    }

    public static void main(String[] args) throws Exception {
        AnnotationTest4 a = new AnnotationTest4();
        // 启动程序!
        // 1、得到Class对象
        Class c = AnnotationTest4.class;
        // 2、提取这个类中的全部成员方法
        Method[] methods = c.getDeclaredMethods();
        // 3、遍历这个数组中的每个方法,看方法上是否存在@MyTest注解,存在
        // 触发该方法执行。
        for (Method method : methods) {
            if(method.isAnnotationPresent(MyTest.class)){
                // 说明当前方法上是存在@MyTest,触发当前方法执行。
                method.invoke(a);
            }
        }
    }
}

动态代理(不改变类,再封装一层操作)

程序为什么需要代理?代理长什么样?

代理

        代理思想就是被代理者没有能力,或者不愿意去完成某件事情,需要找个人(代理)代替自己去完成这件事。

代理作用

        动态代理主要是对被代理对象的行为进行代理。

  

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

使用代理优化用户管理类

场景:

        某系统有一个用户管理类,包含用户登录,删除用户,查询用户等功能,系统要求统计每个功能的执行耗时情况,以便后期观察程序性能。

需求:

        现在,某个初级程序员已经开发好了该模块,请观察该模块的代码,找出目前存在的问题,并对其进行改造。

代理工具类:

public class ProxyUtil {
    public static UserService createProxy(UserService userService){
        UserService userServiceProxy = (UserService) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
                new Class[]{UserService.class}, new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        if(method.getName().equals("login") || method.getName().equals("deleteUsers")||
                               method.getName().equals("selectUsers")){
                            long startTime = System.currentTimeMillis();

                            Object rs = method.invoke(userService, args);

                            long endTime = System.currentTimeMillis();
                            System.out.println(method.getName() + "方法执行耗时:" + (endTime - startTime)/ 1000.0 + "s");
                            return rs;
                        }else {
                            Object rs = method.invoke(userService, args);
                            return rs;
                        }
                    }
                });
        return userServiceProxy;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值