Java高階技術
單元測試
概述
單元測試就是針對最小的功能單元(方法),編寫測試程式碼對該功能進行正確性測試。
目前的測試方法是怎樣的,存在什麼問題
- 只能編寫main方法,並在main方法中再去呼叫其他方法進行測試。
- 使用起來很不靈活,無法實現自動化測試。
- 無法得到測試的報告,需要程式設計師自己去觀察測試是否成功。
Junit單元測試框架
JUnit是使用Java語言實現的單元測試框架,它是第三方公司開源出來的,很多開發工具已經整合了Junit框架,比如IDEA。
優點
- 編寫的測試程式碼很靈活,可以指定某個測試方法執行測試,也支援一鍵完成自動化測試。
- 不需要程式設計師去分析測試的結果,會自動生成測試報告出來。
- 提供了更強大的測試能力。
- 單元測試中的某個方法測試失敗了,不會影響其他測試方法的測試。
具體使用步驟
- 將Junit框架的jar包匯入到專案中(注意:IDEA整合了Junit框架,不需要我們自己手工匯入了)。
- 編寫測試類、測試類方法(注意:測試方法必須是公共的,無引數,無返回值的非靜態方法)。
- 必須在測試方法上使用@Test註解(標註該方法是一個測試方法)。
- 在測試方法中,編寫程式呼叫被測試的方法即可。
- 選中測試方法,右鍵選擇“JUnit執行” ,如果測試透過則是綠色;如果測試失敗,則是紅色。
- 也可以選擇類或者模組啟動來測試全部方法。
斷言機制
- 可以透過預測測試方法的結果來判斷方法是否有bug。
- 使用Assert類的assertEquals()方法。
Junit單元測試框架的常用註解(Junit 4.xxxx版本)
註解 | 說明 |
---|---|
@Test | 測試方法 |
@Before | 用來修飾一個例項方法,該方法會在每一個測試方法執行之前執行一次 |
@After | 用來修飾一個例項方法,該方法會在每一個測試方法執行之後執行一次 |
@BeforeClass | 用來修飾一個靜態方法,該方法會在所有測試方法之前只執行一次 |
@AfterClass | 用來修飾一個靜態方法,該方法會在所有測試方法之後只執行一次 |
- 在測試方法執行前執行的方法,常用於:初始化資源。
- 在測試方法執行後執行的方法,常用於:釋放資源。
Junit單元測試框架的常用註解(Junit 5.xxxx版本)
註解 | 說明 |
---|---|
@Test | 測試方法 |
@BeforeEach | 用來修飾一個例項方法,該方法會在每一個測試方法執行之前執行一次 |
@AfterEach | 用來修飾一個例項方法,該方法會在每一個測試方法執行之後執行一次 |
@BeforeAll | 用來修飾一個靜態方法,該方法會在所有測試方法之前只執行一次 |
@AfterAll | 用來修飾一個靜態方法,該方法會在所有測試方法之後只執行一次 |
- 在測試方法執行前執行的方法,常用於:初始化資源。
- 在測試方法執行後執行的方法,常用於:釋放資源。
反射(Reflection)
- 反射指的是允許以程式設計的方式訪問已載入類的成分(成員變數、方法、構造器等)。
- 反射的基本作用:在執行時獲取類的位元組碼檔案物件,然後可以解析類中的全部成分。
- 反射的核心思想和關鍵就是:得到編譯以後的class檔案物件。
反射的內容
- 反射第一步:獲取類:Class
- 獲取類的構造器:Constructor
- 獲取類的成員變數:Field
- 獲取類的成員方法:Method
獲取類
反射的第一步都是先得到載入後的類,然後才可以去拿類的其他成分。
獲取Class類的物件的三種方式
- 方式一:
Class c1 = Class.forName("全類名");
- 方式二:
Class c2 = 類名.class;
- 方式三:
Class c3 = 物件.getClass();
獲取構造器
步驟
- 獲得class物件。
- 獲得Constructor物件。
- 建立物件。
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,表示取消訪問檢查,進行暴力反射 |
- 反射可以破壞封裝性,私有的構造器也可以執行了。
獲取成員變數
步驟
- 獲得class物件。
- 獲得Field物件。
- 賦值或者取值。
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,表示取消訪問檢查,進行暴力反射 |
獲取成員方法
步驟
- 獲得class物件。
- 獲得Method物件。
- 執行方法。
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 |
應用場景
配合反射等技術做框架。
動態代理
概述
代理思想
被代理者沒有能力,或者不願意去完成某件事情,需要找個人(代理)代替自己去完成這件事。
代理
代理就是一個物件,用來對被代理物件的行為進行管理的物件。
動態代理的作用
動態代理主要是對被代理物件的行為進行代理。
動態代理的開發步驟
- 必須定義介面,裡面定義一些行為,用來約束被代理物件和代理物件都要完成的事情。
- 定義一個實現類實現介面,這個實現類的物件代表被代理的物件。
- 定義一個測試類,在裡面建立被代理物件,然後為其建立一個代理物件返回。(重點)
- 代理物件中,需要模擬收首付款,真正觸發被代理物件的行為,然後接收尾款操作。
- 透過返回的代理物件進行方法的呼叫,觀察動態代理的執行流程。
建立代理物件
Java中代理的代表類是:java.lang.reflect.Proxy,它提供了一個靜態方法,用於為被代理物件產生一個代理物件返回。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
作用:為被代理物件返回一個代理物件。
引數一:用於指定類載入器,來載入代理類,產生代理物件,使用當前類名.class.getClassLoader()即可。
引數二:真實業務物件的介面。(被代理的方法交給代理物件)
引數三:代理的核心處理程式。
透過代理物件呼叫方法的執行流程
- 先走向代理。
- 代理中的invoke()方法執行。
- 回到代理中,由代理負責返回結果給呼叫者。
動態代理的優點
- 可以在不改變方法原始碼的情況下,實現對方法功能的增強,提高了程式碼的複用。
- 簡化了程式設計工作,提高了開發效率,同時提高了軟體系統的可擴充套件性。
- 可以為被代理物件的所有方法做代理。
- 非常的靈活,支援任意介面型別的實現類物件做代理,也可以直接為介面本身做代理。