Java學習筆記——第二十二天

zgg1h發表於2024-03-21

Java高階技術

單元測試

概述

單元測試就是針對最小的功能單元(方法),編寫測試程式碼對該功能進行正確性測試。

目前的測試方法是怎樣的,存在什麼問題

  • 只能編寫main方法,並在main方法中再去呼叫其他方法進行測試。
  • 使用起來很不靈活,無法實現自動化測試。
  • 無法得到測試的報告,需要程式設計師自己去觀察測試是否成功。

Junit單元測試框架

JUnit是使用Java語言實現的單元測試框架,它是第三方公司開源出來的,很多開發工具已經整合了Junit框架,比如IDEA。

優點

  • 編寫的測試程式碼很靈活,可以指定某個測試方法執行測試,也支援一鍵完成自動化測試。
  • 不需要程式設計師去分析測試的結果,會自動生成測試報告出來。
  • 提供了更強大的測試能力。
  • 單元測試中的某個方法測試失敗了,不會影響其他測試方法的測試。

具體使用步驟

  1. 將Junit框架的jar包匯入到專案中(注意:IDEA整合了Junit框架,不需要我們自己手工匯入了)。
  2. 編寫測試類、測試類方法(注意:測試方法必須是公共的,無引數,無返回值的非靜態方法)。
  3. 必須在測試方法上使用@Test註解(標註該方法是一個測試方法)。
  4. 在測試方法中,編寫程式呼叫被測試的方法即可。
  5. 選中測試方法,右鍵選擇“JUnit執行” ,如果測試透過則是綠色;如果測試失敗,則是紅色。
  6. 也可以選擇類或者模組啟動來測試全部方法。

斷言機制

  • 可以透過預測測試方法的結果來判斷方法是否有bug。
  • 使用Assert類的assertEquals()方法。

Junit單元測試框架的常用註解(Junit 4.xxxx版本)

註解 說明
@Test 測試方法
@Before 用來修飾一個例項方法,該方法會在每一個測試方法執行之前執行一次
@After 用來修飾一個例項方法,該方法會在每一個測試方法執行之後執行一次
@BeforeClass 用來修飾一個靜態方法,該方法會在所有測試方法之前只執行一次
@AfterClass 用來修飾一個靜態方法,該方法會在所有測試方法之後只執行一次
  • 在測試方法執行前執行的方法,常用於:初始化資源。
  • 在測試方法執行後執行的方法,常用於:釋放資源。

Junit單元測試框架的常用註解(Junit 5.xxxx版本)

註解 說明
@Test 測試方法
@BeforeEach 用來修飾一個例項方法,該方法會在每一個測試方法執行之前執行一次
@AfterEach 用來修飾一個例項方法,該方法會在每一個測試方法執行之後執行一次
@BeforeAll 用來修飾一個靜態方法,該方法會在所有測試方法之前只執行一次
@AfterAll 用來修飾一個靜態方法,該方法會在所有測試方法之後只執行一次
  • 在測試方法執行前執行的方法,常用於:初始化資源。
  • 在測試方法執行後執行的方法,常用於:釋放資源。

反射(Reflection)

  • 反射指的是允許以程式設計的方式訪問已載入類的成分(成員變數、方法、構造器等)。
  • 反射的基本作用:在執行時獲取類的位元組碼檔案物件,然後可以解析類中的全部成分。
  • 反射的核心思想和關鍵就是:得到編譯以後的class檔案物件。

反射的內容

  1. 反射第一步:獲取類:Class
  2. 獲取類的構造器:Constructor
  3. 獲取類的成員變數:Field
  4. 獲取類的成員方法:Method

獲取類

反射的第一步都是先得到載入後的類,然後才可以去拿類的其他成分。

獲取Class類的物件的三種方式

  • 方式一:Class c1 = Class.forName("全類名");
  • 方式二:Class c2 = 類名.class;
  • 方式三:Class c3 = 物件.getClass();

獲取構造器

步驟

  1. 獲得class物件。
  2. 獲得Constructor物件。
  3. 建立物件。

Class類中用於獲取構造器Constructor類的方法

方法 說明
Constructor<?>[] getConstructors() 返回所有構造器物件的陣列(只能拿public的)
Constructor<?>[] getDeclaredConstructors() 返回所有構造器物件的陣列(只要存在就能拿到)
Constructor<T> getConstructor(Class<?>... parameterTypes) 返回單個構造器物件(只能拿public的)
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回單個構造器物件(只要存在就能拿到)
  • 獲取構造器的作用依然是初始化一個物件返回。

Constructor類中用於建立物件的方法

方法 說明
T newInstance(Object... initargs) 根據指定的構造器建立物件
public void setAccessible(boolean flag) 設定為true,表示取消訪問檢查,進行暴力反射
  • 反射可以破壞封裝性,私有的構造器也可以執行了。

獲取成員變數

步驟

  1. 獲得class物件。
  2. 獲得Field物件。
  3. 賦值或者取值。

Class類中用於獲取成員變數的方法

方法 說明
Field[] getFields() 返回所有成員變數物件的陣列(只能拿public的)
Field[] getDeclaredFields() 返回所有成員變數物件的陣列(只要存在就能拿到)
Field getField(String name) 返回單個成員變數物件(只能拿public的)
Field getDeclaredField(String name) 返回單個成員變數物件(只要存在就能拿到)
  • 獲取成員變數的作用依然是在某個物件中取值、賦值。

Field類中用於取值、賦值的方法

方法 說明
void set(Object obj, Object value) 賦值
Object get(Object obj) 取值
public void setAccessible(boolean flag) 設定為true,表示取消訪問檢查,進行暴力反射

獲取成員方法

步驟

  1. 獲得class物件。
  2. 獲得Method物件。
  3. 執行方法。

Class類中用於獲取成員方法的方法

方法 說明
Method[] getMethods() 返回所有成員方法物件的陣列(只能拿public的)
Method[] getDeclaredMethods() 返回所有成員方法物件的陣列(只要存在就能拿到)
Method getMethod(String name, Class<?>... parameterTypes) 返回單個成員方法物件(只能拿public的)
Method getDeclaredMethod(String name, Class<?>... parameterTypes) 返回單個成員方法物件(只要存在就能拿到)
  • 獲取成員方法的作用依然是在某個物件中進行執行此方法。

Method類中用於觸發執行的方法

方法 說明
Object invoke(Object obj, Object... args) 執行方法:
引數一:呼叫該方法的物件
引數二:給該方法傳遞的引數(如果沒有就不寫)
返回值:方法的返回值(如果沒有就不寫)
public void setAccessible(boolean flag) 設定為true,表示取消訪問檢查,進行暴力反射

反射的作用

  • 可以在執行時得到一個類的全部成分然後操作。
  • 可以破壞封裝性。
  • 也可以破壞泛型的約束性(編譯時會進行泛型擦除)。
  • 更重要的用途是適合:做Java高階框架。
  • 基本上主流框架都會基於反射設計一些通用技術功能。

註解

概述

  • Java 註解(Annotation)又稱 Java 標註,是 JDK5.0 引入的一種註釋機制。
  • Java 語言中的類、構造器、方法、成員變數、引數等都可以被註解進行標註。

自定義註解

自定義註解就是自己做一個註解來使用。

格式

public @interface 註解名稱 {
    public 屬性型別 屬性名() default 預設值; 
}
  • 屬性型別:Java支援的資料型別基本上都支援。
  • public可以不寫,會預設加上。

使用格式

@註解名稱(屬性名=屬性值,...) //沒有預設值的屬性必須賦值

特殊屬性名:value

  • 如果只有一個value屬性的情況下,使用value屬性的時候可以省略value的屬性名不寫。
  • 如果除value外的屬性都有預設值且其他屬性都不用賦值,那麼也可以省略value的屬性名不寫。
  • 但是如果有多個屬性, 且存在某個或某些屬性沒有預設值,那麼value的屬性名是不能省略的。

註解的原理

  • 註解本質是一個介面,Java中所有註解都是繼承了Annotation介面的。
  • @註解(...)其實就是建立了一個實現類物件,實現了該註解以及Annotation介面。

元註解

元註解就是註解註解的註解。

元註解的型別

元註解有兩個: @Target和 @Retention。

@Target(ElementType.型別)

作用:宣告被修飾的註解只能在哪些位置使用。

  • TYPE,類,介面

  • FIELD,成員變數

  • METHOD, 成員方法

  • PARAMETER,方法引數

  • CONSTRUCTOR,構造器

  • LOCAL_VARIABLE,區域性變數

@Retention(RetentionPolicy.型別)

作用:宣告註解的保留週期。

  • SOURCE: 只作用在原始碼階段,位元組碼檔案中不存在。
  • CLASS: 保留到位元組碼檔案階段,執行階段不存在。(預設值)
  • RUNTIME:一直保留到執行階段。(開發常用)

註解的解析

註解的解析就是判斷類上、方法上和成員變數上是否存在註解,並把註解裡的內容給解析出來。

解析註解的指導思想

  • 要解析誰上面的註解,就應該先拿到誰。

  • 與註解解析相關的介面:

    • Annotation:註解的頂級介面,註解都是Annotation型別的物件。
    • AnnotatedElement:該介面定義了與註解解析相關的解析方法。
  • 所有的類成分Class,Method,Field和Constructor都實現了AnnotatedElement介面,他們都擁有解析註解的能力

方法 說明
Annotation[] getDeclaredAnnotations() 獲得當前物件上使用的所有註解,返回註解陣列。
T getDeclaredAnnotation(Class<T> annotationClass) 根據註解型別獲得對應註解物件
boolean isAnnotationPresent(Class<Annotation> annotationClass) 判斷當前物件是否使用了指定的註解,如果使用了則返回true,否則返回false

應用場景

配合反射等技術做框架。

動態代理

概述

代理思想

被代理者沒有能力,或者不願意去完成某件事情,需要找個人(代理)代替自己去完成這件事。

代理

代理就是一個物件,用來對被代理物件的行為進行管理的物件。

動態代理的作用

動態代理主要是對被代理物件的行為進行代理。

動態代理的開發步驟

  1. 必須定義介面,裡面定義一些行為,用來約束被代理物件和代理物件都要完成的事情。
  2. 定義一個實現類實現介面,這個實現類的物件代表被代理的物件。
  3. 定義一個測試類,在裡面建立被代理物件,然後為其建立一個代理物件返回。(重點)
  4. 代理物件中,需要模擬收首付款,真正觸發被代理物件的行為,然後接收尾款操作。
  5. 透過返回的代理物件進行方法的呼叫,觀察動態代理的執行流程。

建立代理物件

Java中代理的代表類是:java.lang.reflect.Proxy,它提供了一個靜態方法,用於為被代理物件產生一個代理物件返回。

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h) 
作用:為被代理物件返回一個代理物件。
引數一:用於指定類載入器,來載入代理類,產生代理物件,使用當前類名.class.getClassLoader()即可。
引數二:真實業務物件的介面。(被代理的方法交給代理物件)
引數三:代理的核心處理程式。

透過代理物件呼叫方法的執行流程

  1. 先走向代理。
  2. 代理中的invoke()方法執行。
  3. 回到代理中,由代理負責返回結果給呼叫者。

動態代理的優點

  1. 可以在不改變方法原始碼的情況下,實現對方法功能的增強,提高了程式碼的複用。
  2. 簡化了程式設計工作,提高了開發效率,同時提高了軟體系統的可擴充套件性。
  3. 可以為被代理物件的所有方法做代理。
  4. 非常的靈活,支援任意介面型別的實現類物件做代理,也可以直接為介面本身做代理。

相關文章