• Java SE入门(二十一)——反射和注解


    iwehdio的博客园:https://www.cnblogs.com/iwehdio/

    1、Junit单元测试

    • 测试的分类:
      • 黑盒测试。只需要关注输入的输出,不考虑实现方式。
      • 白盒测试。考虑实现方式,关注执行流程。
    • Junit 属于白盒测试:
      1. 定义一个测试类。测试类名最好为 被测试的类名 + Test,放入 test 包中。
      2. 定义测试方法,可以独立运行。方法名最后为 test + 测试的方法名。返回值建议为 void,参数列表建议为空参。
      3. 给方法加@Test注解。需要导入 JUnit4 依赖。
    • 断言:将所期望的结果和真实结果进行比较。
      • 例如使用Assert.assertEquals(期望值, 真实值)方法。
      • 结果不同则测试结果为出错。
    • @Before注解,该注解修饰的方法会在所有测试方法执行之前被执行。
    • @After注解,该注解修饰的方法会在所有测试方法执行之后被执行(即使测试方法出错也会被执行)。

    2、反射

    将类的各个组成部分封装为其他对象,这被称为反射机制。

    • Java 代码在计算机中经历的三个阶段:

      1. javac 编译为 .class 字节码文件。被称为源代码阶段。
      2. 类通过类加载器(ClassLoader)加载进内存,被加载为 Class 类对象。
        • 成员变量封装为 Field[] 对象,构造方法封装为 Construct[] 对象,成员方法封装为 Method[] 对象。这就是反射。
        • 被称为类对象阶段。
      3. 实例化对象。被称为运行阶段。
    • 反射的好处:

      • 在程序运行的过程中,可以对对象进行操作。
      • 可以解耦,提供程序的可扩展性。
    • 获取字节码 Class 对象的方式:

      1. 如果是源代码阶段,通过Class.forName("全类名")将字节码文件加载进内存,创建并返回 Class 类对象。
        • 多用于配置文件,将类名定义在配置文件中,读取文件加载类。
      2. 如果是类对象阶段,通过类名.class属性获取。
        • 多用于参数传递。
      3. 如果是运行阶段,通过对象.getClass()方法获取。
        • 多用于对象的获取字节码对象。
      4. 同一个字节码文件(.class)在一次程序运行过程中,只会被加载一次,使用不同方式获取的 Class 对象是同一个。
    • Class 对象的功能:

      • 获取功能:
        • 获取成员变量。
          • Field[] getFields():获取所有public修饰的成员变量。
          • Field getField(String name):获取指定的public修饰的成员变量。
          • Field[] getDeclaredFields():获取所有的成员变量,包括所有修饰符。
          • Field getFieldDeclared(String name):获取指定的成员变量,包括所有修饰符。
        • 获取构造方法。
          • Constructor<?>[] getConstructors():获取所有public的构造方法。
          • Constructor<?> getConstructor(class<?>...parameterTypes):获取指定的public修饰的构造方法。
            • 传入的可变参数是class对象类型,如String和int应传入String.classint.class
          • Constructor<?>[] getDeclaredConstructor():获取所有的构造方法,包括所有修饰符。
          • Constructor<?> getDeclaredConstructor(class<?>...parameterTypes):获取指定的构造方法,包括所有修饰符。
        • 获取成员方法。
          • Method[] getMethods():获取所有public的成员方法。
          • Method[] getMethod(String name, class<?>...parameterTypes):获取指定的public修饰的成员方法。
            • 传入的是方法名称和方法的参数列表。
          • Method[] getDeclaredMethods():获取所有的成员方法。
          • Method[] getDeclaredMethod(String name, class<?>...parameterTypes):获取指定的成员方法。
        • 获取类名。
          • String getName():获取全类名。
    • 成员变量对象Field的方法:

      • Object get(Object o):获取成员变量的值。
      • void set(Object o, Object value):设置成员变量的值。
      • void setAccessible(true):访问非公有的成员变量,需要忽略访问权限修饰符的安全检查(暴力反射)。
    • 构造方法对象Constructor的方法:

      • Object newInstance(Object...parameter):创建对象。
      • 如果是空参构造,也可以使用Class对象的newInstance方法。
      • void setAccessible(true):访问非公有的成员变量,需要忽略访问权限修饰符的安全检查(暴力反射)。
    • 成员方法Method对象的方法:

      • Object invoke(Object o, Object...args):传入对应的对象和参数列表的class对象列表。
      • String getName():获取方法名。
      • 所有方法,还包括了这个对象继承的父类中的方法。
      • void setAccessible(true):访问非公有的成员变量,需要忽略访问权限修饰符的安全检查(暴力反射)。
    • 实例:

      • 需求:利用反射创建一个框架类,在不改变该类的任何代码的情况下,创建任意类的对象,并执行其任意方法。

      • 步骤:

        1. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中。
        2. 在程序中加载读取配置文件。
        3. 利用反射加载类文件进内存。
        4. 创建对象、执行方法。
      • RelectFram.java:

        public class ReflectFrame {
            public static void main(String[] args) throws Exception {
                Properties pro = new Properties();
                ClassLoader classLoader = ReflectFrame.class.getClassLoader();
                InputStream inputStream = classLoader.getResourceAsStream("pro.properties");
                pro.load(inputStream);
                String className = pro.getProperty("className");
                String methodName = pro.getProperty("methodName");
                Class cls = Class.forName(className);
                Object obj = cls.newInstance();
                Method method = cls.getMethod(methodName);
                method.invoke(obj);
            }
        }
        
      • Person.java:

        public class Person {
            private String name;
            public Person() {
            }
            public Person(String name) {
                this.name = name;
            }
            public String getName() {
                return name;
            }
            public void setName(String name) {
                this.name = name;
            }
            public void sleep() {
                System.out.println("sleep");
            }
        }
        
      • pro.properties:

        className=cn.iwehdio.domin.Student
        methodName=study
        

    3、注解

    是一种代码级别的说明,对元素进行说明注释。

    • 注解使用:@注解名称

    • 注解作用:编写文档、编译检查、代码分析。

    • Java中预定义的注解:

      • @Override:检测被该注解标注的方法是否继承/实现自父类/接口。
      • @Deprecated:该注解标注的内容,表示已过时。
      • SupperessWarnings("all"):压制所有警告。
    • 自定义注解:

      • 格式:
        • 元注解:用于描述注解的注解。
        • public @interface 注解名{属性列表}
      • 本质:
        • public interface 注解名 extends java.lang.annotation.Annotation{}
        • 注解是一个接口,继承自Annotation接口。
      • 属性:接口中可以定义的抽象方法。
        1. 属性的返回值类型为基本数据类型、String、枚举、注解或以上类型的数组。
        2. 定义了属性,在使用时需要给属性赋值。格式为@注解(属性名=属性值)
        3. 属性可以用default指定默认值。
        4. 如果只有一个value属性,赋值时可以直接写属性值。
        5. 数组赋值时,使用{}
      • 元注解:
        • @Target:描述注解能够作用的位置。
        • {ElemntType=枚举值}:TYPE只能作用于类上,METHOD只能作用在方法上,FIELD只能作用于成员变量上。
        • @Retention:描述注解被保留的阶段。
          • RUNTIME会直到运行时被保留,在class字节码文件中并且被JVM读到。
        • @Docunmented:描述注解是否被抽取到api文档中。
        • @Inherited:描述注解是否被子类继承。
    • 注解的解析:获取注解中定义的属性值。

      • 注解可以部分替代配置文件。
      1. 获取该类的字节码文件对象:被注解修饰的类.class

      2. 获取注解对象,在内存中生成一个该注解接口的子类实现对象:getAnnotation(注解名.class)

      3. 调用注解对象中的抽象方法,获取返回值。

      4. 实例:

        • Myanno:

          @Target({ElementType.TYPE})
          @Retention(RetentionPolicy.RUNTIME)
          public @interface Myanno {
              String className();
              String methodName();
          }
          
        • DemoTest:

          @Myanno(className = "package cn.iwehdio.annotation;", methodName = "show")
          public class DemoTest {
              public static void main(String[] args) {
                  Class<DemoTest> dtClass = DemoTest.class;
                  Myanno ma = dtClass.getAnnotation(Myanno.class);
                  String classname = ma.className();
                  String methodname = ma.methodName();
                  System.out.println(classname);
                  System.out.println(methodname);
              }
          }
          
    • 实例:

      • 给所需要测试的方法加上@check注解,对所有要测试的方法进行测试,并输出错误日志。

      • check.java:

        @Target({ElementType.METHOD})
        @Retention(RetentionPolicy.RUNTIME)
        public @interface check {
        }
        
      • caculator.java:

        public class calulator {
            @check
            public void mul() {
                System.out.println("1*0=" + (1*0));
            }
            @check
            public void div() {
                System.out.println("1*0=" + (1/0));
            }
        }
        
      • demo2.java:

        public class demo2 {
            public static void main(String[] args) throws IOException {
                calulator c = new calulator();
                Class cls = c.getClass();
                Method[] methods = cls.getMethods();
        
                int num = 0;
                BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt"));
        
                for (Method m : methods ) {
                    if(m.isAnnotationPresent(check.class)) {
                        try {
                            m.invoke(c);
                        } catch (Exception e) {
                            bw.write("异常出现在" + m.getName());
                            bw.newLine();
                            bw.write("异常名称" + e.getCause().getClass());
                            bw.newLine();
                            bw.write("异常类型" + e.getCause().getMessage());
                            bw.newLine();
                            bw.write("-----------");
                            num++;
                        }
                    }
                }
                bw.write("共出现" + num + "次异常");
                bw.newLine();
                bw.flush();
                bw.close();
            }
        }
        
      • BUG输出bug.txt:

        异常出现在div
        异常名称class java.lang.ArithmeticException
        异常类型/ by zero
        -----------
        共出现1次异常
        

    iwehdio的博客园:https://www.cnblogs.com/iwehdio/
  • 相关阅读:
    va_list va_start va_end va_arg 解决变参问题
    标准输出文件
    Qt QDataTime QString 两个类的使用
    联合开发网站
    iOS LLDB调试器和断点调试
    Wireshark 网络抓包工具Wireshark的使用
    linux 操作系统下c语言编程入门
    iOS 应用崩溃日志分析
    iOS sqlite3数据库解析
    iOS 解析手势识别(Gesture Recognizers)
  • 原文地址:https://www.cnblogs.com/iwehdio/p/12896617.html
Copyright © 2020-2023  润新知