java中的反射和注解

Junit单元测试

白盒测试

  • 定义一个测试类(测试用例,测试类名:测试类Test,包名:…test)
  • 定义测试方法:可以独立运行
  • 给方法加注解@test
  • 导入Junit
package cn.itcast.junit;

import org.junit.Assert;
import org.junit.Test;

public class CaculatorTest {
    @Test
    public void testAdd(){
        Caculator c = new Caculator();
        int result = c.add(1, 2);
        Assert.assertEquals(3, result);
    }
}

@Before初始化方法,用于资源申请,所有测试方法在执行之前都会先执行该方法
@After释放资源方法,在所有测试完之后执行该方法

反射

框架设计的灵魂
将类的各个组成部分封装成其他对象
在这里插入图片描述
好处

  • 可以在程序运行过程中操作这些对象
  • 可以解耦,提高程序的可扩展性

Class对象的功能:

获取功能:

获取Class对象的方式

  • Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象多用于配置文件,将类名定义在配置文件中。读取文件,加载类
  • 类名.class:通过类名的属性class获取多用于参数的传递
  • 对象.getClass():getClass()方法在Object类中定义多用于对象的获取字节码的方式
  • 结论:同一个字节码文件在第一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的class对象都是同一个

获取成员变量们

  • Field[]getFields
  • Field getField(String name)
  • Field[] getDeclaredFields()
  • Field getDeclaredField(String name)

获取构造方法们

  • Constructor<?>[] getConstructors()
  • Constructor<T> getConstructor(类<?>... parameterTypes)
  • Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
  • Constructor<?>[] getDeclaredConstructors()

获取成员方法

  • Method[] getMethods()
  • Method getMethod(String name, 类<?>... parameterTypes)
  • Method[] getDeclaredMethods()
  • Method getDeclaredMethod(String name, 类<?>... parameterTypes)

获取类名

  • String getName()

忽略访问权限修饰符的安全检查

  • setAccessible(true)暴力反射

通过构造方法创建对象

  • 创建对象:T newInstance(Object… initargs)

Method:方法对象

  • 执行方法:Object invoke(Object obj, Object… args)
  • 获取方法名称: String getName:获取方法名
package cn.itcast.reflect;

import cn.itcast.domain.Person;

import java.lang.reflect.Method;

public class Reflect2 {
    public static void main(String[] args) throws Exception {
        Class personClass = Person.class;
//        Field[] fields = personClass.getFields();
//        for (Field field : fields) {
//            System.out.println(field);
//        }
//        System.out.println("--------------");
//        Field a = personClass.getField("a");
//        Person p = new Person();
//        a.set(p, 32);
//        System.out.println(a.get(p));
//        System.out.println("=============");
//        Field[] declaredField = personClass.getDeclaredFields();
//        for (Field field : declaredField) {
//            System.out.println(field);
//        }
//        Constructor constructor = personClass.getConstructor(String.class, int.class);
//        System.out.println(constructor);
//        Object person = constructor.newInstance("张三", 23);
//        System.out.println(person);
//        System.out.println("------------");
//        Constructor constructor1 = personClass.getConstructor();
//        System.out.println(constructor1);
//        Object person2 = constructor1.newInstance();
//        System.out.println(person2);
        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, "rice");
        System.out.println("---------");
        Method[] methods = personClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
    }
}

实例

实现

  • 配置文件
  • 反射

步骤

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

配置文件

className = package cn.itcast.domain.Person
methodName = eat

反射

package cn.itcast.reflect;

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

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        //可以创建任意类的对象,可以执行任意方法
        /*Person p = new Person();
        p.eat();*/
        Properties pro = new Properties();
        //获取class目录下的配置文件
        ClassLoader classLoader = ReflectClass.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("pro.properties");
        pro.load(is);
        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);
    }
}

注解

作用分类

  • 编写文档:通过代码里标识的注解生成文档【生成doc文档】
  • 代码分析: 通过代码里标识的注解对代码进行分析【使用反射】
  • 编译检查: 通过代码里标识的注解能让编译器能够实现基本的编译检查【Override】

JDK中预定义的注解

  • @Override
  • @Deprecared
  • @SuppressWarnings

自定义注解
格式:

  • 元注解
  • public @interface 注解名称{}

本质:

  • 本质上就是接口,继承于Annotation接口

属性:接口中可以定义的成员方法

  • 基本数据类型
  • String
  • 枚举
  • 注解
  • 以上类型的数组
  • 定义了属性,在使用时需要给属性赋值

元注解

  • @Target:描述能够作用的位置
  • @Retention: 描述注解被保留的阶段
  • @Documented: 描述注解是否被抽取到api
  • @Inherited: 描述注解能否被子类继承

在程序中使用注解

  1. 获取注解定义的位置对象
  2. 获取指定的注解
    getAnnotation(class)
    //其实就是在内存中生成了一个该注解接口的子类实现对象
public class ProImpl implements Pro{
            public String className(){
                return "cn.itcast.annotation.Demo1";
            }
            public String methodName(){
                return "show";
            }
        }
  1. 调用注解中的抽象方法获取配置的属性值
package cn.itcast.annotation;

import java.lang.reflect.Method;

@Pro(className = "cn.itcast.annotation.Demo1", methodName = "show")
public class ReflectTest {
    public static void main(String[] args) throws Exception{
        //解析注解
        //1.1获取该类的字节码文件对象
        Class<ReflectTest> reflectTestClass = ReflectTest.class;
        //其实就是在内存中生成了一个该注解接口的子类实现对象
        Pro an = reflectTestClass.getAnnotation(Pro.class);
        String className = an.className();
        String methodName = an.methodName();
        System.out.println(className);
        System.out.println(methodName);
        Class cls = Class.forName(className);
        Object object = cls.newInstance();
        Method method = cls.getMethod(methodName);
        method.invoke(object);

    }
}

实例

注解

package cn.itcast.annotation.demo;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

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

package cn.itcast.annotation.demo;

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
    public void show() {
        System.out.println("永无bug ...");
    }
}

测试类

package cn.itcast.annotation.demo;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
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();
        int number = 0;
        BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt"));
        for (Method method : methods) {
            if (method.isAnnotationPresent(Check.class)) {
                try {
                    method.invoke(c);
                } catch (Exception e) {
                    number ++;
                    bw.write(method.getName() + " 方法出现了异常");
                    bw.newLine();
                    bw.write("异常的名称" + e.getCause().getClass().getSimpleName());
                    bw.newLine();
                    bw.write("异常的原因" + e.getCause().getMessage());
                    bw.newLine();
                }
            }
        }
        bw.write("本次测试一共出现" + number + " 次异常");
        bw.flush();
        bw.close();

    }
}

生成的bug.txt

div 方法出现了异常
异常的名称ArithmeticException
异常的原因/ by zero
本次测试一共出现1 次异常

猜你喜欢

转载自blog.csdn.net/qq_37717458/article/details/89151784