mobaijun 发表于 2021-7-30 09:23

Java基础之单元测试和注解学习笔记分享

## Junit单元测试

> 测试分类:
>
> * 黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值。
> * 白盒测试:需要写代码的。关注程序具体的执行流程。

![黑盒测试和白盒测试](https://cdn.jsdelivr.net/gh/mobaijun/blog_css_js/image/blog_images/20200623210425.png)

> 什么是单元测试
>
> * 对一部分代码进行测试

## Junit介绍

> Junit是一个Java语言的单元测试框架,属于白盒测试,简单理解为可以用于取代java的main方法。Junit属于第三方工具,需要导入jar包后使用。
>
> 什么是Junit:
>
> * 是第三方写的一个单元测试框架,可以取代main方法
> * Junit需要导入jar包,压缩包,里面有很多的java代码,因为Junit使用广泛,所以idea就已经包含了Junit,只需要引入即可
>
> Junit的好处:
>
> * 可以单独测试一部分代码
>
> Junit的使用步骤:
>
> > * 编写测试类
> >
> > * 编写测试方法:
> >
> >   * 必须是public修饰符的
> >
> >   * 必须是没有返回值的void
> >
> >   * 必须没有参数
> >
> >   > public void 方法名() {
> >   >                         测试代码
> >   >          }
> >
> >   * 给测试方法添加@Test注解
> >
> >   * 运行测试方法
> >
> >   * 看结果
> >
> >   * 绿色:表示通过
> >   * 红色:表示失败

## Junit的使用

> * 编写测试类,简单理解Junit可以用于取代java的main方法
> * 在测试类方法上添加注解 @test
> * @Test修饰的方法要求:public void 方法名() {…} ,方法名自定义建议test开头,没有参数。
> * 添加Junit库到lib文件夹中,然后进行jar包关联
> * 使用:点击方法左侧绿色箭头,执行当前方法(方法必须标记@Test)。执行结果红色:代表失败;执行结果 绿色:代表成功

~~~java
package com.mobai;

import org.junit.Test;
import java.util.Arrays;
/**
* Software:IntelliJ IDEA 2020.1 x64
* Author: MoBai·杰
* ClassName:Test
* 类描述:Junit测试
*/
public class TestDemo1 {

    @Test
    public void test01() {
      for (int i = 0; i < 100; i++) {
            System.out.println(" i: " + Arrays.class.getClass());
      }
    }
}
~~~

## 常用注解

> * @Test,用于修饰需要执行的测试方法
> * @Before,修饰的方法会在测试方法之前被自动执行
> * @After,修饰的方法会在测试方法执行之后自动被执行

~~~java
package com.mobai;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.Arrays;
/**
* Software:IntelliJ IDEA 2020.1 x64
* Author: MoBai·杰
* ClassName:Test
* 类描述:Junit测试
*/
public class TestDemo1 {

    @Before
    public void testBefore() {
      System.out.println(" 前置方法 ");
    }

    @After
    public void testAfter() {
      System.out.println(" 后置方法 ");
    }

    @Test
    public void test01() {
      for (int i = 0; i < 10; i++) {
            System.out.println(" i: " + Arrays.class.getClass());
      }
    }
}
~~~

## Class对象

!(https://cdn.jsdelivr.net/gh/mobaijun/blog_css_js/image/blog_images/20200623230456.png)

> * 当程序需要用到Person类的时候会将Person.class加载到内存中
> * 将Person.class加载到内存,分别存放成员变量,成员方法,构造方法
> * Java虚拟机会主动创建一个Class对象,描述方法区中的Person.class的内容

## 反射的概念

> > 什么是反射?
>
> * 在程序运行的过程中,通过Class对象得到类中构造方法,成员方法,成员变量,并操作他们
>
> > 反射的应用场景
>
> * idea等开发工具的智能提示
> * 开发框架 SSM
>
> > 反射的好处
>
> * 可以在程序运行过程中,操作这些对象。
> * 可以解耦,提高程序的可扩展性。

## 三种获取Class对象的信息

| 获取CLASS对象的方式   | 作用                         | 应用场景                                                |
| ----------------------- | ---------------------------- | --------------------------------------------------------- |
| 类名.class            | 通过类名的属性class获取      | 多用来传递参数                                          |
| Class.forName(“类全名”) | 通过指定的字符串路径获取   | 多用来加载配置文件,将类名定义在配置文件中,读取文件,加载类 |
| 对象.getClass()         | 通过对象的getClass()方法获取 | 多用来获取对象的字节码                                    |

> 三种获取Class对象的方式
>
> * 类名.class
> * 对象名.getClass()
> * Class.forName(类全名)/(类全名就是:包名.类名)
>
> > 注意:这三种方式得到的都是同一个对象

~~~java
/**
* Software:IntelliJ IDEA 2020.1 x64
* Author: MoBai·杰
* ClassName:TestClass
* 类描述: 三种获取Class对象的方式
*/
public class TestClass {
    public static void main(String[] args) throws Exception {
      // 1.类名.class
      Class clz = Book.class;
      System.out.println("clz = " + clz);

      // 2.通过对象名.getClass()
      Book book = new Book();
      Class clz2 = book.getClass();
      System.out.println("clz2 = " + clz2);

      // 3.通过Class.forName(类全名)
      Class clz3 = Class.forName("com.mobai.annion.Book");
      System.out.println("clz3 = " + clz3);

      // 这三种方式得到的都是同一个对象
      System.out.println(clz2 == clz3);
      System.out.println(clz == clz2);
    }
}
~~~

## 获取Class对象的信息

> 常用方法:

| **String getSimpleName();** | 获得简单类名,只是类名,没有包                                 |
| --------------------------- | ------------------------------------------------------------ |
| **String getName();**       | **获得完整类名,包含包名 + 类名**                           |
| **T newTnstatance();**      | 创建此Class对象所表示的类是一个新实例<br />要求:类必须有<public>的无参构造方法 |

~~~java
public class Test02 {
    public static void main(String[] args) throws ClassNotFoundException {
      // 1.得到Class对象
      Class clazz = Class.forName("com.mobai.annion.Book");

      // 2.调用方法getName
      String name1 = clazz.getName();
      // name1 = com.mobai.annion.Book
      System.out.println("name1 = " + name1);

      // 2.1调用方法getSimpleName
      String name2 = clazz.getSimpleName();
      // name2 = Book
      System.out.println("name2 = " + name2);
    }
}
~~~

## 反射获取public的构造方法

> Constructor类作用:
>
> * 表示类中构造方法
>
> 如何得到public的Constructor对象:
>
> * 获取Class对象
> * 通过Class对象获取构造方法
>
> > cls.getConstructor(); 获取一个public的构造方法
> >
> > cls.getConstructors(); 获取所有public的构造方法
>
> * Constructor类的方法:
>
>   * newInstance(); 创建对象
>
> * 基本数据类型与引用数据类型的Class对象
>
>   > int.class != Integer.class

* 创建一个学生类

~~~java
/**
* Software:IntelliJ IDEA 2020.1 x64
* Author: MoBai·杰
* ClassName:Student
* 类描述: 学生类
*/
public class Student {
    private String name;
    private Integer age;

    public String getName() {
      return name;
    }

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

    public Integer getAge() {
      return age;
    }

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

    public Student(String name) {
      this.name = name;
    }

    public Student(Integer age) {
      this.age = age;
    }

    public Student(String name, Integer age) {
      this.name = name;
      this.age = age;
    }

    public Student() {
    }

    @Override
    public String toString() {
      return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
~~~

* 测试类

~~~java
public class TestDemo1 {
    public static void main(String[] args) throws Exception {
      Class<?> clazz = Class.forName("com.mobai.fanshe.Student");
      // 1.获取所有public的构造方法
      Constructor<?>[] constructors = clazz.getConstructors();
      for (Constructor<?> constructor : constructors) {
            System.out.println("constructor = " + constructor);
      }

      // 2.获取一个public构造方法
      Constructor<?> constructors1 = clazz.getConstructor();
      // public com.mobai.fanshe.Student()
      System.out.println("constructors1 = " + constructors1);

      // 2.1反射获取指定的构造方法
      Constructor<?> constructor = clazz.getConstructor(String.class, Integer.class);
      // public com.mobai.fanshe.Student(java.lang.String,java.lang.Integer)
      System.out.println("constructor = " + constructor);
    }
}
~~~

## 反射通过Constructor创建对象

~~~java
public class TestDemo1 {
    public static void main(String[] args) throws Exception {
      Class<?> clazz = Class.forName("com.mobai.fanshe.Student");
      // 1.获取所有public的构造方法
      Constructor<?>[] constructors = clazz.getConstructors();
      for (Constructor<?> constructor : constructors) {
            System.out.println("constructor = " + constructor);
      }

      // 2.获取一个public构造方法
      Constructor<?> constructors1 = clazz.getConstructor();
      // public com.mobai.fanshe.Student()
      System.out.println("constructors1 = " + constructors1);

      // 2.1反射获取指定的构造方法
      Constructor<?> constructor = clazz.getConstructor(String.class, Integer.class);
      // public com.mobai.fanshe.Student(java.lang.String,java.lang.Integer)
      System.out.println("constructor = " + constructor);

      // 3.使用newInstance创建对象
      Object instance = constructor.newInstance("墨白君", 15);
      // Student{name='墨白君', age=15}
      System.out.println("instance = " + instance);
    }
}
~~~

## 反射获取声明的构造方法

> 声明的Constructor就是类中有的构造方法,不管权限
>
> 如何得到声明的Constructor:
>
> * 得到Class对象
> * 通过Class对象得到声明Constructor
>
> > Declared的规律:
> >
> > * cls.getConstructor();获取一个public的构造方法
> > * cls.getConstructors();获取所有public的构造方法
> > * cls.getDeclaredConstructors(); 得到所有声明的构造方法
> > * cls.getDeclaredConstructor(); 得到一个声明的构造方法
> > * 没有Declared是得到public的
> > * 有Declared是得到声明的
> > * 没有s的是得到一个
> > * 有s的是得到多个
>
> 对私有构造方法我们在使用之前调用
>
> * con.setAccessible(true); 暴力反射

* 学生类

~~~java
public class Student {
    private String name;
    private Integer age;

    public String getName() {
      return name;
    }

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

    public Integer getAge() {
      return age;
    }

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

    public Student(String name) {
      this.name = name;
    }

    private Student(Integer age) {
      this.age = age;
    }

    public Student(String name, Integer age) {
      this.name = name;
      this.age = age;
    }

    public Student() {
    }

    @Override
    public String toString() {
      return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
~~~

* 测试类

~~~java
public class TestDemo2 {
    public static void main(String[] args) throws Exception {
      // 1.获取Class对象
      Class<?> clazz = Class.forName("com.mobai.fanshe.Student");

      // 2.得到一个申明的构造方法
      Constructor<?> constructor = clazz.getDeclaredConstructor(Integer.class);
      // private com.mobai.fanshe.Student(java.lang.Integer)
      System.out.println("constructor = " + constructor);

      // 暴力反射
      constructor.setAccessible(true);
      // 创建对象
      Object instance = constructor.newInstance(16);
      System.out.println("instance = " + instance);
    }
}
~~~

## Class的newInstance方法创建对象

> Class的newInstance方法就是使用无参构造创建对象.

~~~java
public class TestDemo3 {
    public static void main(String[] args) throws Exception {
      // 获取Class对象
      Class<?> clazz = Class.forName("com.mobai.fanshe.Student");
      // 获取无参构造
      Constructor<?> constructor = clazz.getConstructor();
      // 通过无参构造创建对象
      Object instance = constructor.newInstance();
      // instance = Student{name='null', age=null}
      System.out.println("instance = " + instance);


      // Class对象创建对象的方法
      Student instance1 = (Student) clazz.newInstance();
      // instance1 = Student{name='null', age=null}
      System.out.println("instance1 = " + instance1);
    }
}
~~~

## 反射获取Method

> * Method类作用
>   * 表示类中的方法
>
> * 如何得到Method对象
>   * 得到Class对象
>   * 通过Class对象获取Method
>   * cls.getMethods(); 获取所有public的方法
>   * cls.getMethod(); 获取一个public的方法
>   * cls.getDeclaredMethods(); 获取所有声明的方法
>   * cls.getDeclaredMethod(); 获取一个声明的方法
>
> * Method类中的方法: 得到Method就可以调用这个方法
>   * Object invoke(Object target, Object... args);
>   * Object target: 调用方法的对象
>   * Object... args: 调用方法时传递的参数
>   * Object: 返回值类型

* 学生类

~~~java
public class Student {
    private String name;
    private Integer age;

    public String getName() {
      return name;
    }

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

    public Integer getAge() {
      return age;
    }

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

    public Student(String name) {
      this.name = name;
    }

    private Student(Integer age) {
      this.age = age;
    }

    public Student(String name, Integer age) {
      this.name = name;
      this.age = age;
    }

    public Student() {
    }

    @Override
    public String toString() {
      return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
~~~

* 通过class获取所有方法

~~~java
public class TestDemo4 {
    public static void main(String[] args) throws Exception {
      // 获取Class对象
      Class<?> clazz = Class.forName("com.mobai.fanshe.Student");
      // 通过CLass获取方法
      Method[] methods = clazz.getMethods();
      // 遍历获取所有public方法
      for (Method method : methods) {
            System.out.println("method = " + method);
      }

      // 获取一个方法
      Method setName = clazz.getMethod("setName", String.class);
      // setName = public void com.mobai.fanshe.Student.setName(java.lang.String)
      System.out.println("setName = " + setName);

      // 获取所有声明的方法
      Method[] methods1 = clazz.getDeclaredMethods();
      for (Method method : methods1) {
            System.out.println("method = " + method);
      }
      // 获取一个声明的方法
      Method getName = clazz.getDeclaredMethod("getName");
      // public java.lang.String com.mobai.fanshe.Student.getName()
      System.out.println("getName = " + getName);
    }
}
~~~

## 什么是注解?

> > **注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、 接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。**
>
> * **Annotnation的作用:**
>   * **说明程序的,给计算机解释**
>   * **可以被其他程序读取**
> * **Annotnation的格式**
>   * **注解是以"@注解名称"在代码中存在的,还可以添加一些参数**
> * **Annotnation在哪里使用?**
>   * **可以附加在package,class,method,field等上面,相当于给他们添加了额外的辅助信息,可以通过反射机制编程实现对这些元数据的访问**
>
> * **作用分类:**
>   * **编写文档:通过代码里标识的注解生成文档【生成文档doc文档】**
>   * **代码分析:通过代码里标识的注解对代码进行分析【使用反射】**
>   * **编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】**
> * **常见注解:**
>   * **@author:用来标识作者名,eclipse开发工具默认的是系统用户名。**
>   * **@since:从那个版本开始**
>   * **@version:用于标识对象的版本号,适用范围:文件、类、方法。**
>   * **@Override:用来修饰方法声明,告诉编译器该方法是重写父类中的方法,如果父类不存在该方法,则编译失败。**

## 自定义注解

> **自定义注解: 注解本质上就是一个接口,该接口默认继承Annotation接口**
>
> ~~~java
> public interface MyAnno extends java.lang.annotation.Annotation {}
> ~~~

* **自定义注解**

~~~java
// 空注解
public @interface Anno01 {
}
~~~

* **有参数的注解**

> **只能使用Java提供的基本数据类型,String可以,Class可以,枚举类,其他暂未测试,包装类不可以.自定义类不可以**

~~~java
public @interface Anno02 {
    // 属性
    String name();
    double price() default 99;
    String[] authors();

//    Anno01 abc(); // 注解
//    WeekDay w();
//    Class p();
}

enum WeekDay {
    SUN, MON
}

class Person {

}
~~~

## 注解的属性类型

> * 八种基本数据类型
> * String,注解,枚举,Class
> * 以上类型的一维数组
>
> > 自定义类不可以

## 使用自定义注解

> * **注解可以用来保存数据**
>
> * **使用自定义注解格式:**
>
>   > @注解名(属性名=属性值, 属性名=属性值)
>
> 注意:注解的属性可以有默认值,当使用注解时不赋值就使用默认值,赋值就按照赋的值

* **定义注解**

~~~java
package com.mobai.annaction;

// 自定义注解
public @interface MoBai {
    // 名字
    String name();

    // 默认属性
    String value() default "框架师";
}
~~~

* **使用注解**

~~~java
public class AnnactionTest {

    @MoBai(name = "墨白",value = "")
    public static void main(String[] args) {
    }

    @Test
    public void annTest(){
    }
}
~~~

## 自定义注解的特殊情况

> **当注解只有一个属性,并且属性名是value时,在使用注解时可以省略属性名**

* **自定义注解**

~~~java
public @interface MoBai {
    String value();
}
~~~

* **使用注解**

~~~java
@MoBai("abc")
public class Demo11 {
    @MoBai(value = "abc")
    public static void main(String[] args) {
    }
}
~~~

## 元注解

> * **@Target**
> * **@Retention**
>
> > **修饰注解的注解叫做元注解**

#### @Target元注解:

> * **默认情况注解可以放在任意位置**
>
>   > * **限制注解可以放在哪些位置**
>   >   * **@Target(ElementType.TYPE)         注解只能放在类或者接口上**
>   >   * **@Target(ElementType.CONSTRUCTOR)    注解只能放在构造方法上**
>   >   * **@Target(ElementType.FIELD)          注解只能放在成员变量上**
>   >   * **@Target(ElementType.METHOD)         注解只能放在成员方法上**
>   >   * **@Target(ElementType.LOCAL_VARIABLE) 注解只能放在局部变量上**

#### @Retention元注解:

> * **默认是CLASS阶段**
>
> > * **限制注解可以活到什么时候**
> >
> >   > * **SOURCE = = = = = = = = = = >>CLASS = = = = = = = = = = = = = >>RUNTIME**
> >   > * **源代码阶段(.java) = = = = >>编译 = = = >>.class = = = = = = >>运行**

* **自定义注解**

~~~java
//@Target(ElementType.TYPE) // 注解只能放在类或者接口上
//@Target(ElementType.CONSTRUCTOR) // 注解只能放在构造方法上
//@Target(ElementType.FIELD) // 注解只能放在成员变量上
//@Target(ElementType.METHOD) // 注解只能放在成员方法上
//@Target(ElementType.LOCAL_VARIABLE) // 注解只能放在局部变量上
@Retention(RetentionPolicy.RUNTIME)
public @interface MoBai {
}
~~~

* **使用注解**

~~~java
@MoBai
public class Demo12 {
    @MoBai
    private String a;

    @MoBai
    public Demo12() {

    }

    @MoBai
    public static void main(String[] args) {
      @MoBai
      int x = 10;
    }
}
~~~

## 注解的解析

> * **什么是注解解析?**
>
> > **获取注解中保存的数据**
>
> * **注解解析相关接口?**
>
> > * **AnnotatedElement接口中:**
> >
> >   * **Annotation getAnnotation(Class<T> annotationClass) 获取一个注解**
> >
> >   * **Annotation[] getAnnotations()获取多个注解**
> >
> >   * **boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 判断有没有指定的注解**
> >
> >   > **注意: Constructor , Field , Method实现了AnnotatedElement接口**
>
> * **如何解析注解?**
>
> > * **注解在谁头上,就用谁来获取注解**
> > * **注解在构造方法上,使用Constructor对象获取注解**
> > * **注解在成员方法上,使用Method对象获取注解**
> > * **注解在成员变量上,使用Field对象获取注解**
> > * **使用反射**

* **创建自定义注解**

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

/**
* Software:IntelliJ IDEA 2020.1 x64
* Author: MoBai·杰
* [@]Interface:MoBook
* 注解描述: 自定义注解
*/
// 限定方法使用
@Target(ElementType.METHOD)
// 活到运行时候
@Retention(RetentionPolicy.RUNTIME)
public @interface MoBook {
    String name();

    double price();

    String[] authors();
}
~~~

* **定义数据类使用注解**

~~~java
/**
* Software:IntelliJ IDEA 2020.1 x64
* Author: MoBai·杰
* ClassName:Book
* 类描述: 书籍类
*/
public class Book {
    @MoBook(name = "框架师", price = 25.0, authors = "墨白")
    public void sell() {

    }
}
~~~

* **测试类**

~~~java
public class TestMoBook {
    public static void main(String[] args) throws Exception {
      // 1.获取Class对象
      Class<?> className = Class.forName("com.mobai.annion.Book");
      // 2.获取Method对象
      Method method = className.getMethod("sell");
      // 3.通过method对象获取注解
      Annotation[] annotations = method.getAnnotations();
      // 4.遍历数组
      for (Annotation annotation : annotations) {
            System.out.println("annotation = " + annotation);
      }
      // 3.3 boolean isAnnotationPresent​(Class<? extends Annotation> annotationClass)
      //判断有没有指定的注解
      boolean b = method.isAnnotationPresent(MoBook.class);
      System.out.println(b);
      if (b) {
            // 3.2 Annotation getAnnotation​(Class<T> annotationClass) 获取一个注解
            MoBook ann = method.getAnnotation(MoBook.class);
            System.out.println(ann.name() + "::" + ann.price() + "::" + Arrays.toString(ann.authors()));
      }
    }
}
~~~

lyj996 发表于 2021-7-30 10:19

好,学习了

13502501892 发表于 2021-7-30 10:28

多学分享 祝你早日秀发飘飘

jasonwood 发表于 2021-7-30 13:01

最近在学这方面的,先收藏了

不知道改成啥 发表于 2021-7-30 13:38

mk下,正在转java...
页: [1]
查看完整版本: Java基础之单元测试和注解学习笔记分享