单元测试

:::tips

概述:

  • 对部分代码进行测试

    Junit

  • JUnit 是一个 Java 编程语言的单元测试工具。是一个非常重要的测试工具

    Junit特点

  • JUnit 是一个开放源代码的测试工具

  • JUnit 可以很方便的对Java中的方法进行测试,提高测试效率
  • JUnit 在一个条中显示进度。如果运行良好则是绿色;如果运行失败,则变成红色

    Junit常用注解

    @Test 表示测试该方法
    @Before 每次在测试方法运行前,都会运行
    @After 每次在测试方法运行后,都会运行
    @BeforeClass 修饰静态方法,在所有测试的方法运行前运行,只会运行一次
    @AfterClass 修饰静态方法,在所有测试的方法运行后运行,只会运行一次 :::

    反射

    :::tips

    反射机制

  • 在程序的运行过程中, 通过Class对象得到类中的信息(构造方法,成员方法,成员变量), 并操作他们这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制

  • 利用反射调用它类中的属性和方法时,无视修饰符

    三种获取Class对象的方式

  • 类名.class

  • 对象.getClass()
  • Class.forName(“类全名”);

    Class类中的方法
  • String getSimpleName(): 获得类名

  • String getName(): 获得类全名:包名+类名

反射获取构造方法Constructor并使用

Class类中获取构造方法
  • Constructor<?>[ ] getConstructors():返回public修饰的所有构造方法对象的数组
  • Constructor getConstructor(Class<?>… parameterTypes):返回public修饰的单个构造方法对象
  • Constructor<?>[ ] getDeclaredConstructors():返回任意权限的所有构造方法对象的数组
  • Constructor getDeclaredConstructor(Class<?>… parameterTypes):返回任意权限的单个构造方法对象

    Constructor类中用于创建对象的方法
  • T newInstance(Object… initargs):根据指定的构造方法创建对象

  • setAccessible(boolean flag):设置为true,表示允许对私有权限的访问

反射获取成员方法Method并使用

Class类中获取成员方法
  • Method[] getMethods():返回所有public的成员方法对象的数组,包括继承的
  • Method getMethod(String name, Class<?>… parameterTypes) :返回单个public的成员方法对象
    • 参数name:表示方法名 ;参数parameterTypes:表示方法的形参类型
  • Method[] getDeclaredMethods():返回所有成员方法对象的数组,不包括继承的
  • Method getDeclaredMethod(String name, Class<?>… parameterTypes):返回单个成员方法对象

    Method类中的方法
  • Object invoke(Object obj, Object… args):运行方法

    • 参数obj:调用成员方法的对象(如果调用的是静态方法,该参数为null即可)
    • 参数args :可变参数,表示调用方法时传递的实参(如果没有就不写)
    • 返回值Object :方法的返回值(如果没有就不用接)

反射获取成员变量Field并使用

Class类中获取成员变量
  • Field getField(String name):返回单个公共成员变量对象
  • Field[] getFields():返回所有公共成员变量对象的数组,包括继承的public成员变量
  • Field getDeclaredField(String name):返回单个成员变量对象
  • Field[] getDeclaredFields():返回所有本类成员变量对象的数组 不包括继承的

    Field类中的方法
  • void set(Object obj, Object value):赋值。参数obj为成员变量所在的对象,参数value为成员变量的值

  • Object get(Object obj) :获取值。参数obj为成员变量所在的对象 :::

注解(Annotation)

:::tips

概述

  • 注解的主要作用:对我们的程序进行标注。通过注解可以给类增加额外的信息
  • 注解是给编译器或JVM看的,编译器或JVM可以根据注解来完成对应的功能

    自定义注解格式

  1. public @interface 注解名称 {
  2. public 属性类型 属性名();
  3. }

注解中的属性可以有默认值,格式为:数据类型 属性名() default 默认值;
属性类型可以为基本数据类型,String,Class,注解,枚举及以上类型的数组

自定义注解的使用

  • 使用自定义注解格式

    • @注解名(属性名1=值1, 属性名2=值2)
      注意
  • 注解中,有默认值的属性可以不赋值

  • 没有默认值的属性一定要赋值

    自定义注解的特殊情况
  • 当注解中只有”一个属性”,并且属性名是”value”,使用注解时,可以省略value属性名

元注解

元注解是指: 修饰注解的注解

@Target 指定注解能在哪里使用
@Retention 注解的生命周期,可以理解为保留时间
@Documented 表示注解在生成帮助文档时会保留下来
@Inherited 表示该注解会被子类继承

@Target
  • 作用:用来标识注解使用的位置
  • 可使用的值定义在 ElementType枚举类 中,常用值如下:

    • TYPE,类,接口
    • FIELD, 成员变量
    • METHOD, 成员方法
    • PARAMETER, 方法参数
    • CONSTRUCTOR, 构造方法
    • LOCAL_VARIABLE, 局部变量
      @Retention
  • 作用:用来标识注解的生命周期

  • 可使用的值定义在 RetentionPolicy枚举类 中,常用值如下:

    • SOURCE:注解只作用在源码阶段,生成的字节码文件中不存在
    • CLASS:注解作用在源码阶段,字节码文件阶段,运行阶段不存在(默认值)
    • RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段

      注解解析

      概述
  • 使用Java反射技术,获得注解的属性数据的过程,称为注解解析

    注解解析相关方法:
  • T getAnnotation(Class annotationClass) 根据注解类型获得对应注解对象

  • Annotation[] getAnnotations() 获得当前结构上使用的所有注解
  • boolean isAnnotationPresent(Class annotationClass) 判断当前结构上是否使用了指定的注解,如果使用了则返回true,否则false
    Field,Method,Constructor,Class 等反射相关的类可以调用以上方法
    ::: ```java import org.junit.Test;

public class Demo {

  1. @Test
  2. public void a() {
  3. System.out.println("测试块1");
  4. }
  5. @Test
  6. public void b() {
  7. System.out.println("测试块2");
  8. }
  9. @Test
  10. public void c() {
  11. System.out.println("测试块3");
  12. }

}

```java
public class Person  {
    //成员变量
    private String name;
    private int age;

    //-------构造方法-------
    public Person(){}

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private Person(String name){
        this.name = name;
    }

    protected Person(int age){
        this.age = age;
    }
    //-----------------------


    //-------get和set方法-------
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }


    //-------其他成员方法-------
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    private int work(int hour){
        System.out.println("一天工作"+hour+"小时");
        return hour*100; //工资
    }

    public static void run(){
        System.out.println("静态的run方法");
    }

    public int  show(int a, int b) {
        return a + b;
    }

}
/*
三种获取Class对象的方式
    方式1:类名.class
    方式2:对象名.getClass()
    方式3:Class.forName("类全名"); //类全名就是 包名.类名
Class类中的方法:
    String getSimpleName(); 获得类名
    String getName();  获得类全名:包名.类名
注意:
    这三种方式获取的是同一个Class对象
    一个类只有一个Class对象(可以理解为一个类只有一个字节码文件)
*/
public class Demo00Reflect {
    public static void main(String[] args) throws ClassNotFoundException {
        //方式1:类名.class
        Class<Person> clazz1 = Person.class;
        System.out.println(clazz1);
        //方式2:对象名.class
        Person p = new Person();
        Class<? extends Person> clazz2 = p.getClass();
        System.out.println(clazz2);
        //方式3:Class.forname("类全名")
        Class<?> clazz3 = Class.forName("demo.test.Person");
        System.out.println(clazz3);
        //三种方式获取的是同一个Class对象
        System.out.println(clazz1 == clazz2);
        System.out.println(clazz1 == clazz3);
        //获得类全名,包名.类名
        System.out.println(clazz1.getName());
        //获得类名
        System.out.println(clazz1.getSimpleName());
    }
}
public class Demo01Reflect {
    public static void main(String[] args) throws Exception {
        //获取Class对象
        Class<Person> clazz = Person.class;
        //反射获取person类所有公开的构造方法
        Constructor<?>[] cons = clazz.getConstructors();
        for (Constructor<?> con : cons) {
            System.out.println(con);
        }
        //获取person类中公开的无参构造方法
        Constructor<Person> con1 = clazz.getConstructor();
        System.out.println(con1);
        //获取person类公开的带参构造方法
        Constructor<Person> con2 = clazz.getConstructor(String.class, int.class);
        System.out.println(con2);
    }
}
/*
    反射方式获取带参构造方法,并创建对象
    反射方式获取无参构造方法,并创建对象
    反射方式获取私有构造方法,并创建对象
Constructor类代表 "构造方法" 这类事物,
    而一个Constructor对象就代表一个具体的构造方法,可以用于创建该构造方法对应的对象。
Constructor类中创建对象的方法:
    T newInstance(Object... initargs):根据指定的构造方法创建对象,
    setAccessible(boolean flag):设置为true,表示取消访问检查
反射方式创建对象特点:
    代码更复杂,但是灵活度更高,而且可以使用任何权限的构造方法创建对象
*/
public class Demo02Reflect {
    public static void main(String[] args) throws Exception {
        //获取Class对象
        Class<Person> clazz = Person.class;
        //nweInstance(构造方法的实参),使用构造器创建对象
        Constructor<Person> con = clazz.getConstructor(String.class, int.class);
        //创建对象
        Person p1 = con.newInstance("张三", 23);
        System.out.println(p1);
        //反射调用私有构造器
        Constructor<Person> dCon = clazz.getDeclaredConstructor(String.class);
        //暴力反射,设置允许访问私有构造器
        dCon.setAccessible(true);
        //创建对象
        Person p = dCon.newInstance("李四");
        System.out.println(p);
    }
}
/*
Method类:
    表示 "成员方法" 这类事物,每一个Method对象代表一个成员方法。

Class类中获取成员方法:(Method对象)
    1.获取public的成员方法:
        Method[] getMethods():获取所有成员方法,包括从父类继承的。
        Method getMethod(String name, Class<?>... parameterTypes) :获取单个成员方法。
               参数name:表示方法名
               参数parameterTypes:表示方法的形参类型

    2.获取本类中任何权限的成员方法:
        Method[] getDeclaredMethods():获取所有成员方法,不包括继承的。
        Method getDeclaredMethod(String name, Class<?>... parameterTypes):获取单个成员方法对象。

注意: int.class 和 Integer.class 是不一样的
*/
public class Demo03Reflect {
    public static void main(String[] args) throws Exception{
        //类全名获取字节码
        Class<?> clazz = Class.forName("demo.test.Person");
        //获取所有的public的方法,包括继承下来的方法
        Method[] met1 = clazz.getMethods();
        for (Method m1 : met1) {
            System.out.println(m1);
        }
        System.out.println();
        //获取任意权限的所有方法,只能获取本类的所有方法
        Method[] met2 = clazz.getDeclaredMethods();
        for (Method m2 : met2) {
            System.out.println(m2);
        }
    }
}
/*
需求: 利用反射根据文件中的不同类名和方法名,创建不同的对象并调用方法。
分析:
    1.通过Properties加载配置文件  load(字符流)
    2.从Properties中获取类名和方法名
    3.通过类名反射得到Class对象
    4.通过Class对象创建一个对象
    5.通过Class对象得到方法
    6.调用方法
*/
public class Demo05Reflect {
    public static void main(String[] args) throws Exception {
        //通过properties加载配置文件
        Properties p = new Properties();
        p.load(new FileReader("Day13Reflect\\ceshi\\config.properties"));
        //从properties中获取类名和方法名
        String className = p.getProperty("className");
        String methodName = p.getProperty("methodName");
        //使用反射运行方法
        //获取字节码对象forName
        Class<?> clazz1 = Class.forName(className);
        //通过Class对象创建方法
        Method met1 = clazz1.getMethod(methodName);
        //通过Class对象创建对象
        Object obj = clazz1.getConstructor().newInstance();
        //运行方法
        met1.invoke(obj);
    }
}

/*
Day13Reflect\\ceshi\\config.properties
className=demo.test.Student
methodName=study
*/
@Anno
@Anno1(name = "张三")
public class Test {
    @Anno1(name = "李四", age = 14)
    private @Anno int a;

    @Anno
    @Anno1(name = "王五")
    public void b(){}
}

public @interface Anno {
}

public @interface Anno1 {
    String name();

    int age() default 0;
}
/*
@Target常用配置
    @Target():
*/
@Week
public class Test {
    @Week
    public Test(int a) {
        this.a = a;
    }

    @Week
    private int a;

    @Week
    private void b(){ }
}

@Target({
    ElementType.TYPE, 
    ElementType.METHOD, 
    ElementType.CONSTRUCTOR, 
    ElementType.FIELD})
public @interface Week {
}
/*
1.注解解析相关方法:
    Annotation[] getAnnotations() 获取所有注解
    <Annotation> T getAnnotation(Class<T> annotationClass) 获取一个指定的注解
    boolean isAnnotationPresent(Class<Annotation> annotationClass) 判断是否有指定的注解
2.如何解析注解?
通过反射来解析注解,注解在谁头上就用谁来解析
    如果注解在类上,使用Class来获取
    如果注解在构造方法上,使用Constructor来获取
    如果注解在成员方法上,使用Method来获取
    如果注解在成员变量上,使用Field来获取
*/
public class Test {
    public static void main(String[] args) throws Exception{
        //反射获取show方法
        //获取字节码对象
        Class<demoBook> clazz = demoBook.class;
        //通过字节码对象获取方法
        Method met1 = clazz.getMethod("show");
        //通过获取的方法去获取注解
        Book an = met1.getAnnotation(Book.class);
        //判断注解是否存在
        boolean ifAnno = met1.isAnnotationPresent(Book.class);
        if (ifAnno) {
            //操作注解获取属性
            System.out.println(an.name());
            System.out.println(an.price());
            System.out.println(Arrays.toString(an.author()));
        }
    }
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Book {
    String name();
    double price();
    String[] author();
}

public class demoBook {
    @Book(name = "红楼梦", price = 29.9, author = {"曹雪芹", "高鹗"})
    public void show() {}
}