JAVA-第六部分-Junit、反射及注解

Junit

  • 单元测试,属于白盒测试,需要写代码,关注程序的具体执行流程
  • 黑盒测试,只需要关注输入输出

使用步骤

  • 导入Junit依赖环境
  • 定义一个测试类 类名+Test
  • 放在xxx.xxx.xxx.test包下
  • 方法名 test+方法名,不用返回值,空参,加注解@Test

结果判断

  • 红色代码错误,绿色正确
  • 断言,判定结果是否正确
public class CalculatorTest {
    @Test
    public void testAdd() {
        int add = new Calculator().add(13, 142);
        Assert.assertEquals(15,add);
    }
}
复制代码

初始化方法与释放资源方法

  • 用于所有测试方法在执行之前和之后要调用的方法,如申请资源
  • @Before初始化
  • @After释放
@Before
public void init() {
    System.out.println("init");
}
@After
public void close() {
    System.out.println("close");
}
复制代码

反射

  • 框架,半成品软件,在框架基础上进行软件开发,简化代码
  • 反射,将类的各个组成部分封装成其他对象(成员变量对象、构造方法对象、成员变量对象),可以在程序运行过程中操作对象;解耦合,提高可扩展性
  • Java代码三个阶段,源代码阶、类对象阶段、运行时阶段

image.png

获取Class对象

  • 同一个字节码文件,在一次程序的运行过程中,只会被加载一次
  • 将字节码文件加载到内存,用于配置文件,加载类
Class<?> aClass = Class.forName("com.mzx.java.DemoReflect.Person");
System.out.println(aClass);
复制代码
  • 通过类名属性获取,参数传递
System.out.println(Person.class);
复制代码
  • 对象方法,获取对象的字节码
Person p1 = new Person();
System.out.println(p1.getClass());
复制代码

获取方法

  • 获取成员变量们,设置值/获取值
//获取所有public修饰的成员变量
for (Field field : aClass.getFields()) {
    System.out.println(field);
}
//获取指定的一个
Field name = aClass.getField("name");
System.out.println(name);
Person p = new Person();
//获取值
System.out.println(name.get(new Person("zhangsan", 18)));
//设置值
name.set(p, "lisi");
System.out.println(p);

//获取所有成员变量不考虑修饰符
for (Field declaredField : aClass.getDeclaredFields()) {
    System.out.println(declaredField);
}
Field name1 = aClass.getDeclaredField("name");
System.out.println(name1);
//在访问私有成员变量之前,要忽略权限修饰符的安全检查
//暴力反射
name1.setAccessible(true);

System.out.println(name1.get(new Person("zhangsan",20)));
Person p = new Person();
name1.set(p, "zhaoliu");
System.out.println(p);
复制代码
  • 获取构造方法们
//获取public修饰的构造器
for (Constructor<?> constructor : aClass.getConstructors()) {
    System.out.println(constructor);
}
//通过构造器的参数类型,获取指定的构造函数
Constructor<?> constructor = aClass.getConstructor(String.class, int.class);
System.out.println(constructor);
//构造器创建对象
Object person = constructor.newInstance("zhangsan", 242);
System.out.println(person);

//忽略修饰符
for (Constructor<?> declaredConstructor : aClass.getDeclaredConstructors()) {
    System.out.println(declaredConstructor);
}
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class);
System.out.println(declaredConstructor);
//暴力反射
declaredConstructor.setAccessible(true);

复制代码
  • 获取成员方法们
//public和继承父类的方法
for (Method method : aClass.getMethods()) {
    System.out.println(method.toString());
}
Method method = aClass.getMethod("setName", String.class);
System.out.println(method);
//执行方法
Person person = new Person("zhangsan", 20);
method.invoke(person, "lisi");
System.out.println(person);

//忽略修饰符,并且只输出类中已写出的方法
for (Method declaredMethod : aClass.getDeclaredMethods()) {
    System.out.println(declaredMethod);
}
Method eat = aClass.getDeclaredMethod("eat", String.class);
System.out.println(eat);
//只获取方法名称
System.out.println(eat.getName());
//暴力反射
eat.setAccessible(true);
eat.invoke(person,"milk");
复制代码
  • 获取类名
System.out.println(aClass.getName());
复制代码

自定义框架

  • 配置文件;反射
  • 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
  • 在程序终结在读取配置文件
  • 使用反射技术加载类文件进内存
  • 创建对象
  • 执行方法

例子

  • 代码
public class ReflectClass {
    public static void main(String[] args) throws Exception {
        //加载配置文件
        Properties pro = new Properties();
        //获取class目录下的配置文件
        //获取把这个class加载到内存中的对象,通过这个对象找到配置文件
        ClassLoader classLoader = ReflectClass.class.getClassLoader();
        InputStream resourceAsStream = classLoader.getResourceAsStream("pro.properties");
        pro.load(resourceAsStream);
        String className = pro.getProperty("className");
        String methodName = pro.getProperty("methodName");
        String parameter = pro.getProperty("parameter");
        String parameterType = pro.getProperty("parameterType");
        //加载类进内存
        Class<?> aClass = Class.forName(className);
        Object o = aClass.getConstructor().newInstance();
        Method method = aClass.getDeclaredMethod(methodName, Class.forName(parameterType));
        method.setAccessible(true);
        method.invoke(o, parameter);
    }
}
复制代码
  • 配制文件
className = com.mzx.java.DemoReflect.Person
methodName = eat
parameter = beef 
parameterType = java.lang.String
复制代码

注解

  • Annotation
  • 描述程序,给计算机看
  • 就是一个标签,不属于程序的一部分

文档生成

  • 命令行
javadoc DemoMain.java
复制代码
  • java文件
/**
 * @author 99永远差一分
 * @version 1.0
 * @since 2021.8.12
 */
public class DemoMain {
    /**
     * 加
     * @param a 整数一
     * @param b 整数二
     * @return 两数之和
     */
    public int add (int a, int b) {
        return a + b;
    }
}
复制代码

内置注解

  • @Override 该注解标注的方法是否继承父类
  • @Deprecated 该注解标注的内容已过时
  • @SuppressWarings 压制警告
@SuppressWarnings("all")
复制代码

自定义注解

  • 通过反编译可以看到注解的本质是接口,该接口默认继承Annotation
(base) appledeMacBook-Pro-3:test apple$ javac MyAnnotation.java 
(base) appledeMacBook-Pro-3:test apple$ javap MyAnnotation.class 
Compiled from "MyAnnotation.java"
public interface com.mzx.java.DemoAnnotation.MyAnnotation extends java.lang.annotation.Annotation {

}
复制代码

属性

  • 接口中的抽象方法,必须返回基本数据类型、String、枚举、注解及以上类型的数组
  • 使用时,除了default后,必须给属性(抽象方法)赋值;如果只有一个属性需要赋值,并且名称为vale,则可以省略名字,直接赋值
//使用
@MyAnnotation(age = 1, person = Person.p1, override = @Override, strs = {"abc", "asdaw", "daczx"})

public @interface MyAnnotation {
    int age();
    String name() default "zhangsan";
    Person person();
    //注解类型
    Override override();
    //数组中只有一个值,大括号可以省略
    String[] strs();
}
复制代码

元注解

  • 用于描述注解的注解
  • @Target 描述注解能够作用的位置
// 作用与类上,方法上,成员变量上
@Target(value = {ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
复制代码
  • @Retention 描述注解被保留的阶段
//被描述的注解,会保留到class字节码的文件中,并被JVM读到
@Retention(RetentionPolicy.RUNTIME)
//CLASS 会保存到字节码文件中,但不会被读到
//SOURCE 不会保存到字节码文件中
复制代码
  • @Documented 描述注解是否能被抽取到API文档中
  • @Inherited 描述注解是否能被子类继承

注解解析

  • 获取注解定义的位置的对象
  • 获取执行注解
  • 调用注解中的抽象方法获取配置的属性值
  • 注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Properties {
    String classname();
    String methodname();
    String parameter();
    String parameterType();
}
复制代码
  • 调用
@Properties(classname = "com.mzx.java.DemoAnnotation.Woker", methodname = "eat", parameter = "beef", parameterType = "java.lang.String")
public class ReflectClass {
    public static void main(String[] args) throws Exception {
        //解析注解
        //获取本类
        Class<ReflectClass> reflectClassClass = ReflectClass.class;
        //获取该类上的所有注解,在内存中声称了一个该注解接口的子类实现实现对象,重写的接口方法直接返回值
        Properties annotation = reflectClassClass.getAnnotation(Properties.class);
        //获取注解对象中定义的抽象方法,获取返回值
        String classname = annotation.classname();
        String methodname = annotation.methodname();
        String parameter = annotation.parameter();
        String parameterType = annotation.parameterType();
        //执行
        Class<?> aClass = Class.forName(classname);
        Object o = aClass.getConstructor().newInstance();
        Method declaredMethod = aClass.getDeclaredMethod(methodname, Class.forName(parameterType));
        declaredMethod.setAccessible(true);
        declaredMethod.invoke(o, parameter);
    }
}
复制代码

注解测试

  • 通过注解,对注解后的方法进行测试和异常打印
public static void main(String[] args) throws IOException {
    Calculator calculator = new Calculator();
    Class<? extends Calculator> aClass = calculator.getClass();
    Method[] methods = aClass.getMethods();
    int num = 0; //出现异常的次数
    //文件
    BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("bug.txt"));

    for (Method method : methods) {
        //方法上是否有异常
        if (method.isAnnotationPresent(Check.class)) {
            try {
                method.invoke(calculator);
            } catch (Exception e) {
                //捕获异常,记录到文件
                 num ++;
                 bufferedWriter.write(method.getName() + " - 出现异常");
                 bufferedWriter.newLine();
                 bufferedWriter.write("异常的名称 - " + e.getCause().getClass().getSimpleName());
                 bufferedWriter.newLine();
                 bufferedWriter.write("异常的原因 - " + e.getMessage());
                 bufferedWriter.newLine();
                 bufferedWriter.write("------------------------------");
                 bufferedWriter.newLine();
            }
        }
    }
    bufferedWriter.write("本次一共出现 " + num + " 次异常");
    bufferedWriter.flush();
    bufferedWriter.close();
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享