美文网首页Java
Java(L3)-- Junit、反射、注解

Java(L3)-- Junit、反射、注解

作者: 刘点石 | 来源:发表于2019-12-15 21:06 被阅读0次

Junit、反射、注解

今天学习的是 Java 基础加强部分的内容,包括 Junit、反射、注解三部分内容。

Part I. Junit

  1. 测试的分类:

    • 黑盒测试:只关注输入与输出的结果,不关注内部实现的细节。
    • 白盒测试:既关注输入与输出的结果,又关注程序的执行流程。
  2. Junit:白盒测试

    步骤:

    • 定义一个测试类:
      • 类名一般为 XxxxTest,如 CalculatorTest
      • 报名一般为 xxx.xxx.test,如
    • 定义测试方法
      • 方法名:testXxxx,如 testAdd()
      • 返回值:void
      • 参数列表:空参
    • 给测试方法加注解 @Test
    • 导入 junit 依赖包

    运行结果:

    • 红色:测试方法运行发现错误

    • 绿色:测试方法运行未发现错误

    • 注:一般情况下,会用断言操作来处理测试函数中得到的结果

      Assert.assertEquals(期望的结果,运算的结果);
      

    补充:

    • @Before:修饰的方法会在测试方法前自动执行
    • @After:修饰的方法会在测试方法执行后自动执行

Part II. 反射

  1. Java代码执行的三个阶段

    一般情况下,Java 代码执行过程分为三个阶段:

    • Source 源代码阶段
    • Class 类对象阶段
    • Runtime 运行时阶段

    下面对这三个阶段依次解释:

    Source 阶段

    一般而言,当我们定义一个类时,会定义其成员变量构造方法成员方法,并将其放在一个 .java 文件中。该文件经过编译后会生成一个 .class 文件,即字节码文件,其中也包含了成员变量、构造方法和成员方法三部分。Source 源代码阶段主要包含这两个部分。

    Class 阶段:

    字节码文件经过类加载器(ClassLoader)加载到内存中,每个类经过加载后对应一个 Class 对象,该对象包括一个存放成员变量对象(field)的数组 Field [] fields;一个存放构造方法对象(constructor)的数组 Constructor [] constructors;和一个用于存放成员方法对象(method)的数组 Method [] methods。

    Runtime 阶段:

    内存中的 Class 类对象可以直接创建类对象,即进入 Runtime 阶段。

  2. 反射

    反射即程序在运行时可以访问、检测和修改它本身状态或行为的一种能力(维基百科)。在 Java 中,由 Source 阶段和 Runtime 阶段得到 Class 类对象并进行各类操作的过程称之为反射(我的理解)。

    得到 Class 对象的方法:

    • Class.forName("全类名"):将字节码加载进内存,返回 Class 对象

      多用于配置文件,将类名、方法名等定义在配置文件中。

    • 类名.class:通过类名的属性class获取

    • 对象.getClass():由对象获取 Class,getClass()方法在Object类中定义。

    注:同一个字节码文件 (*.class) 在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。

  3. Class 对象的功能

  • 获取成员变量:
    • Field[] getFields():获取所有public修饰的成员变量
    • Field getField(String name):获取指定名称的 public修饰的成员变量
    • Field[] getDeclaredFields():获取所有的成员变量
    • Field getDeclaredField(String name):获取指定名称的成员变量
  • 获取构造方法
    • Constructor<?>[] getConstructors():获取所有public修饰的构造函数
    • Constructor<T> getConstructor(Class<?>... parameterTypes):获取指定名称的 public 修饰的构造函数
    • Constructor<?>[] getDeclaredConstructors():获取所有构造函数
    • Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):获取指定名称的构造函数
  • 获取成员方法
    • Method[] getMethods():获取所有public修饰的成员方法
    • Method getMethod(String name, Class<?>... parameterTypes):获取指定名称的 public 修饰的成员方法
    • Method[] getDeclaredMethods():获取所有的成员方法
    • Method getDeclaredMethod(String name, Class<?>... parameterTypes):获取指定名称的成员方法。
  • Field:成员变量
    • 设置值:void set(Object obj, Object value)
    • 获取值:get(Object obj)
    • 忽略访问权限修饰符的安全检查:setAccessible(true),又称暴力反射
  • Constructor:构造方法
    • 创建对象:T newInstance(Object... initargs)
    • 创建空参对象也可以直接调用 Class 对象的 newInstance 方法
  • Method:方法对象
    • 执行方法:Object invoke(Object obj, Object... args)
    • 获取方法名称:String getName()
  1. 案例

    要求:实现一个简易框架,可以从配置文件读取类名和方法名并执行该类的该方法。

    public static void main(String[] args) throws Exception {
        //读取配置文件并加载
        Properties pro = new Properties();
        ClassLoader classLoader = ReflectTest.class.getClassLoader();
        InputStream resourceAsStream = classLoader.getResourceAsStream("pro.properties");
        pro.load(resourceAsStream);
     //获取类名和方法名
        String className = pro.getProperty("className");
        String methodName = pro.getProperty("methodName");
     //创建 Class 对象并创建实例
        Class<?> cls = Class.forName(className);
        Object o = cls.newInstance();
     //由方法名获取成员方法并执行
        Method method = cls.getMethod(methodName);
        method.invoke(o);
    }
    

Part III. 注解

注解(Annotation),也叫元数据,一种代码级别的说明。它是 JDK1.5 及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

  1. 注解的作用分类

    • 编写文档:通过代码里标识的注解生成文档
    • 代码分析:通过代码里标识的注解对代码进行分析
    • 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查(如 Override )
  2. JDK 中预定义的一些注解

    • @Override:检测被该注解标注的方法是否是继承自父类(接口)的
    • @Deprecated:该注解标注的内容,表示已过时
    • @SuppressWarnings:压制警告。若传递的是 “all” 参数,则压制所有警告
  3. 自定义注解

    • 格式:
    元注解
    public @interface 注解名称{
     属性列表;
    }
    
    • 本质:注解本质上就是一个接口,该接口默认继承Annotation接口
    public interface MyAnno extends java.lang.annotation.Annotation {}
    
    • 属性:

      由于注解本身是一个对象,它的“属性”实际上就是他的抽象函数。

      属性的返回值类型有下列取值:

      • 基本数据类型
      • String
      • 枚举
      • 注解
      • 以上类型的数组

      定义了属性,在使用时需要给属性赋值:

      • 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值
      • 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可
      • 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略
    • 元注解:用于描述注解的注解

      • @Target:描述注解能够作用的位置

        其参数 ElementType 的取值:

        • TYPE:可以作用于类上
        • METHOD:可以作用于方法上
        • FIELD:可以作用于成员变量上
      • @Retention:描述注解被保留的阶段

        其参数一般取 RetentionPolicy.RUNTIME,会保留到字节码中并且会被 JVM 获取到

      • @Documented:描述注解是否被抽取到 api 文档中

      • @Inherited:描述注解是否被子类继承

    • 在程序中获取注解的属性值

      • 获取注解所修饰的对象(Class,Method 或 Field)

      • 使用 getAnnotation(Class) 获取制定的注解

        注:执行该方法实际上就是在内存中实现了该注解的子类对象

      • 使用注解的抽象函数获取属性值

    • 案例1:

      使用注解实现 Part II 中的简易框架:

      首先定义注解对象:

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

然后定义主类:

 ```java
 @Pro(className = "cn.dianshi.domain.Person", methodName = "sleep")
 public class ReflectTest {
     public static void main(String[] args) throws Exception {
         Class<ReflectTest> reflectTestClass = ReflectTest.class;
         Pro annotation = reflectTestClass.getAnnotation(Pro.class);
         String s = annotation.className();
         String s1 = annotation.methodName();
         System.out.println(s);
         System.out.println(s1);
 
         Class<?> aClass = Class.forName(s);
         Method method = aClass.getMethod(s1);
         Object o = aClass.newInstance();
         method.invoke(o);
     }
 }
 ```
  • 案例2:

    使用注解对以下类的方法进行测试,如有错误则将错误信息写在 bug.txt 中:

    Calculator.java:

    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));
        }
    }
    

    实现:

    首先定义 Check 注解:

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Check {
    }
    

    然后编写主类:

    public class TestCheck {
        public static void main(String[] args) throws Exception {
            Calculator calculator = new Calculator();
            Class<? extends Calculator> aClass = calculator.getClass();
            Method[] methods = aClass.getMethods();
            int count = 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) {
                        count ++;
                        bufferedWriter.write(method.getName() + "方法出现异常");
                        bufferedWriter.newLine();
                        bufferedWriter.write("异常名称" + e.getCause().getClass().getSimpleName());
                        bufferedWriter.newLine();
                        bufferedWriter.write("异常的原因" + e.getCause().getMessage());
                        bufferedWriter.newLine();
                        bufferedWriter.write("--------------------");
                        bufferedWriter.newLine();
                    }
                }
            }
    
            bufferedWriter.write("一共出现" + count + "次异常");
    
            bufferedWriter.flush();
            bufferedWriter.close();
        }
    }
    
  • 注解小结:

    • 多数情况下我们只是使用注解,只有极少数情况下才会自定义注解
    • 注解的服务对象是编译器和解析程序
    • 注解不是程序的一部分,可以理解为注解就是一个标签

相关文章

  • Java(L3)-- Junit、反射、注解

    Junit、反射、注解 今天学习的是 Java 基础加强部分的内容,包括 Junit、反射、注解三部分内容。 Pa...

  • 博客地址

    java注解-01、java注解-02、Git面试资源java反射-01、java反射-02、java反射-03为...

  • 基础加强

    Junit单元测试: 反射:框架设计的灵魂 注解:

  • Junit 反射、注解

    Junit测试方法,可以替代main方法执行测试代码。 如何定义测试方法: 1)不能有返回值,应该是void 2)...

  • Java基础加强——Junit、反射、注解

    今日内容 Junit单元测试: 反射:框架设计的灵魂 pro.properties文件 注解: ​ Calulat...

  • 笔记整理_3

    UT4断言 org.junit.ComparisonFailure: 反射 带有dubboservice注解的类 ...

  • 1.Junit单元测试,反射,注解

    主要内容 Junit单元测试: 反射:框架设计的灵魂 注解:

  • Java基础:反射

    反射注解动态代理相关阅读 Java基础:类加载器 Java基础:反射 Java基础:注解 Java基础:动态代理 ...

  • Spring注解原理探索(三)

    之 Java如何识别注解 关键词:Java 反射java.lang.reflect 包,实现反射功能的工具类。注解...

  • Java中的注解和反射

    个人博客http://www.milovetingting.cn Java中的注解和反射 注解 Java注解(An...

网友评论

    本文标题:Java(L3)-- Junit、反射、注解

    本文链接:https://www.haomeiwen.com/subject/naulnctx.html