Java SE-Junit、反射、注解

Junit单元测试
反射
注解
简单的测试框架

Junit单元测试
测试分类
  • 黑盒测试
  • 白盒测试
区别
  • 黑盒测试不需要写代码,给出输入,看结果是否符合输出
  • 白盒测试需要写代码关注程序的具体执行流程
Junit单元测试
  • Junit单元测试为白盒测试
使用步骤
  • 具体导入环境见收藏夹

在这里插入图片描述

public class Calculator {
    public int add(int a, int b) {
        return a+b;
    }

    public int sub(int a, int b) {
        return a-b;
    }
}

import com.junit.demo01junit.Calculator;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class CalculatorTest {
    /**
     *   初始化方法
     *     用于申请资源,所有测试方法在执行之前都会执行该方法
     */
    @Before
    public void init() {
        System.out.println("init...");
    }

    /**
     *   释放资源方法
     *     在所有测试方法执行完之后,都会自动执行该方法
     */
    @After
    public void close() {
        System.out.println("close...");
    }

    /**
     *   add测试方法
     */
    @Test
    public void testAdd() {
        Calculator calculator = new Calculator();
        int res = calculator.add(1,3);
        //创建断言,判断预期值和具体值是否有差别
        Assert.assertEquals(10, res);
        System.out.println(res);
    }

}


反射

在这里插入图片描述

概念
  • 将类的各个部分封装为其它对象,这就是反射机制
好处
  • 解耦,增加程序的扩展性
  • 可以在程序运行过程中,操作这些对象
获取Class对象的方式
  • Class.forName(“全类名”);将字节码加载到内存,返回Class对象
    • 多用于配置文件,将类名配置在文件中
  • 类名.class:通过类名的属性classh获取
    • 多用于参数的传递
  • 对象.getClass():getClass()方法在Object类中定义
    • 多用于对象获取字节码的方式
public class Person {
    private String name;
    private int age;

    public String a;
    protected String b;
    String c;
    private String d;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", a='" + a + '\'' +
                ", b='" + b + '\'' +
                ", c='" + c + '\'' +
                ", d='" + d + '\'' +
                '}';
    }

    public Person() {
    }

    public void eat() {
        System.out.println("eat...");
    }

    public void eat (String food) {
        System.out.println(food);
    }
}

/**
 *   获取Class对象的方式
 *
 *    - Class.forName("全类名");将字节码加载到内存,返回Class对象
 *    - 类名.class:通过类名的属性class获取
 *     - 对象.getClass():getClass()方法在Object类中定义
 *
 */
public class Demo01Reflect {
    public static void main(String[] args) throws ClassNotFoundException {
        //1、Class.forName("全类名");将字节码加载到内存,返回Class对象
        Class cl1 = Class.forName("com.reflect.demo01reflect.Person");
        System.out.println(cl1);

        //2、类名.class:通过类名的属性classh获取
        Class cl2 = Person.class;
        System.out.println(cl2);

        //3、对象.getClass():getClass()方法在Object类中定义
        Person p = new Person();
        Class cl3 = p.getClass();
        System.out.println(cl3);

        //比较三个对象
        System.out.println(cl1==cl2);  //true
        System.out.println(cl2==cl3);  //true
    }
}

Class对象获取Field
import com.reflect.demo01reflect.Person;

import java.io.FileInputStream;
import java.lang.reflect.Field;

/**
 *   获取功能
 *      1、获取成员变量
 *           Field getField(String name)
 *              返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。
 *           Field[] getFields()
 *               返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。
 *           Field getDeclaredField(String name)
 *               返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
 *           Field[] getDeclaredFields()
 *               返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。
 *      2、获取构造方法
 *            Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
 *               返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。
 *            Constructor<?>[] getDeclaredConstructors()
 *               返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。
 *             Constructor<T> getConstructor(Class<?>... parameterTypes)
 *               返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
 *            Constructor<?>[] getConstructors()
 *              返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。
 *      3、获取成员方法
 *            Field getDeclaredField(String name)
 *              返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
 *            Field[] getDeclaredFields()
 *                返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。
 *             Method getMethod(String name, Class<?>... parameterTypes)
 *              返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
 *           Method[] getMethods()
 *              返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口
 *              (包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。
 *      4、获取类名
 *          String getName()
 *           以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。
 */
public class Demo01Get {
    public static void main(String[] args) throws Exception {
        //获取Person的Class对象
        Class personClass = Person.class;

        //1、Field[] getFields() 获取所有的public修饰的成员变量
        Field[] fields = personClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("============");

        //2、Field getField(String name)
        Field a = personClass.getField("a");
        //获取成员变量a的值
        Person p = new Person();
        Object value = a.get(p);
        //设置a的值
        a.set(p, "张三");
        System.out.println(p);
        System.out.println("==============");

        //3、Field[] getDeclaredFields()
        //获取所有的成员变量
        Field[] declaredFields = personClass.getDeclaredFields();
        for (Field field : declaredFields) {
            System.out.println(field);
        }

        //4、Field getDeclaredField(String name)
        Field d = personClass.getDeclaredField("d");
        //因为d是私有变量,若要访问要忽略访问权限修饰符的安全检查
        d.setAccessible(true);  //暴力fan'she
        Object value2 = d.get(p);
        System.out.println(value2);
    }
}

Class对象获取Constructor
import com.reflect.demo01reflect.Person;

import java.lang.reflect.Constructor;

/**
 *   获取构造器
 *
 *    *      2、获取构造方法
 *  *            Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
 *  *               返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。
 *  *            Constructor<?>[] getDeclaredConstructors()
 *  *               返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。
 *  *             Constructor<T> getConstructor(Class<?>... parameterTypes)
 *  *               返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
 *  *            Constructor<?>[] getConstructors()
 *  *              返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。
 */
public class Demo01Constructor {
    public static void main(String[] args) throws Exception {
        //获取Person的Class对象
        Class personClass = Person.class;

        //Constructor<T> getConstructor(Class<?>... parameterTypes)
        Constructor constructor = personClass.getConstructor(String.class, int.class);
        System.out.println(constructor);
        //创建对象
        Object person = constructor.newInstance("小狗", 10);
        System.out.println(person);

        //创建空参数构造方法
        Constructor constructor1 = personClass.getConstructor();
        System.out.println(constructor1);
        //床架对象
        Object person1 = constructor1.newInstance();
        System.out.println(person1);

        /**
         *  优化创建空参构造方法
         */
        Object o = personClass.newInstance();
        System.out.println(o);
    }
}
Class对象获取Method
import com.reflect.demo01reflect.Person;

import java.lang.reflect.Method;

/**
 *    *      3、获取成员方法
 *  *            Field getDeclaredField(String name)
 *  *              返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
 *  *            Field[] getDeclaredFields()
 *  *                返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。
 *  *             Method getMethod(String name, Class<?>... parameterTypes)
 *  *              返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
 *  *           Method[] getMethods()
 *  *              返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口
 *  *              (包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。
 *
 *           执行方法
 *             Object invoke(Object obj, Object...args)
 *            获取方法名称
 *            String getName
 */
public class Demo01Method {
    public static void main(String[] args) throws Exception {
        //获取Class对象
        Class personClass = Person.class;

        /**
         *   通过方法名和参数列表获取方法
         */
        //获取指定名称的方法
        Method eat_method = personClass.getMethod("eat");
        Person p = new Person();
        //执行方法
        eat_method.invoke(p);

        Method eat_method2 = personClass.getMethod("eat", String.class);
        eat_method2.invoke(p, "狗肉");
        System.out.println("=================");

        //获取所有public修饰的方法
        Method[] methods = personClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
            String name = method.getName();
            System.out.println(name);
        }

        //同样支持暴力反射
        //method.setAccessible(true);

        //获取类名
        String className = personClass.getName();
        System.out.println(className);

    }

}

注意
  • 同一个字节码文件(*.class)在一次运行过程中只会被加载一次无论通过哪一种方式获取Class对象都是同一个
反射案例
  • 在不改变任何类的前提下,可以创建任意类对象,执行任意方法
  • 通过修改配置文件来修改调用的方法

在这里插入图片描述

//配置文件
className=com.reflect.demo01reflect.demo05reflecttest.Person
methodName=eat

public class Person {
    private String name;
    private int age;

    public String a;
    protected String b;
    String c;
    private String d;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", a='" + a + '\'' +
                ", b='" + b + '\'' +
                ", c='" + c + '\'' +
                ", d='" + d + '\'' +
                '}';
    }

    public Person() {
    }

    public void eat() {
        System.out.println("eat...");
    }

    public void eat (String food) {
        System.out.println(food);
    }
}

public class Student {
    public void sleep() {
        System.out.println("sleep...");
    }
}

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 *   框架类:
 *     在不改变任何类的前提下,可以创建任意类对象,执行任意方法
 */
public class ReflectTest {
    public static void main(String[] args) throws Exception {
        //可以创建任意类对象,执行任意方法

        /**
         *   前提:可以创建任意类对象,执行任意类方法
         */

        //1、加载配置文件
        //创建Properties对象
        Properties properties = new Properties();
        //加载配置文件,转换为一个集合
        //获取class目录下的配置文件
        ClassLoader classLoader = ReflectTest.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("pro.properties");
        properties.load(is);

        //2、获取配置文件中配置的数据
        String className = properties.getProperty("className");
        String methodName = properties.getProperty("methodName");

        //3、加载该类进入内存
        Class cls = Class.forName(className);
        //4、创建对象
        Object obj = cls.newInstance();
        //5、获取对象方法
        Method method = cls.getMethod(methodName);
        //执行方法
        method.invoke(obj);

    }
}

注解
注解简介
  • 注解实在jdk1.5之后的新特性
  • 说明程序的
本质
  • 注解本质就是一个接口,该接口默认继承Annotation接口
注解分类
  • 编写文档
    • 通过代码里标注的注解生成文档(生成doc文档)
  • 代码分析
    • 通过代码里标注的注解对代码进行分析(使用反射)
  • 编译检查
    • 通过代码里标注的注解让编译器实现基本的编译检查(override)
常用注解
/**
 *    jdk中预定义的一些注解
 *      @override  :检测被该注解标识的方法是否是继承父类的
 *      @Deprecated :该注解标识的内容,表示已过时
 *      @supperessWarnings :压制警告
 */

//压制所有警告
@SuppressWarnings("all")
public class Demo01 {

    @Override
    public String toString() {
        return super.toString();
    }

    @Deprecated
    public void show1() {

    }

    public void show2() {
        show1();
    }
}

自定义注解
格式
 *   格式:
 *      元注解
 *      public @interface 注解名称(){
 *          属性列表(其实是抽象方法)
 *      }
案例

public enum Person {
    p1,p2;
}

/**
 *   自定义注解
 *   格式:
 *      元注解
 *      public @interface 注解名称(){
 *          属性列表(其实是抽象方法)
 *      }
 *   本质:
 *      注解本质就是一个接口,该接口默认继承Annotation接口
 *   属性:
 *      接口中可以定义的抽象方法
 *      要求:
 *         1、属性的返回值类型
 *             基本数据类型
 *             String
 *             枚举
 *             注解
 *             以上类型的数组
 *         2、定义了属性,在使用时需要给属性赋值
 *            在如果在定义属性时使用default关键字给属性默认初始化则使用注解时,可以不进行属性赋值
 *            如果只有一个属性需要赋值且属性名为value,则value可以省略,直接赋值即可
 *            数组赋值时,值使用大括号,若数组中只有一个元素,则大括号可以省略
 *      元注解:
 *          用来描述注解的注解
 *          @target :用来描述注解能够作用的位置
 *          @Retention :描述注解被保留的阶段
 *          @Documented :描述注解是否被抽取到api文档中
 *          @Inherited :描述注解是否被子类继承
 */
public @interface MyAnno {
    int age();
    //定义时默认赋值
    String name() default "张三";

    Person per();

    MyAnno2 anno2();

    String[] str();
}

@MyAnno(age = 11, per = Person.p1, anno2 = @MyAnno2, str = {"abc", "cdf"})
public class Worker {

}

import java.lang.reflect.Method;

@Pro(className = "com.annotation.demo01annotation.test.Demo01", methodName = "show")
public class ReflectTest {
    public static void main(String[] args) throws Exception{
        //1、解析注释
        //1.1获取该类的字节码文件对象
        Class<ReflectTest> reflectTestClass = ReflectTest.class;
        //2、获取上边的注解对象
        Pro an = reflectTestClass.getAnnotation(Pro.class);

        //3、调用注解对象中调用的方法,并获取返回值
        String className = an.className();
        String methodName = an.methodName();

        System.out.println(className);
        System.out.println(methodName);



        //3、加载该类进入内存
        Class cls = Class.forName(className);
        //4、创建对象
        Object obj = cls.newInstance();
        //5、获取对象方法
        Method method = cls.getMethod(methodName);
        //执行方法
        method.invoke(obj);
    }
}

自定义注解的使用
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 *   描述被执行的类名和方法名
 */

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Pro {
    String className();
    String methodName();
}

元注解
定义
  • 用于描述注解的注解
import java.lang.annotation.*;

/**
 *  * 元注解:
 *  * 用来描述注解的注解
 *  * @target :用来描述注解能够作用的位置
 *      ElementType取值
 *          TYPE :可以作用于类上
 *          METHOD :可以作用于方法上
 *          FIELD : 可以作用于成员变量上
 *  * @Retention :描述注解被保留的阶段
 *          @Retention(RetentionPolicy.RUNTIME) :当前被描述的注解,会保留到class字节码文件中,并被jvm读取到
 *  * @Documented :描述注解是否被抽取到api文档中
 *  * @Inherited :描述注解是否被子类继承
 */

@Target(value = {ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyAnno3 {
}

在程序中解析注解

在这里插入图片描述

简单的测试框架
  • 主方法执行时,会检测所有加上Check注解的方法,判断是否有异常,记录到文件中
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)   //这里用来检测方法
public @interface Check {
}

import com.sun.security.jgss.GSSUtil;

/**
 *   定义的计算机类
 */
public class Calculator {

    /**
     *  加法
     */
    @Check
    public void add() {
        System.out.println("1 + 0 =" + (1+0));
    }

    /**
     *   减法
     */
    @Check
    public void sub() {
        System.out.println("1 - 0 =" + (1-0));
    }

    /**
     *  乘法
     */
    @Check
    public void mul() {
        System.out.println("1 * 0 =" + (1*0));
    }

    /**
     *  除法
     */
    @Check
    public void div() {
        System.out.println("1 / 0 =" + (1/0));
    }

    public void show() {
        System.out.println("永无bug...");
    }
}

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 *   简单的测试框架
 *    当主方法执行时,会检测所有加上Check注解的方法,判断是否有异常,记录到文件中
 */
public class TestCheck {
    public static void main(String[] args) throws IOException {
        //创建对象
        Calculator c = new Calculator();
        //获取文件字节码对象
        Class cls = c.getClass();
        //获取所有方法
        Method[] methods = cls.getMethods();

        //创建存储异常文件
        BufferedWriter buw = new BufferedWriter(new FileWriter("bug.txt"));

        //出现异常的次数
        int num = 0;
        for (Method method : methods) {
            //判断上述方法是否有Check注解
            if (method.isAnnotationPresent(Check.class)) {
                //有,则执行
                try {
                    method.invoke(c);
                } catch (Exception e) {
                    //捕获异常
                    num++;
                    buw.write(method.getName() + "方法出异常了");
                    buw.newLine();
                    buw.write("异常名称:" + e.getCause().getClass().getSimpleName());
                    buw.newLine();
                    buw.write("异常原因:" + e.getCause().getMessage());
                    buw.newLine();
                    buw.write("--------------");
                    buw.newLine();
                }
            }
        }

        buw.write("本次测试共出现" + num + "次异常");
        buw.flush();
        buw.close();
    }
}

小结
  • 以后大多数情况,我们仅仅会只用注解,而不去定义注解
  • 注解的使用者:编译器、解析程序
  • 注解不是程序的一部分,可以立即为注解就是一个标签
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值