1. 程式人生 > >14Junit、反射、註解

14Junit、反射、註解

日期 初始化 javadoc equals lena java api 實例對象 使用場景 編譯失敗


14Junit、反射、註解

14.1.1 Junit的概述
一般IDE都內置了junit,若需要自行下載jar包,可以訪問官網,官網地址如下:http://www.junit.org

1. 特點
- 方法命名規則:以test開頭,使用駝峰命名法。
- 方法聲明上:必須使用註解:@Test,必須使用public修飾符,沒有返回值,方法沒有參數。

2. 運行測試方法
- 選中方法名:右鍵 --> Run 測試方法名,則運行選中的測試方法
比如測試方法名為testSum,則右鍵 --> Run testSum
- 選中類名:右鍵 --> Run 類名,則運行該類的所有測試方法
比如類名為TestCalculte,則右鍵 --> Run TestCalculte
- 選中模塊名或項目名:右鍵 --> Run ‘All Tests‘,則運行整個模塊中所有類的所有測試方法。
3. 查看測試結果
- 綠色:表示測試通過,
- 紅色:表示失敗或出現錯誤,

14.1.2 常用註解

- @Before:在每個測試方法之前都會運行一次
- @After:在每個測試方法運行以後運行的方法
- @BeforeClass:在所有的測試方法運行之前,只運行一次,而且必須用在靜態方法上面。
- @AfterClass:所有的測試方法運行以後,只運行一次,必須用在靜態方法上面。

- 業務類
?
/**
業務類
*/
public class Calculate {
/*
求a和b之和
*/
public int sum(int a,int b){
return a + b;
}

/**
求a和b之差
*/
public int sub(int a,int b){
return a - b;
}
}
?
- 測試類
?
import org.junit.*;

public class Test2 {

@BeforeClass
public static void testBeforeClass() {
System.out.println("類加載時, 只運行一次.");
}

@Before
public void testBefore() {
System.out.println("測試方法運行前被執行 ...");
}

@Test
public void testSum() {
Calculator calculator = new Calculator();
int sum = calculator.sum(10, 20);
System.out.println("sum = " + sum);
}

@Test
public void testSub() {
Calculator calculator = new Calculator();
int sub = calculator.sub(100, 20);
System.out.println("sub = " + sub);
}

@After
public void testAfter() {
System.out.println("每個測試方法被執行後執行 ...");
}

@AfterClass
public static void testAfterClass() {
System.out.println("類結束前, 只執行一次.");
}
}

輸出結果 :
類加載時, 只運行一次.
測試方法運行前被執行 ...
sub = 80
每個測試方法被執行後執行 ...
測試方法運行前被執行 ...
sum = 30
每個測試方法被執行後執行 ...
類結束前, 只執行一次.
?


14.2 反射

14.2.1 反射的基本概念

2.1 什麽是反射


反射是一種機制,利用該機制可以在程序運行過程中對類進行解剖並操作類中的方法,屬性,構造方法等成員。

2.2 使用反射機制解剖類的前提


必須先要獲取到該類的字節碼文件對象,即Class類型對象。關於Class描述字節碼文件如下圖所示:

tips:

1)Java中使用Class類表示某個class文件.

2)任何一個class文件都是Class這個類的一個實例對象.

2.3 獲取Class對象的三種方式


- 創建測試類:Student
?
public class Student {
// 屬性

// 行為
}
?
?
?
2.2.1 方式1:通過類名.class獲取
?
@Test
public void test1() {
// 方式一 : 通過類名獲取 class 對象
// 格式 : 類名.class 屬性
// 常用場景 : 反射獲取方法時, 確定方法的形參列表類型
Class<Student> cls = Student.class;
System.out.println("cls = " + cls);
}

輸出結果 :
cls = class cn.itcast.test2.Student
?
?
?
2.2.2 方式2:通過Object類的成員方法getClass()方法獲取
?
@Test
public void test2() {

// 1. 創建一個 Student 類型的對象
Student stu = new Student();

// 2. 調用方法, 並傳入 stu 對象
showInfo(stu);
}

public void showInfo(Object obj) {

// 方式二 : 使用對象名調用 getClass() 方法.
// 格式 : 對象名.getClass() 方法.
// 使用場景 : 在方法內部, 確定傳入形參的真實類型.

Class<?> cls = obj.getClass();
System.out.println("cls = " + cls);
}

輸出結果 :
cls = class cn.itcast.test2.Student
?
?
?
2.2.3 方式3:通過Class.forName("全限定類名")方法獲取
?
@Test
public void test3() throws ClassNotFoundException {

// 方式三 : 使用 Class 調用靜態方法 forName(全限定類名); `包名+類名`
// 使用場景 : 加載外部的配置文件時使用

Class<?> cls = Class.forName("cn.itcast.test2.Student");
System.out.println("cls = " + cls);
}

輸出結果 :
cls = class cn.itcast.test2.Student
?


2.3 獲取Class對象的信息


知道怎麽獲取Class對象之後,接下來就介紹幾個Class類中常用的方法了。

2.3.1 Class對象相關方法


String getSimpleName(); 獲得簡單類名,只是類名,沒有包
String getName(); 獲取完整類名,包含包名+類名
T newInstance() ;創建此 Class 對象所表示的類的一個新實例。要求:類必須有public的無參數構造方法
public class TestDate {

@Test
public void testDate1() throws Exception {

// 1. 獲取 Date 表示的 Class 對象.
Class<?> cls = Class.forName("java.util.Date");

// 2. 獲取簡單類名
String simpleName = cls.getSimpleName();
System.out.println("simpleName = " + simpleName);

// 3. 獲取完成名稱 (包名 + 類名)
String name = cls.getName();
System.out.println("name = " + name);

// 3. 創建一個日期對象
// cls.newInstance(); 已過時.
Object obj = cls.getDeclaredConstructor().newInstance();
System.out.println("obj = " + obj);
}
}

輸出結果 :
simpleName = Date
name = java.util.Date
obj = Sun Jul 15 15:39:01 CST 2018
?
?

2.4 獲取Class對象的Constructor信息


一開始在闡述反射概念的時候,我們說到利用反射可以在程序運行過程中對類進行解剖並操作裏面的成員。而一般常操作的成員有構造方法,成員方法,成員變量等等,那麽接下來就來看看怎麽利用反射來操作這些成員以及操作這些成員能幹什麽,先來看看怎麽操作構造方法。而要通過反射操作類的構造方法,我們需要先知道一個Constructor類。

2.4.1 Constructor類概述


Constructor是構造方法類,類中的每一個構造方法都是Constructor的對象,通過Constructor對象可以實例化對象。

2.4.2 Class類中與Constructor相關方法


1. Constructor getConstructor(Class... parameterTypes)
根據參數類型獲取構造方法對象,只能獲得public修飾的構造方法。
如果不存在對應的構造方法,則會拋出 java.lang.NoSuchMethodException 異常。

2. Constructor getDeclaredConstructor(Class... parameterTypes)
根據參數類型獲取構造方法對象,包括private修飾的構造方法。
如果不存在對應的構造方法,則會拋出 java.lang.NoSuchMethodException 異常。

3. Constructor[] getConstructors()
獲取所有的public修飾的構造方法

4. Constructor[] getDeclaredConstructors()
獲取所有構造方法,包括privat修飾的

2.4.3 Constructor類中常用方法


1. T newInstance(Object... initargs)
根據指定參數創建對象。
2. void setAccessible(true)
暴力反射,設置為可以直接訪問私有類型的構造方法。

2.5 獲取Class對象的Method信息


操作完構造方法之後,就來看看反射怎麽操作成員方法了。同樣的在操作成員方法之前我們需要學習一個類:Method類。

2.5.1 Method類概述


Method是方法類,類中的每一個方法都是Method的對象,通過Method對象可以調用方法。

2.5.2 Class類中與Method相關方法


1. Method getMethod("方法名", 方法的參數類型... 類型)
根據方法名和參數類型獲得一個方法對象,只能是獲取public修飾的

2. Method getDeclaredMethod("方法名", 方法的參數類型... 類型)
根據方法名和參數類型獲得一個方法對象,包括private修飾的

3. Method[] getMethods() (了解)
獲取所有的public修飾的成員方法,包括父類中。

4. Method[] getDeclaredMethods() (了解)
獲取當前類中所有的方法,包含私有的,不包括父類中。

2.5.3 Method類中常用方法


1. Object invoke(Object obj, Object... args)
根據參數args調用對象obj的該成員方法
如果obj=null,則表示該方法是靜態方法

2. void setAccessible(boolean flag)
暴力反射,設置為可以直接調用私有修飾的成員方法

2.5.4 示例代碼
?
測試一 :
?
@Test
public void testMethod1() throws Exception {

// 1. 獲取 Student 類表示的 Class 對象
Class<?> cls = Class.forName("cn.itcast.test2.Student");

// 2. 調用 getMethod 方法
Method eat = cls.getMethod("eat", String.class);

// 3. 調用 invoke 方法
Object obj = cls.getDeclaredConstructor().newInstance();
eat.invoke(obj, "牛肉");
}

輸出結果 :
正在吃牛肉
?
?
?
測試二 :
?
@Test
public void testDeclaredMethod2() throws Exception {

// 1. 獲取 Student 類表示的 Class 對象
Class<?> cls = Class.forName("cn.itcast.test2.Student");

// 2. 調用 declaredMethod 方法
Method sleep = cls.getDeclaredMethod("fallInLove");

// 3. 暴力反射 (設置可訪問權限)
sleep.setAccessible(true);

// 4. 調用 invoke 執行
Object obj = cls.getDeclaredConstructor().newInstance();
sleep.invoke(obj);
}

輸出結果 :
正在談戀愛 ...
?
?
?
測試三 :
?
@Test
public void testStaticMethod3() throws Exception {

// 1. 獲取 Student 類表示的 Class 對象
Class<?> cls = Class.forName("cn.itcast.test2.Student");

// 2. 調用 getMethod 方法
Method study = cls.getMethod("study");

// 3. 調用 invoke 方法
study.invoke(null);
}

輸出結果 :
正在學習中 ...
?
?
?
測試四 :
?
@Test
public void tesMethods4() throws Exception {

// 1. 獲取 Student 類表示的 Class 對象
Class<?> cls = Class.forName("cn.itcast.test2.Student");

// 2. 調用 getMethods 方法
Method[] methods = cls.getMethods();

// 3. 遍歷 methods 數組
for (Method method : methods) {
System.out.println(method);
}
}
?
測試五 :
?
@Test
public void tesDelcaredMethods5() throws Exception {

// 1. 獲取 Student 類表示的 Class 對象
Class<?> cls = Class.forName("cn.itcast.test2.Student");

// 2. 調用 getDeclaredMethods 方法
Method[] methods = cls.getDeclaredMethods();

// 3. 遍歷 methods 數組
for (Method method : methods) {
System.out.println(method);
}
}

2.6 獲取Class對象的Field信息(了解)


2.6.1 Field類概述


Field是屬性類,類中的每一個屬性(成員變量)都是Field的對象,通過Field對象可以給對應的成員變量賦值和取值。

2.6.2 Class類中與Field相關方法


1. Field getDeclaredField(String name)
根據屬性名獲得屬性對象,包括private修飾的

2. Field getField(String name)
根據屬性名獲得屬性對象,只能獲取public修飾的

3. Field[] getFields()
獲取所有的public修飾的屬性對象,返回數組。

4. Field[] getDeclaredFields()
獲取所有的屬性對象,包括private修飾的,返回數組。

2.6.3 Field類中常用方法


void set(Object obj, Object value)
Object get(Object obj)

void setAccessible(true);暴力反射,設置為可以直接訪問私有類型的屬性。
Class getType(); 獲取屬性的類型,返回Class對象。


2.6.4 示例代碼
?
測試一 :
?
@Test
public void testField1() throws Exception {

// 1. 獲取 Student 類表示的 Class 對象
Class<?> cls = Class.forName("cn.itcast.test2.Student");

// 2. 調用 getField 方法
Field description = cls.getField("description");

// 3. 設置屬性
Object obj = cls.getDeclaredConstructor().newInstance();
description.set(obj, "這就是那個神奇的學生.");

// 4. 獲取屬性
Object desc = description.get(obj);
System.out.println("desc = " + desc);
}

輸出結果 :
desc = 這就是那個神奇的學生.
?
?
?
測試二 :
?
@Test
public void testDeclaredField2() throws Exception {

// 1. 獲取 Student 類表示的 Class 對象
Class<?> cls = Class.forName("cn.itcast.test2.Student");

// 2. 調用 getDeclaredField 方法
Field name = cls.getDeclaredField("name");

// 3. 暴力反射
name.setAccessible(true);

// 4. 設置屬性
Object obj = cls.getDeclaredConstructor().newInstance();
name.set(obj, "111");

// 5. 查看
System.out.println(obj);
}

輸出結果 :
Student{name=‘111‘, age=0, gender= }
?
?
?
測試三 :
?
@Test
public void testFields3() throws Exception {

// 1. 獲取 Student 類表示的 Class 對象
Class<?> cls = Class.forName("cn.itcast.test2.Student");

// 2. 調用 getFields 方法
Field[] fields = cls.getFields();

// 3. 遍歷 fields 數組
for (Field field : fields) {
System.out.println(field);
}
}

輸出結果 :
public java.lang.String cn.itcast.test2.Student.description
?
?
?
測試四 :
?
@Test
public void testDeclaredFields4() throws Exception {

// 1. 獲取 Student 類表示的 Class 對象
Class<?> cls = Class.forName("cn.itcast.test2.Student");

// 2. 調用 getDeclaredFields 方法
Field[] fields = cls.getDeclaredFields();

// 3. 遍歷 fields 數組
for (Field field : fields) {
System.out.println(field);
}
}

2.7 反射案例


編寫一個工廠方法可以根據配置文件產任意類型的對象。
?
- 例如有配置文件stu.properties,存儲在當前項目下,內容如下:
?
className=cn.itcast.reflect.Student
name=rose
age=18
gender=女
?
- 根據配置文件信息創建一個學生對象。
?
?
?
Student 類 :
?
public class Student {
// 屬性
private String name;
private int age;
private char gender;

// 公開構造方法 :
public Student(String name, int age, char gender) {
this.name = name;
this.age = age;
this.gender = gender;
}

// 公開無參構造方法
public Student() {
}

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

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;
}

public char getGender() {
return gender;
}

public void setGender(char gender) {
this.gender = gender;
}
}
?
CreateObject 類 :
?
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Properties;
import java.util.Set;

public class CreateObject {

// 屬性
private static Properties prop;

// 靜態方法 : 加載配置文件
static {
// 初始化 :
prop = new Properties();

try {
prop.load(new FileReader("stu.properties"));
} catch (IOException e) {

// e.printStackTrace();
throw new RuntimeException("配置文件加載失敗!");
}
}

// 方法 : 根據配置文件, 創建對象
public static Object createObject() throws Exception {

// 1. 獲取 class 名稱
String className = prop.getProperty("className");

// 2. 獲取 class 對象
Class<?> cls = Class.forName(className);

// 3. 創建一個 cls 表示的對象
Object obj = cls.getDeclaredConstructor().newInstance();

// 4. 獲取屬性集對象的所有 `鍵集`
Set<String> keys = prop.stringPropertyNames();

// 5. 遍歷
for (String key : keys) {

// 判斷 :
if ("class".equals(key)) continue;

// 6. 根據 key 獲取對應的 value
String value = prop.getProperty(key);

// 7. 獲取所有的 fields 數組
Field field = cls.getDeclaredField(key);

// 8. 設置訪問權限
field.setAccessible(true);

// 9. 獲取屬性的類型
Class<?> type = field.getType();

// 10. 判斷類型
if (type == int.class) {
int v = Integer.parseInt(value);
// 設置屬性
field.set(obj, v);
} else if (type == char.class) {
char c = value.charAt(0);
// 設置屬性
field.set(obj, c);
} else {
field.set(obj, value);
}
}

// 11. 返回對象
return obj;
}
}
?
測試類 :
?
public class Test {
public static void main(String[] args) throws Exception {

Object obj = CreateObject.createObject();
System.out.println("obj = " + obj);
}
}

輸出結果 :
obj = Student{name=‘rose‘, age=18, gender=女}
?

14.3 註解

3.1 註解的概述

3.1.1 註解的概念


- 註解是JDK1.5的特性。

- 註解相當一種標記,是類的組成部分,可以給類攜帶一些額外的信息。
- 標記(註解)可以加在包,類,字段,方法,方法參數以及局部變量上。
- 註解是給編譯器或JVM看的,編譯器或JVM可以根據註解來完成對應的功能。
註解(Annotation)相當於一種標記,在程序中加入註解就等於為程序打上某種標記,以後,javac編譯器、開發工具和其他程序可以通過反射來了解你的類及各種元素上有無何種標記,看你的程序有什麽標記,就去幹相應的事,標記可以加在包、類,屬性、方法,方法的參數以及局部變量上。

3.1.2 註解的作用


註解的作用就是給程序帶入參數。

以下幾個常用操作中都使用到了註解:

1. 生成幫助文檔:@author和@version
- @author:用來標識作者姓名。
- @version:用於標識對象的版本號,適用範圍:文件、類、方法。
- 使用@author和@version註解就是告訴Javadoc工具在生成幫助文檔時把作者姓名和版本號也標記在文檔中。如下圖:

2. 編譯檢查:@Override
- @Override:用來修飾方法聲明。
- 用來告訴編譯器該方法是重寫父類中的方法,如果父類不存在該方法,則編譯失敗。如下圖

3. 框架的配置(框架=代碼+配置)
- 具體使用請關註框架課程的內容的學習。

3.1.3 常見註解

1. @author:用來標識作者名。
2. @version:用於標識對象的版本號。
3. @Override :用來修飾方法聲明,告訴編譯器該方法是重寫父類中的方法,如果父類不存在該方法,則編譯失敗。
4. @Deprecated: 用來表示不贊成使用.

3.2 自定義註解

3.2.1 定義格式


public @interface 註解名 {

}

如:定義一個名為 Student 的註解
public @interface Student {

}

- 以上定義出來的註解就是一個最簡單的註解了,但這樣的註解意義不大,因為註解中沒有任何內容,就好像我們定義一個類而這個類中沒有任何成員變量和方法一樣,這樣的類意義也是不大的,所以在定義註解時會在裏面添加一些成員來讓註解功能更加強大,這些成員就是屬性。接下來就看看怎麽給註解添加屬性。

3.2.2 註解的屬性


1. 屬性的作用
- 可以讓用戶在使用註解時傳遞參數,讓註解的功能更加強大。
2. 屬性的格式
- 格式1:數據類型 屬性名();
- 格式2:數據類型 屬性名() default 默認值;
3. 屬性定義示例
// 該註解擁有三個屬性 (name, age, gender)
public @interface Student {

String name();

int age() default 18;

char gender() default ‘男‘;
}
4. 屬性適用的數據類型
- 八種基本數據類型(byte, short, int, long, float, double, char, boolean)
- String類型,Class類型,枚舉類型,註解類型
- 以上所有類型的一維數組

3.3 使用自定義註解

3.3.1 定義註解


1. 定義一個註解:Book
- 包含屬性:String value() 書名
- 包含屬性:double price() 價格,默認值為 100
- 包含屬性:String[] authors() 多位作者
說明 : 當註解中只有一個屬性且名稱是value,在使用註解時給value屬性賦值可以直接給屬性值,無論value是單值元素還是數組類型。

2. 代碼實現
public @interface Book {
// 書名
String value();
// 價格
int price() default 100;
// 多位作者
String[] authors();
}
3.3.2 使用註解


1. 定義類在成員方法上使用Book註解

public class BookShelf {

@Book(value = "西遊記", price=998, authors = {"吳承恩", "白求恩"})
public void show() {

}
}
?
使用註意事項

- 如果屬性有默認值,則使用註解的時候,這個屬性可以不用賦值。

- 如果屬性沒有默認值,那麽在使用註解時一定要給屬性賦值。

3.4 註解之元註解

3.4.1 元註解的概述


- Java API 提供的註解
- 專門用來定義註解的註解。
- 任何 Java 官方提供的非元註解的定義中都使用到了元註解。
3.4.2 常用元註解


- @Target 註釋的使用位置.
- @Retention 註解的聲明周期.
3.4.2.1 元註解之@Target


- 作用:指明此註解用在哪個位置,如果不寫默認是任何地方都可以使用。
- 可選的參數值在枚舉類ElemenetType中包括:
TYPE: 用在類,接口上
FIELD:用在成員變量上
METHOD: 用在方法上
PARAMETER:用在參數上
CONSTRUCTOR:用在構造方法上
LOCAL_VARIABLE:用在局部變量上
3.4.2.2 元註解之@Retention


- 作用:定義該註解的生命周期(有效範圍)。
- 可選的參數值在枚舉類型RetentionPolicy中包括
SOURCE:註解只存在於Java源代碼中,編譯生成的字節碼文件中就不存在了。
CLASS:註解存在於Java源代碼、編譯以後的字節碼文件中,運行的時候內存中沒有,默認值。
RUNTIME:註解存在於Java源代碼中、編譯以後的字節碼文件中、運行時內存中,程序可以通過反射獲取該註解。

3.4.3 元註解使用示例


// (書名, 價格, 作者)

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

// 元註解 : Target 目標 (註解使用的位置)
@Target({ElementType.METHOD, ElementType.TYPE})

// 元註解 : Retention 保留策略 (SOURCE, CLASS, RUNTIME)
@Retention(RetentionPolicy.RUNTIME)
public @interface Book {

String value(); // 說明 : 如果註解只有一個屬性, 最好取名為 value, 因為書寫時, 可以省略.

int price() default 100;

String[] authors(); // 多位作者
}
?
?
?
BookShelf 類
?
@Book(value = "西遊記", price=998, authors = {"吳承恩", "xxx"})
public class BookShelf {

// 屬性
// @Book(value = "西遊記", price=998, authors = {"吳承恩", "xxx"})
private int id;

@Book(value = "西遊記", price=998, authors = {"吳承恩", "xxx"})
public void show() {
String value = "";
int price = 0;
String[] authors = {};
System.out.println("書名為 : " + value);
System.out.println("價格為 : " + price);
System.out.println("作者為 : " + Arrays.toString(authors));
}
}
?
?
?


3.5 註解解析

3.5.1 什麽是註解解析


- 通過Java技術獲取註解數據的過程則稱為註解解析。

3.5.2 與註解解析相關的接口


- Anontation:所有註解類型的公共接口,類似所有類的父類是Object。
- AnnotatedElement:定義了與註解解析相關的方法,常用方法:
boolean isAnnotationPresent(Class annotationClass); 判斷當前對象是否有指定的註解,有則返回true,否則返回false。
T getAnnotation(Class<T> annotationClass); 獲得當前對象上指定的註解對象。

3.5.3 獲取註解數據的原理


- 註解作用在那個成員上,就通過反射獲得該成員的對象(Filed)來得到它的註解。

- 如註解作用在方法上,就通過方法(Method)對象得到它的註解*

- 如註解作用在類上,就通過Class對象得到它的註解

3.5.4 使用反射獲取註解的數據


3.5.4.1 需求說明


1. 定義註解Book,要求如下:
- 包含屬性:String value() 書名
- 包含屬性:double price() 價格,默認值為 100
- 包含屬性:String[] authors() 多位作者
- 限制註解使用的位置:類和成員方法上
- 指定註解的有效範圍:RUNTIME
2. 定義BookStore類,在類和成員方法上使用Book註解
3. 定義TestAnnotation測試類獲取Book註解上的數據

3.5.4.2 代碼實現


1.註解Book
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Book {
// 書名
String value();
// 價格
int price() default 100;
// 作者 (多位作者)
String[] authors();
}
?
2.BookShelf 類
?
import java.lang.reflect.Method;
import java.util.Arrays;

@Book(value = "西遊記", price=998, authors = {"吳承恩", "白求恩"})
public class BookShelf {

// 屬性
// @Book(value = "西遊記", price=998, authors = {"吳承恩", "白求恩"})
private int id;

@Book(value = "西遊記", price=998, authors = {"吳承恩", "白求恩"})
public void show() {
// 定義變量
String value = "";
int price = 0;
String[] authors = {};

// 獲取當前類的 Class 對象
Class<? extends BookShelf> cls = this.getClass();
try {
// 獲取當前方法對象
Method show = cls.getMethod("show");
// 判斷當前方法上是否有註解信息
if (show.isAnnotationPresent(Book.class)) {
// 條件成立, 獲取到當前註解對象
Book book = show.getAnnotation(Book.class);
// 取出信息, 並實現賦值
value = book.value();
price = book.price();
authors = book.authors();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}

// 輸出查看
System.out.println("書名為 : " + value);
System.out.println("價格為 : " + price);
System.out.println("作者為 : " + Arrays.toString(authors));
}
}
?
3.TestBookShelf 類
?
public class TestBookShelf {
public static void main(String[] args) {

BookShelf bookShelf = new BookShelf();
bookShelf.show();
}
}

輸出結果 :
書名為 : 西遊記
價格為 : 998
作者為 : [吳承恩, 白求恩]

書名為 :
價格為 : 0
作者為 : []
?
?
?
補充 : 解析類上的註解
?
import java.util.Arrays;

public class TestBookShelf2 {
public static void main(String[] args) throws ClassNotFoundException {

// 1. 獲取類
Class<?> cls = Class.forName("cn.itcast.annotation.BookShelf");
// 2. 判斷該類上是否有 Book 註解信息
if (cls.isAnnotationPresent(Book.class)) {
// 3. 獲取 Book 註解對象
Book book = cls.getAnnotation(Book.class);
// 4. 取出註解信息
String value = book.value();
int price = book.price();
String[] authors = book.authors();
// 5. 輸出查看
System.out.println("value = " + value);
System.out.println("price = " + price);
System.out.println("authors = " + Arrays.toString(authors));
}
}
}

輸出結果 :
value = 西遊記
price = 998
authors = [吳承恩, 白求恩]

3.6 註解案例

3.5.1 案例說明


模擬Junit測試的@Test
3.5.2 案例分析


1. 模擬Junit測試的註釋@Test,首先需要編寫自定義註解@MyTest,並添加元註解,保證自定義註解只能修飾方法,且在運行時可以獲得。
2. 然後編寫目標類(測試類),然後給目標方法(測試方法)使用 @MyTest註解,編寫三個方法,其中兩個加上@MyTest註解。
3. 最後編寫調用類,使用main方法調用目標類,模擬Junit的運行,只要有@MyTest註釋的方法都會運行。
3.5.3 案例代碼

1. 註解MyTest
?
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
}
?
1. 目標類MyTestClass
?
public class MyTestClass {

@MyTest
public void test01() {
System.out.println("測試方法一被執行 ...");
}

public void test02() {
System.out.println("測試方法二被執行 ...");
}

@MyTest
public void test03() {
System.out.println("測試方法三被執行 ...");
}
}
?
1. 調用類ParseAnnotation
?
import java.lang.reflect.Method;

// 解析註解類 :
public class ParseAnnotation {
public static void main(String[] args) throws Exception {

// 1. 獲取 MyTestClass 的 Class 對象
Class<?> cls = Class.forName("cn.itcast.practice.MyTestClass");
Object obj = cls.getDeclaredConstructor().newInstance();

// 2. 調用 getMethods 獲取所有方法
Method[] methods = cls.getMethods();

// 3. 遍歷 methods 數組
for (Method method : methods) {

// 4. 判斷當前方法上是否存在 MyTest 註解
if (method.isAnnotationPresent(MyTest.class)) {

// 5. 執行當前方法
method.invoke(obj);
}
}
}
}

輸出結果 :
測試方法一被執行 ...
測試方法三被執行 ...

14Junit、反射、註解