1. 程式人生 > >Junit單元測試、反射、註解

Junit單元測試、反射、註解

Junit單元測試

什麼是Junit:

  1. Junit是Java語言編寫的第三方單元測試框架。
  2. 框架就是jar(類庫)包的集合。
  3. junit.jar(idea整合器自帶)

什麼是單元測試:

  1. 在Java中,一個類就是一個單元。
  2. 單元測試就是開發者編寫的一小段程式碼用來對類中的方法功能進行測試。

Junit的作用:

  1. 用來測試類中的方法功能是否正確,保證程式的穩定性和有效性。
  2. 可以讓符合要求的方法獨立執行。

Junit的使用步驟:

1)編寫業務類

  • 在業務類中編寫業務方法,實現某一功能的方法。

2)編寫測試類

  • 在測試類中編寫測試方法,對業務類中的業務方法進行測試。

測試類的命名規範:

以Test開頭,以業務類類名結尾。

測試方法的要求:

1)命名要求:

以test開頭,以為業務方法結尾。

  • 比如業務方法名:saveStudent,那麼測試方法名:testSaveStudent(業務方法名的首字母本來是小寫的,到了測試方法名之後就變成大寫

2)宣告要求:

  • 必須是public修飾的。
  • 必須沒有返回值。
  • 必須沒有引數。
  • 必須使用@Test註解修飾。

如何執行測試方法:

1)選中測試方法名 ‐‐> 右鍵 ‐‐> Run '測試方法名'

  • 執行選中測試方法。

​​​​​​​2)選擇類名 ‐‐> 右鍵 ‐‐> Run '測試類名'

  • 執行選中類所有的測試方法。

​​​​​​​3)選中模組名 ‐‐> 右鍵 ‐‐> Run 'All Tests'

  • 執行選中模組中所有測試類中的所有測試方法。

如何檢視測試結果:

  • 綠色:代表測試成功,沒有問題。
  • 紅色:代表測試出現問題了。

Junit4.0常用註解:

  • @Before:用來修飾方法,該方法會在每一個測試方法執行之前執行一次。
  • @After:用來修飾方法,該方法會在每一個測試方法執行之後執行一次。
  • @BeforeClass:用來靜態修飾方法,該方法會在所有測試方法執行之前執行一次。
  • @AfterClass:用來靜態修飾方法,該方法會在所有測試方法執行之後執行一次。

Junit5.0常用註解:

  •  @BeforeEach:用來修飾方法,該方法會在每一個測試方法執行之前執行一次。
  •  @AfterEach:用來修飾方法,該方法會在每一個測試方法執行之後執行一次。
  •  @BeforeAll:用來靜態修飾方法,該方法會在所有測試方法執行之前執行一次。
  •  @AfterAll:用來靜態修飾方法,該方法會在所有測試方法執行之後執行一次。

測試方法中常用的方法:

斷言:預先判斷某個條件一定成立,如果不成立則程式直接崩潰。

Assert類的靜態方法:

 static void assertEquals(String message, Object expected, Object actual)

  • message: 異常訊息提示字串。
  • expected:期望值。
  • actual:實際值。

示例程式碼:

public class Calcalate {
    // 業務方法:求兩個數之和
    public int sum(int a,int b){
        return a + b;
    }

    // 業務方法:求兩個數之差
    public int sub(int a,int b){
        return a - b;
    }
}
public class TestCalcalate{ 
@Test
    public void testSum(){
        // 呼叫方法求兩個數之和
        int result = c.sum(1, 1);      
        Assert.assertEquals("期望值和實際值不一致", 2, result);
        System.out.println("result = " + result);
    }
}

反射

反射引入:

IDEA是如何知道類或物件有哪些屬性,哪些方法?

  • 通過反射技術在程式執行過程中對類進行了解剖獲得了型別的所有成員。

什麼是反射:

反射是種機制,利用該機制可以在程式的執行過程中對類進行解剖並操作類中的所有成員。

  • 構造方法:通過反射操作構造方法建立類的物件。
  • 成員方法: 通過反射操作成員方法來呼叫方法。
  • 成員變數:通過反射操作成員變數就是給該成員變數賦值和取值。

反射在實際開發中的應用:

  1. 開發IDE(整合開發環境) 比如IDEA,Eclipse
  2. 開發框架 spring,mybatis ...
  3. 框架的學習

反射的前提條件:

  • 要先獲取類的class物件。

class物件

class物件的概述:

class物件的獲取方式:

  1. 通過類名.class獲取。
  2. 通過物件呼叫getClass方法獲取。
  3. 通過Class類的靜態方法forName("類全名字串")獲取。(最常用

class類中常用方法:

String getSimpleName()

  • 獲得類名字串

​​​​​​​String getName()

  • 獲得類全名字串

​​​​​​​T newInstance()

  • 建立類的物件。
  • JDK1.9過時。實質走的是類的無參構造。

反射之操作構造方法

Constructor類概述:

  1. 類中的每一個構造方法都對應一個Constructor類的物件。
  2. 反射操作構造方法就是要獲得對應的Constructor物件類建立該類的物件。

Class類中與Constructor相關的方法:

Constructor getConstructor(Class... parameterTypes)

  • 根據引數型別獲得對應的構造方法物件,只能獲取public修飾

​​​​​​​Constructor getDeclaredConstructor(Class... parameterTypes)

  • 根據引數型別獲得對應的構造方法物件, 包括private修飾

​​​​​​​Constructor[] getConstructors()

  • 獲得類中所有的構造方法物件,返回陣列,只能是public修飾的

​​​​​​​Constructor[] getDeclaredConstructors()

  • 獲得類中所有的構造方法物件,返回陣列,包括private修飾的

Constructor物件常用方法:

T newInstance(Object... initargs)

  • 根據引數建立物件。

​​​​​​​void setAccessible(true)

  • 設定是否取消許可權檢查(暴力反射)
  • true:取消許可權檢查
  • false:預設值,要檢查許可權
  • 要在Constructor物件未呼叫方法之前使用才有效。

示例程式碼:

public class ReflectDemo01 {
    /*
       反射操作public修飾的構造方法
    */
    @Test
    public void test01()throws Exception{
        // 獲得Class物件
        Class c = Student.class;
        // 獲得無引數構造方法物件
        Constructor con = c.getConstructor();
        // 建立物件
        Object stu = con.newInstance();
        System.out.println(stu);
        System.out.println("‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐");
        // 獲得兩個引數構造方法物件
        Constructor conn = c.getConstructor(String.class, int.class);
        // 根據引數建立學生物件
        Object stu02 =  conn.newInstance("jack",20);
        System.out.println(stu02);
    }
    /*
2.4 反射之操作成員方法
2.4.1 Method類概述
2.4.2 Class類中與Method相關的方法
        反射操作private修飾的構造方法
     */
    @Test
    public void test02()throws Exception{
        // 獲得Class物件
        Class c = Student.class;
        // 獲得一個引數構造方法物件
        Constructor con = c.getDeclaredConstructor(String.class);
        // 暴力反射(設定取消許可權檢查)
        con.setAccessible(true);
        // 建立物件
        Object stu = con.newInstance("rose");
        System.out.println(stu);
    }
    /*
        反射之獲得所有構造方法物件
     */
    @Test
    public void test03()throws Exception{
        // 獲得Class物件
        Class c = Student.class;
        // 獲得所有構造方法物件:只能獲得public修飾的
        // Constructor[] cons = c.getConstructors();
        // 獲得所有構造方法物件:包括private修飾的
        Constructor[] cons = c.getDeclaredConstructors();
        for (Constructor con : cons) {
            System.out.println(con);
        }
    }
}

反射之操作成員方法

Method類概述:

  1. 類中的每一個成員方法都是一個Method類的物件。
  2. 反射操作成員方法就是要獲得該成員方法對應的Method物件來呼叫該方法。

Class類中與Method相關的方法:

Method getMethod(String methodName,Class...params)

  • 根據方法名和引數型別獲得對應的成員方法物件,只能獲得public修飾的

​​​​​​​Method getDeclaredMethod(String methodName,Class...params)

  • 根據方法名和引數型別獲得對應的成員方法物件,包括private修飾的

Method[] getMethods()

  • 獲得類所有的成員方法物件,返回陣列,只能獲取public修飾的,包括父類的

Method[] getDeclaredMethods()

  • 獲得類所有的成員方法物件,返回陣列,包括private修飾的,只包括本類的

Methid物件常用方法:

Object invoke(Object obj, Object... args)

  • 呼叫方法。
  • obj:呼叫哪個物件的方法
  • ​​​​​​​​​​​​​​若是非靜態方法,obj = class物件.newInstance()
  • 若是靜態方法,則obj = class物件
  • args:呼叫方法時要傳遞的引數
  • 返回值:呼叫方法返回的結果。

​​​​​​​void setAccessible(boolean flag)

  • 設定是否取消許可權檢查(暴力反射)
  • true:取消許可權檢查
  • false:預設值,要檢查許可權
  • 要在Constructor物件未呼叫方法之前使用才有效。

示例程式碼:

public class ReflectDemo01 {
    /*
        反射操作public修飾的成員方法
     */
    @Test
    public void test01()throws Exception{
        // 獲得Class物件
        Class c = Student.class;
        // 建立學生物件
        Object stu = c.newInstance();
        // 獲得study方法對應Method物件
        Method studyMethod01 = c.getMethod("study");
        // 呼叫方法
        Object result = studyMethod01.invoke(stu);
        System.out.println(result);
        System.out.println("‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐");
        // 獲得有引數的study方法對應Method物件
        Method studyMethod02 = c.getMethod("study",int.class);
        // 傳遞引數呼叫方法
        studyMethod02.invoke(stu,10);
    }
    /*
        反射操作private修飾的成員方法
     */
2.5 反射之操作成員變數
2.5.1 Field類概述
    @Test
    public void test02()throws Exception{
        // 獲得Class物件
        Class c = Student.class;
        // 建立學生物件
        Object stu = c.newInstance();
        // 獲得sleep方法對應Method物件
        Method sleepMethod = c.getDeclaredMethod("sleep",String.class,int.class);
        // 暴力反射
        sleepMethod.setAccessible(true);
        // 呼叫方法
        sleepMethod.invoke(stu,"如?",8);
    }
    /*
        反射操作static修飾的成員方法
     */
    @Test
    public void test03()throws Exception{
        // 獲得Class物件
        Class c = Student.class;
        // 獲得靜態方法對應的Method物件
        Method drinkMethod = c.getDeclaredMethod("drink");
        // 暴力方式
        drinkMethod.setAccessible(true);
        // 呼叫方法
        drinkMethod.invoke(c);
    }
    /*
        反射之獲得所有成員方法物件
     */
    @Test
    public void test04()throws Exception{
        // 獲得Class物件
        Class c = Student.class;
        // 獲得類所有的成員方法物件,返回陣列,只能獲取public修飾的, 包括父類的
        // Method[] methods = c.getMethods();
        // 獲得類所有的成員方法物件,返回陣列,包括private修飾的,只包括本類的
        Method[] methods = c.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
    }
}

反射之操作成員變數

Field類概述:

  1. 類中的每一個成員變數都是一個Field類物件。
  2. 反射操作成員變數就是要獲得成員變數對應的Field物件來給成員變數賦值和取值。

Class類中與Field相關的方法:

Field getField(String name);

  • 根據成員變數名獲得對應的Field物件,只能是public修飾的

* Field getDeclaredField(String name);

  • 根據成員變數名獲得對應的Field物件,包括private修飾

* Field[] getFields();

  • 獲得類所有的成員變數物件,只包括public修飾的

* Field[] getDeclaredFields();

  • 獲得類所有的成員變數物件,包括private修飾的

Field物件常用方法:

void  set(Object obj, Object value) void setInt(Object obj, int i)  void setLong(Object obj, long l) void setBoolean(Object obj, boolean z) void setDouble(Object obj, double d)

  • setXxx方法都是給物件obj的屬性設定使用,針對不同的型別選取不同的方法。
  • 若不是靜態成員變數,則obj = class物件.newInstance()
  • 若是靜態成員變數,則 obj = class物件

​​​​​​​Object get(Object obj)  int getInt(Object obj)   long getLong(Object obj) boolean getBoolean(Object ob) double getDouble(Object obj)

  • getXxx方法是獲取物件obj對應的屬性值的,針對不同的型別選取不同的方法。
  • 若不是靜態成員變數,則obj = class物件.newInstance()
  • 若是靜態成員變數,則 obj = class物件

​​​​​​​void setAccessible(boolean flag)

  • 設定是否取消許可權檢查(暴力反射)
  • true:取消許可權檢查
  • false:預設值,要檢查許可權
  • 要在Field物件未呼叫方法之前使用才有效。

​​​​​​​Class getType()

  • 獲取屬性的型別,返回Class物件。

​​​​​​​String getName()

  • 獲取該成員變數的名稱。

示例程式碼:

public class ReflectDemo01 {
    /*
       反射操作public修飾的成員變數
    */
    @Test
    public void test01()throws Exception{
        // 獲得Class物件
        Class c = Student.class;
        // 建立學生物件
        Object obj = c.newInstance();
        System.out.println(obj);
        // 根據成員變數名獲得field物件
第3章 註解
        Field field01 = c.getField("gender");
        // 給obj物件的gender成員變數賦值
        field01.set(obj, "男");
        System.out.println(obj);
        // 獲得物件obj的gender成員變數值
        Object result = field01.get(obj);
        System.out.println(result);
    }
    /*
        反射操作private修飾的成員變數
     */
    @Test
    public void test02()throws Exception{
        // 獲得Class物件
        Class c = Student.class;
        // 建立學生物件
        Object obj = c.newInstance();
        System.out.println(obj);
        // 根據成員變數名獲得field物件
        Field ageField = c.getDeclaredField("age");
        // 暴力反射
        ageField.setAccessible(true);
        // 給obj物件的age成員變數賦值
        ageField.setInt(obj, 20);
        System.out.println(obj);
        // 獲得物件obj的age成員變數值
        int result = ageField.getInt(obj);
        System.out.println(result);
    }
    /*
        反射之獲得所有成員變數物件
     */
    @Test
    public void test03()throws Exception{
        // 獲得Class物件
        Class c = Student.class;
        // 獲得類所有的成員變數物件,只包括public修飾的
        // Field[] fields = c.getFields();
        // 獲得類所有的成員變數物件,包括private修飾的
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }
    }
}

註解

註解的概述:

  1. 註解是JDK1.5的新特性。
  2. 註解相當一種標記,是類的組成部分,可以給類攜帶一些額外的資訊。
  3. 標記(註解)可以加在包,類,成員變數,方法,方法引數以及區域性變數上。
  4. 註解是給編譯器或JVM看的,編譯器或JVM可以根據註解來完成對應的功能。

註解的作用:

註解的作用就是給程式1帶入引數。

  1. 編譯檢查:@Override
  2. 框架的配置(框架=程式碼+配置)

自定義註解

註解的定義格式:

 @interface 註解名{ }

屬性的作用:

可以讓使用者在使用註解時傳遞引數,增強註解的功能。

屬性的定義格式:

  • ​​​​​​​格式1:資料型別 屬性名();
  • 格式2:資料型別 屬性名() default 預設值;

屬性適用的資料型別:

  1. 八種基本資料型別。
  2. String,Class,註解,列舉類。
  3. 以上資料型別的陣列形成。

屬性定義示例:

@interface Student{     // 姓名(屬性)     String name();     // 年齡     int age() default 18;     // 愛好     String[] hobbies(); }

使用自定義註解

 示例格式:

@Book(value = "紅樓夢",authors = {"曹雪芹"},price = 200) public class BookShelf { }

使用注意事項:

  1. 如果屬性有預設值,則使用註解的時候,這個屬性可以不用賦值。
  2. 如果屬性沒有預設值,那麼在使用註解時一定要給屬性賦值。

特殊屬性value:

  1. 如果註解中只有一個屬性且屬性名為value時,則使用註解給value屬性賦值時可以省略屬性名value。
  2. 如果註解中除了value屬性之外還有其屬性,則使用註解給屬性賦值時value屬性名也不能省略了。

註解的巢狀:

//定義註解
@interface A{
    String value();
    int age() default 200;
}
@interface B{
    String[] value();
    A a();
}
//使用註解
@B(value = {"rose","xx"},a = @A("lily"))

註解之元註解

元註解的概述:

  1. 官方定義的註解。
  2. 用來定義註解的註解。
  3. Java提供的任何非元註解的定義都使用到了元註解。
  4. 一個註解可以使用多個元註解來註解。

常用的元註解:

  1. @Target
  2. @Retention
  3. @Inherited: 用來標明該註解可以被子類繼承。

@Target概述:

  1. 用來說明註解使用的位置,比如成員方法,類,介面,成員變數上。
  2. 如果沒有使用該註解定義註解,則預設註解可以作用在任意成員上。
  3. 可取值定義在ElmentType列舉類中,常用的值有如下幾個:
TYPE 可以使用在類,介面上
FIELD 可以使用成員變數上
METHOD 可以使用成員方法上
CONSTRUCTOR 可以使用構造方法上
LOCAL_VARIABLE 可以使用在區域性變數上
PARAMETER 可以使用方法引數上

示例:@Target(ElementType.METHOD)

  • 可以定義多個範圍,在大括號裡用逗號分割。
  • 例:@Target({ElementType.METHOD,ElementType.FIELD})

@Retention概述:

  1. 用來說明註解的有效範圍(生命週期)。
  2. 可取值定義在RetentionPolicy列舉類中,常用的值有如下幾個:
  • ​​​​​​​SOURCE:原始碼階段,編譯生產的class檔案,執行階段就不存在。
  • CLASS: 位元組碼階段, 註解存在原始碼階段,位元組碼階段,執行階段就不存在。
  • RUNTIME: 執行階段, 註解存在原始碼階段,位元組碼階段,執行階段

​​​​​​​示例:@Retention(RetentionPolicy.RUNTIME)

註解解析

什麼是註解解析:

通過Java技術獲取註解屬性資訊的過程則稱為註解解析。

註解解析相關的類和介面:

  • Annotation: 類,該類是所有註解的父類。
  • AnnotatedElement: 介面 該介面中定義了獲取註解資訊的相關方法。

註解須知:

  • Class,Method,Field,Constructor等類都是實現了AnnotatedElement介面的。

AnnotatedElement介面的方法:

T getAnnotation(Class<T> annotationClass)

  • 獲得當前物件上使用的指定型別的註解

Annotation[] getAnnotations() 

  • 獲得所有註解物件,包括繼承父類的

Annotation[] getDeclaredAnnotations() 

  • 獲得所有註解物件,只能獲得本類的

boolean isAnnotationPresent(Class annotationClass)  

  • 判斷當前物件上是否使用了註定型別的註解,使用了返回true,否則false

註解解析的原則:

註解作用在哪個成員上就獲得該成員對應的物件去獲得註解資訊。

  • 比如註解作用在成員方法上,那麼要獲得該成員方法對應的Method物件。
  • 比如註解作用在成員變數上,那麼要獲得該成員變數對應的Field物件。
  • 比如註解作用在構造方法上,那麼要獲得該構造方法對應的Constructor物件。
  • 比如註解作用在類上,那麼要獲得該類對應的Class物件。

示例程式碼:

public class AnnotationDemo01 {

    // 解析獲得類上使用所有註解資訊
    @Test
    public void test03()throws Exception{
        // 獲得Class物件
        Class c =  BookStore.class;
        // 獲得所有註解資訊,包括繼承父類的
        // Annotation[] annotations = c.getAnnotations();

        // 獲得所有註解資訊,只能獲得本類的
        Annotation[] annotations = c.getDeclaredAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
    }

    // 解析獲得類上使用的註解資訊
    @Test
    public void test02()throws Exception{
        // 獲得Class物件
        Class c =  BookStore.class;
        if (c.isAnnotationPresent(Book.class)){
            // 根據註解型別獲得註解物件
            Book bookAnn = (Book) c.getAnnotation(Book.class);
            System.out.println(bookAnn.value());
            System.out.println(bookAnn.price());
            System.out.println(Arrays.toString(bookAnn.authors()));
        }
    }

    // 解析獲得成員方法上使用的註解資訊
    @Test
    public void test01()throws Exception{
        // 獲得Class物件
        Class c =  BookStore.class;
        // 根據方法名獲得Method物件
        Method buyMethod = c.getMethod("buy");

        if (buyMethod.isAnnotationPresent(Book.class)){
            // 根據註解型別獲得註解物件
            Book bookAnn =  buyMethod.getAnnotation(Book.class);

            System.out.println(bookAnn.value());
            System.out.println(bookAnn.price());
            System.out.println(Arrays.toString(bookAnn.authors()));
        }
    }
}