Junit单元测试

Junit使用:白盒测试

步骤:

  1. 定义一个测试类
  2. 定义一个测试方法:可以独立运行
  3. 给方法加上@Test注解
  4. 导入junit依赖

判定结果:一般使用断言操作处理结果

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

@Before:修饰的方法会在测试方法之前被自动执行

@After:修饰的方法会在测试方法执行之后自动被执行,即使测试方法报错也同样执行

反射

反射:将类的各个组成部分封装为其他对象

反射是框架设计的灵魂,在框架的基础上进行软件开发能够有效的简化编码工作

如图,Java代码在计算机中运行分为如下三个阶段:

1

其中,由Sourse源代码阶段到Class类对象阶段的过程就是反射机制

反射的好处

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

class对象

获取Class对象的方式

  • Sourse源代码阶段——**Class.forName(“全类名”)**:将字节码文件加载进内存,返回Class对象——多用于配置文件,将类名定义在配置文件中,读取文件,加载类

    其中全类名即包名.类名,如:PersonTest.Person,且由于类名可能有误,该方法可能会出现异常

  • Class类对象阶段——类名.class:通过类名的属性class获取——多用于参数的传递

  • RunTime运行时阶段——**对象.getClass()**:getClass()方法定义在Object类中,即被所有类直接或间接继承——多用于对象的获取字节码的方式

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

Class对象功能

  • 获取成员变量

    Field[] getFields() //获取所有public修饰的成员变量
    Field getField(String name) //获取指定名称的 public修饰的成员变量
    Field[] getDeclaredFields() //获取所有的成员变量,不考虑修饰符
    Field getDeclaredField(String name)
  • 获取构造方法

    Constructor<?>[] getConstructors()  
    Constructor<T> getConstructor(类<?>... parameterTypes)
    Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
    Constructor<?>[] getDeclaredConstructors()
  • 获取成员方法

    Method[] getMethods()  //获取所有public修饰的方法
    Method getMethod(String name, 类<?>... parameterTypes)
    Method[] getDeclaredMethods()
    Method getDeclaredMethod(String name, 类<?>... parameterTypes)
  • 获取类名

    String getName()  
    //使用类对象调用,得到的类名是全类名

    Field:成员变量

  • 设置值

    void set(Object obj,object value)
    //传入成员变量名,以及设置给该变量的值
  • 获取值

    get(Object obj)
  • 忽略访问权限修饰符的安全检查

    setAccessible(true);		//暴力反射

    Constructor:构造方法

创建对象:

T newInstance(Object… initargs)
如:
Constructor constructor=personClass.getConstructor(String.class,int.class); //得到构造方法对象
constructor.newInstance("小明",12); //用构造方法对象创建personClass类对象 对应的类 的对象

如果使用空参创建对象,操作可以简化:使用class对象的newInstance方法

Method:方法对象

执行方法:

Object invoke(Object obj,Object… args)
//需要传入方法对象,如果有参数,需要传入参数

获取方法名:

String getName()
//使用方法对象调用,得到的是方法名

反射-案例

需求:完成一个“框架”,在不改变该类任何代码的前提下,可以创建任意类的对象,并调用其中任意方法

实现:

  1. 配置文件
  2. 反射

步骤:

  1. 将需要创建的对象的全类名和需要执行的方法定义在配置文件

    创建一个pro.properties文件于同目录下,并定义全类名与执行方法,如:

    className=test1.test.Person	//此处写Person类的全类名
    methodName=breathe //此处写需要调用的方法
  2. 在程序中加载读取配置文件

    Properties pro=new Properties();	//创建一个Properties对象
    ClassLoader classLoader=Test.class.getClassLoader(); //获取测试类的类加载器
    InputStream is=classLoader.getResourceAsStream("pro.properties"); //用类加载器的getResourceAsStream方法获取文件pro.properties路径作为字节流
    pro.load(is); //用pro对象调用load方法,加载is路径下配置文件到pro中
    String className=pro.getProperty("className");
    String methodName=pro.getProperty("methodName");
    //获取配置文件中的className和mehodName存为变量
  3. 使用反射来加载类文件进内存

    Class cls=Class.forName(className);	//用类名获取类对象
  4. 创建对象

    Object obj=cls.newInstance();	//用class的newInstance方法创建对象
    Method method=cls.getMethod(methodName); //用方法名获取该类中的同名方法
  5. 执行方法

    method.invoke();	//通过方法对象执行方法

    通过该种方法,可以在不修改原码的基础上变更创建对象的类和对象调用的方法,只需要修改配置文件中的值即可

注解

也称元数据,放在包、类、字段、方法、局部变量等前面,用于对这些元素进行说明注释,使用注解时写作:@注解名称

作用分类:

编写文档:通过代码里标识的元数据生成文档【通过javadoc命令生成doc文档】

代码分析:通过代码里标识的元数据对代码进行分析【使用反射】

编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【@Override】

JDK中预定义的注解

@Override:用于检测被该注解标注的方法是否继承自父类(接口)

@Deprecated:该注解标注的内容表示已过时

@SuppressWarnings:用于压制警告,一般传递参数all,写作@SuppressWarnings(“all”)

自定义注解

格式:

元注解
public @interface 注解名{
属性列表;
}

本质

注解本质上就是一个接口,该接口默认继承自Annotation接口

自定义注解编译为class文件后再用javap命令反编译,java会自动生成一个文件,其文件内容类似如下:

public interface MyAnno extends java.lang.annotation.Annotation{}

属性

接口中的抽象方法,其要求如下:

  1. 属性的返回值类型必须如下
    • 基本数据类型
    • String
    • 枚举
    • 注解
    • 以上类型的数组
  2. 定义了属性,在使用时需要给属性赋值
    1. 如不想赋值,则需要加默认,即default后加初始值
    2. 如果只有一个属性需要赋值,且属性为value,则value可以省略,直接写值
    3. 数组赋值时,值使用{}包裹,若数组只有一个值,则{}可以省略

元注解

用于描述注解的注解,可用JDK已经定义的注解修饰自定义的注解,常用元注解如下:

  • @Target:描述注解能够作用的位置
    • ElementType取值:
      • TYPE:可以作用于类上
      • METHOD:可以作用于方法上
      • FIELD:可以作用于成员变量上
  • @Retention:描述注解被保留的阶段,一般使用RetentionPolicy.RUNTIME,表示自定义的注解会保留到class字节码中,被JVM读取到
  • @Documented:描述注解是否被抽取到api文档中
  • @Inherited:描述注解是否被其修饰的类的子类继承

程序中使用(解析)注解

主要是用于获取注解中定义的属性值

  1. 获取注解定义的位置的对象(Class,Method,Field)
  2. 获取指定的注解:getAnnotation(Class),其实就是在内存中生成了一个该注解接口的子类实现对象
  3. 调用注解中的抽象方法获取配置的属性值

相关使用

判断一个方法是否被一个注解修饰:

方法名.isAnnotationPresent(注解名.class)

BufferedWriter对象创建文档并写入:

BufferedWriter bw=new BufferedWriter(new FileWriter("bug.txt"));
//通过BufferedWriter对象创建一个txt文档用于记录bug
bw.write(method.getName()+"方法出现异常:"+); //写入记录
bw.newline(); //新起一行
bw.write("异常的名称:"+e.getCause().getClass().getSimpleName());
bw.newline(); //其中e是捕捉到的异常,通过该方法可以获得其简单类名
bw.write("异常的原因"+e.getCause().getMessage());
bw.flush(); //将以上写入的内容打印到对应txt文档
bw.close(); //关闭缓冲区

小结:

  • 比起自定义注解,已有的注解更加常用
  • 注解是给编译器和解析程序使用的
  • 注解不是程序的一部分,可以理解为一个标签