泛型
泛型概念
泛型是在JDK1.5之後引入的,旨在讓我們寫出更加通用化,更加靈活的程式碼。通用化的手段在於讓資料型別變得引數化,定義泛型時,對應的資料型別是不確定的,泛型方法被呼叫時,會指定具體型別,其核心目標是為了解決容器型別在編譯時安全檢查的問題。
泛型:一般用在類、方法、介面中,叫做泛型類、泛型介面、泛型方法
泛型的使用
package demo.generic;
import lombok.Data;
/**
* 泛型類的定義
* @param <T>
*/
@Data
public class GenericClassExample<T> {
//member這個成員變數的型別為T,T的型別由外部指定
private T member;
//泛型類的構造方法形參member的型別也為T,T的型別由外部指定
public GenericClassExample(T member) {
this.member = member;
}
//泛型類中也可以定義普通方法,普通方法的引數也為泛型
public T handleSomething(T target) {
return target;
}
}
- 泛型的引數不支援基本型別;泛型相關的資訊不會進入到執行時階段
package demo.generic; public class GenericDemo { public static void main(String[] args) { GenericClassExample<String> strExample = new GenericClassExample<>("abc"); GenericClassExample<Integer> intExample = new GenericClassExample<>(123); System.out.println(strExample.getClass()); // 列印泛型類的型別 System.out.println(intExample.getClass()); // 列印泛型類的型別 } } // **********執行結果********* //class demo.generic.GenericClassExample //class demo.generic.GenericClassExample // 我們可以從執行結果看出strExample和intExample的型別是一樣的,因此泛型類的型別約束只在編譯時有效
- 能否在泛型裡面使用具備繼承關係的類?
- 使用萬用字元 ?,但是會使得泛型的型別檢查失去意義
- 給泛型加入上邊界 <? extends E>
- 給泛型加入下邊界 <? super E>
package demo.generic; public class GenericDemo { //給泛型加如上邊界 ? extends E, 泛型型別必須是E的型別或其子類 public static void handleMember(GenericClassExample<? extends Number> integerExample) { Integer result = (Integer) integerExample.getMember() + 100; System.out.println("result is " + result); } //給泛型加入下邊界 ? super E ,泛型型別必須是E的型別或其父類 public static void handleSuperMember(GenericClassExample<? super Integer> integerExample) { Integer result = (Integer) integerExample.getMember() + 100; System.out.println("result is " + result); } public static void main(String[] args) { GenericClassExample<String> strExample = new GenericClassExample<>("abc"); GenericClassExample<Integer> integerExample = new GenericClassExample<>(123); GenericClassExample<Number> numberExample = new GenericClassExample<>(new Integer(123)); // handleMember(strExample); // 編譯會報錯,因為String不是Number的子類 handleMember(integerExample); // 不會報錯,因為Integer是Number的子類 handleSuperMember(integerExample); // 不會報錯,因為Integer和泛型類的型別相同 handleSuperMember(numberExample ); // 不會報錯,因為Number是泛型類Integer的父類 } }
- 泛型方法: 使用泛型識別符號標識的方法
// <E> 泛型識別符號 public static <E> void printArray(E[] array) { for(E element : array){ System.out.printf("%s",element); } }
- 泛型字母的含義
- E - Element: 在集合中使用,因為集合中存放的是元素
- T - Type: Java類
- K - Key: 鍵
- V - Value: 值
- N - Number: 數值型別
反射
反射的概念及作用
反射允許程式在執行時來進行自我檢查並且對內部的成員進行操作。反射主要是指程式可以訪問、檢測和修改它本身狀態或行為的一種能力,並能根據自身行為的狀態和結果,調整或修改應用所描述行為的狀態和相關的語義。
- 反射機制的作用
- 在執行時判斷任意一個物件所屬的類
- 在執行時獲取類的物件
- 在執行時訪問java物件的屬性、方法、構造方法等
- java.lang.reflect類庫裡面主要的類
- Field: 表示類中的成員變數
- Method: 表示類中的方法
- Constructor: 表示類的構造方法
- Array: 該類提供了動態建立陣列和訪問陣列元素的靜態方法
- 反射依賴的Class:用來表示執行時型別資訊的對應類
- 每個類都有唯一一個與之相應的Class物件
- Class類為類型別,而Class物件為類型別物件
- Class類的特點
- Class類也是類的一種,class則是關鍵字
- Class類只有一個私有的建構函式,只有JVM能夠建立Class類的例項
- JVM中只有唯一一個和類相對應的Class物件來描述其型別資訊
- 獲取CLass物件的三種方式
- Object -> getClass()
- 任何資料型別(包括基本資料型別)都有一個“靜態”的class屬性
- 通過Class類的靜態方法:forName(String className) (常用)
package demo.reflect; public class ReflectTarget { public static void main(String[] args) throws ClassNotFoundException { // 第一種方式獲取Class物件 ReflectTarget reflectTarget = new ReflectTarget(); Class reflectClass01 = reflectTarget.getClass(); System.out.println("1st: " + reflectClass01); //通過第二種方式獲取Class物件 Class reflectClass02 = ReflectTarget.class; System.out.println("2nd: " + reflectClass01); //比較第一種方式獲取得class物件和第二種方式獲取得class物件是否為同一個 System.out.println(reflectClass01 == reflectClass02); // 第三種方式獲取Class物件 Class reflectClass03 = Class.forName("demo.reflect.ReflectTarget"); System.out.println("3rd: " + reflectClass03); //比較第二種方式獲取得class物件和第三種方式獲取得class物件是否為同一個 System.out.println(reflectClass02 == reflectClass03); } } /************執行結果如下************/ /* * 1st: class demo.reflect.ReflectTarget * 2nd: class demo.reflect.ReflectTarget * true * 3rd: class demo.reflect.ReflectTarget * true * */ /** * 根據執行結果得知:Class物件有且僅有一個 * **/ ```
- Class物件就像一面鏡子,透過這面鏡子可以看到類的結構
反射的主要用法
- 如何獲取類的構造方法並使用
- 在我們上面自定義的ReflectTarget類中建立各種型別的建構函式,用於測試
// --------建構函式-------- // 訪問修飾符為預設的建構函式,即同包可訪問得 ReflectTarget(String str) { System.out.println("(預設)的構造方法 s= " + str); } //無參建構函式 public ReflectTarget() { System.out.println("呼叫了公有的無參建構函式。。。"); } //有一個引數的建構函式 public ReflectTarget(char name) { System.out.println("呼叫了帶有一個引數建構函式,引數為:" + name); } //有多個引數的建構函式 public ReflectTarget(String name,int index) { System.out.println("呼叫了有多個引數建構函式,引數值為【目標名】:" + name + "【序號】" + index); } //受保護的建構函式 protected ReflectTarget(boolean b) { System.out.println("受保護的構造方法:" + b); } //私有的建構函式 private ReflectTarget(int index){ System.out.println("私有的構造方法,序號:" + index); }
- 新建立一個類ConstructorCollector測試通過反射獲取目標反射類的所有構造方法
package demo.reflect; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; /** * 獲取構造方法: * 1)批量構造方法 * public Constructor[] getConstructors() 獲取所有”公有的“構造方法 * public Constructor[] getDeclaredConstructors() 獲取所有的構造方法(包括私有的、受保護的、預設和公有的) * 2)獲取單個的方法,並呼叫 * public Constructor getConstructor(Class...parameterTypes) 獲取單個的”公有的“構造方法 * public Constructor getDeclaredConstructor(Class...parameterTypes) 獲取某個構造方法(可以是私有的、受保護的、預設和公有的) * * 呼叫構造方法: Constructor --> newInstance(Object...intArgs) */ public class ConstructorCollector { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Class clazz = Class.forName("demo.reflect.ReflectTarget"); // 獲取所有的公有構造方法 System.out.println("**************所有公有的構造方法**************"); Constructor[] conArray = clazz.getConstructors(); for (Constructor con : conArray) { System.out.println(con); } // 獲取所有的構造方法 System.out.println("**************所有的構造方法(包括私有的、受保護的、預設和公有的)**************"); conArray = clazz.getDeclaredConstructors(); for (Constructor con : conArray) { System.out.println(con); } //或獲取單個帶引數的公有構造方法 System.out.println("**************獲取公有、帶兩個引數的構造方法**************"); Constructor con = clazz.getConstructor(String.class,int.class); System.out.println("con = " + con); // 獲取單個私有的構造方法 System.out.println("**************獲取單個私有的構造方法**************"); con = clazz.getDeclaredConstructor(int.class); System.out.println("con = " + con); System.out.println("##################################################"); System.out.println("**************通過私有構造方法建立例項**************"); con.setAccessible(true); // 設定暴力訪問,忽略掉訪問修飾符 ReflectTarget reflectTarget = (ReflectTarget) con.newInstance(1); } } /*** * 執行結果如下: * **************所有公有的構造方法************** * public demo.reflect.ReflectTarget(java.lang.String,int) * public demo.reflect.ReflectTarget() * public demo.reflect.ReflectTarget(char) * **************所有的構造方法(包括私有的、受保護的、預設和公有的)************** * private demo.reflect.ReflectTarget(int) * protected demo.reflect.ReflectTarget(boolean) * public demo.reflect.ReflectTarget(java.lang.String,int) * demo.reflect.ReflectTarget(java.lang.String) * public demo.reflect.ReflectTarget() * public demo.reflect.ReflectTarget(char) * **************獲取公有、帶兩個引數的構造方法************** * con = public demo.reflect.ReflectTarget(java.lang.String,int) * **************獲取單個私有的構造方法************** * con = private demo.reflect.ReflectTarget(int) * ################################################## * **************通過私有構造方法建立例項************** * 私有的構造方法,序號:1 */
- 在我們上面自定義的ReflectTarget類中建立各種型別的建構函式,用於測試
- 如何獲取類的欄位並使用
- 在我們上面自定義的ReflectTarget類中建立各種不同訪問修飾符修飾的欄位,用於測試
// --------欄位-------- public String name; protected int index; char type; private String targetInfo; @Override public String toString() { return "ReflectTarget{" + "name='" + name + '\'' + ", index=" + index + ", type=" + type + ", targetInfo='" + targetInfo+ '\'' + '}'; }
- 新建立一個類FieldCollector 測試通過反射獲取目標反射類的所有成員變數
package demo.reflect; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; /** * 獲取成員變數並呼叫: * 1)批量的 * Field[] getFields() 獲取所有的”公有欄位” * Field[] getDeclaredFields() 獲取所有欄位(包括私有的、受保護的、預設和公有的) * 2)獲取單個的 * public Field getField(String fieldName) 獲取單個的”公有的“欄位 * public Field getDeclaredField(String fieldName) 獲取某個欄位(可以是私有的、受保護的、預設和公有的) * * 設定欄位值: Field --> public void set(Object obj,Object value) * 引數說明: * 1. obj:要設定的欄位所對應的物件 * 2. value:要為欄位設定的值 */ public class FieldCollector { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //獲取class物件 Class reflectTargetClass = Class.forName("demo.reflect.ReflectTarget"); //獲取所有的公有欄位 System.out.println("**************獲取所有的公有欄位**************"); Field[] fieldArray = reflectTargetClass.getFields(); for (Field field : fieldArray) { System.out.println(field); } //獲取所有的欄位 System.out.println("**************獲取所有欄位(包括私有的、受保護的、預設和公有的)**************"); fieldArray = reflectTargetClass.getDeclaredFields(); for (Field field : fieldArray) { System.out.println(field); } // 獲取公有欄位並賦值 System.out.println("**************獲取公有欄位並賦值**************"); Field field = reflectTargetClass.getField("name"); System.out.println("公有的欄位 name = " + field); // 通過反射呼叫無參構造方法,並使用無參構造建立物件 ReflectTarget reflectTarget = (ReflectTarget)reflectTargetClass.getConstructor().newInstance(); // 給獲取物件的field屬性賦值 field.set(reflectTarget,"待反射一號"); // 驗證對應的值 name System.out.println("驗證name: " + reflectTarget.name); // 因為name屬性是公有的,所以可以直接通過例項呼叫 System.out.println("**************獲取私有欄位targetInfo並賦值**************"); field = reflectTargetClass.getDeclaredField("targetInfo"); System.out.println(field); field.setAccessible(true); // 設定暴力訪問 field.set(reflectTarget,"13730862985"); // 因為targetInfo屬性是私有的,不能直接通過例項呼叫,由於我們重寫了toString方法,所以直接列印reflectTarget物件就好了 System.out.println("驗證targetInfo: " + reflectTarget); } } /** * 執行結果如下: * **************獲取所有的公有欄位************** * public java.lang.String demo.reflect.ReflectTarget.name * **************獲取所有欄位(包括私有的、受保護的、預設和公有的)************** * public java.lang.String demo.reflect.ReflectTarget.name * protected int demo.reflect.ReflectTarget.index * char demo.reflect.ReflectTarget.type * private java.lang.String demo.reflect.ReflectTarget.targetInfo * **************獲取公有欄位並賦值************** * 公有的欄位 name = public java.lang.String demo.reflect.ReflectTarget.name * 呼叫了公有的無參建構函式。。。 * 驗證name: 待反射一號 * **************獲取私有欄位targetInfo並賦值************** * private java.lang.String demo.reflect.ReflectTarget.targetInfo * 驗證targetInfo: ReflectTarget{name='待反射一號', index=0, type= , targetInfo='13730862985'} */
- 注意:通過getField()方法可以獲取到從父類繼承的公有欄位,但getDeclareField()方法是獲取不到從父類繼承的欄位的
- 在我們上面自定義的ReflectTarget類中建立各種不同訪問修飾符修飾的欄位,用於測試
- 如何獲取類的方法並呼叫
- 在在我們上面自定義的ReflectTarget類中建立被各種不同訪問修飾符修飾的方法,用於測試
package demo.reflect; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * 獲取成員方法並呼叫: * 1)批量的 * public Method[] getMethods() 獲取所有的”公有方法”(包含了父類的方法,也包含了Object類中的公有方法) * public Method[] getDeclaredMethods() 獲取所有成員方法(包括私有的、受保護的、預設和公有的) * 2)獲取單個的 * public Method getMethod(String name,Class<?>...parameterTypes) 獲取單個的”公有的“欄位 * 引數: * name: 方法名 * Class...: 形參的Class型別物件 * public Method getDeclaredMethod(String name,Class<?>...parameterTypes) 獲取某個欄位(可以是私有的、受保護的、預設和公有的) * * 呼叫方法: * Method --> public Object invoke(Object obj,Object...args); * 引數說明: * obj: 待呼叫方法方法的物件 * args: 呼叫方法時所傳遞的實參 */ public class MethodCollector { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //獲取class物件 Class reflectTargetClass = Class.forName("demo.reflect.ReflectTarget"); // 獲取所有的公有方法 System.out.println("*******************獲取所有的public方法,包括父類和Object*******************"); Method[] methodArray = reflectTargetClass.getMethods(); for (Method method : methodArray) { System.out.println(method); } // 獲取該類所有的方法 System.out.println("*******************獲取該類所有的方法,包括私有的*******************"); methodArray = reflectTargetClass.getDeclaredMethods(); for (Method method : methodArray) { System.out.println(method); } // 獲取單個公有方法 System.out.println("*******************獲取公有的show1()*******************"); Method method = reflectTargetClass.getMethod("show1", String.class); System.out.println(method); // 通過反射呼叫無參構造方法,並使用無參構造建立物件 ReflectTarget reflectTarget = (ReflectTarget)reflectTargetClass.getConstructor().newInstance(); method.invoke(reflectTarget,"待反射方法一號"); System.out.println("*******************獲取私有的show4()*******************"); method = reflectTargetClass.getDeclaredMethod("show4", int.class); System.out.println(method); method.setAccessible(true); // 接受show4()的返回值 String result = String.valueOf(method.invoke(reflectTarget, 100)) ; System.out.println(result); } } /** * 執行結果如下:我們從執行結果可以看到通過getMethods(),獲取到Object類中的公有方法 * *******************獲取所有的public方法,包括父類和Object******************* * public static void demo.reflect.ReflectTarget.main(java.lang.String[]) throws java.lang.ClassNotFoundException * public java.lang.String demo.reflect.ReflectTarget.toString() * public void demo.reflect.ReflectTarget.show1(java.lang.String) * public final void java.lang.Object.wait() throws java.lang.InterruptedException * public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException * public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException * public boolean java.lang.Object.equals(java.lang.Object) * public native int java.lang.Object.hashCode() * public final native java.lang.Class java.lang.Object.getClass() * public final native void java.lang.Object.notify() * public final native void java.lang.Object.notifyAll() * *******************獲取該類所有的方法,包括私有的******************* * public static void demo.reflect.ReflectTarget.main(java.lang.String[]) throws java.lang.ClassNotFoundException * public java.lang.String demo.reflect.ReflectTarget.toString() * public void demo.reflect.ReflectTarget.show1(java.lang.String) * private java.lang.String demo.reflect.ReflectTarget.show4(int) * protected void demo.reflect.ReflectTarget.show2() * void demo.reflect.ReflectTarget.show3() * *******************獲取公有的show1()******************* * public void demo.reflect.ReflectTarget.show1(java.lang.String) * 呼叫了公有的無參建構函式。。。 * 呼叫了公有的,String引數的show1(): str = 待反射方法一號 * *******************獲取私有的show4()******************* * private java.lang.String demo.reflect.ReflectTarget.show4(int) * 呼叫了私有的,並且有返回值的,int引數的show4(): index = 100 * show4Result */
- 在在我們上面自定義的ReflectTarget類中建立被各種不同訪問修飾符修飾的方法,用於測試
註解
註解介紹及作用
由於反射需要獲取到相關的類全名(類名+包名),因此我們還需要記錄哪些類是通過反射來獲取的。我們可以通過XML來儲存類相關的資訊已供反射用,此外,我們還可以通過註解來儲存類相關資訊以供反射呼叫。
註解:提供一種為程式元素設定後設資料的方法
- 後設資料是新增到程式元素如方法、欄位、類和包上的額外資訊
- 註解是一種分散式的後設資料設定方式,XML是集中式的設定方式
- 註解不能直接干擾程式執行
- 反編譯位元組碼檔案的指令:javap -verbose com.reminis.demo.annotation.TestAnnotation,通過反編譯可以看到我們的自定義註解自動繼承了Annotation
- 註解的功能
- 作為特定得標記,用於告訴編譯器一些資訊
- 編譯時動態處理,如動態生成程式碼
- 執行時動態處理,作為額外資訊的載體,如獲取註解資訊
- 註解的分類
- 標準註解:Override、Deprecated、SuppressWarnings
- 元註解:@Retention、@Target、@Inherited、@Documented,用於修飾註解的註解,通常用在註解的定義上
- @Target:註解的作用目標,描述所修飾註解的使用範圍
- packages、types(類、介面、列舉、Annotation型別)
- 型別成員(方法、構造方法、成員變數、列舉值)
- 方法引數和本地變數(如迴圈變數、catch引數)
- @Retention:註解的生命週期(標註註解保留時間的長短)
- @Documented:註解是否應當被包含在JavaDoc文件中
- @Inherited:是否允許子類繼承該註解
- @Target:註解的作用目標,描述所修飾註解的使用範圍
自定義註解的實現
自定義註解自動實現了 java.lang.annotation.Annotation
public @interface 註解名{
修飾符 返回值 屬性名() 預設值;
修飾符 返回值 屬性名() 預設值;
...
}
註解屬性支援的型別:所有的基本型別(int,float,boolean,byte,double,char,long,short)、 String 型別、 Class型別、Enum型別、Annotation型別、以上所有型別的陣列。
我們現在自定義一個註解PersonInfoAnnotation,可以用在欄位上,在程式執行時有效,如下:
package demo.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定義註解
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PersonInfoAnnotation {
//名字
public String name();
// 年齡
public int age() default 19;
// 性別
public String gender() default "男";
// 開發語言
public String[] language();
}
我們再自定義一個註解CourseInfoAnnotation,該註解可以用在類和方法上,在程式執行時有效,如下:
package demo.annotation;
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 CourseInfoAnnotation {
// 課程名稱
public String courseName();
// 課程 標籤
public String courseTag();
// 課程簡介
public String courseProfile();
// 課程代號
public int courseIndex() default 107;
}
新建立一個SelfStudyCourse類,在該類上及該類的欄位和方法上,使用我們上面的已經定義好了的註解
package demo.annotation;
@CourseInfoAnnotation(courseName = "計算機網路",courseTag = "計算機基礎",
courseProfile = "計算機網路學習的核心內容就是網路協議的學習。" +
"網路協議是為計算機網路中進行資料交換而建立的規則、標準或者說是約定的集合。" +
"因為不同使用者的資料終端可能採取的字符集是不同的,兩者需要進行通訊,必須要在一定的標準上進行")
public class SelfStudyCourse {
@PersonInfoAnnotation(name = "新一",language = {"Java","C++","Go","Python"})
public String author;
@CourseInfoAnnotation(courseName = "Linux 教程",courseTag = "程式設計基礎",
courseProfile = "Linux 遵循 GNU 通用公共許可證(GPL),任何個人和機構都可以自由地使用 Linux 的所有底層原始碼,也可以自由地修改和再發布。" +
"由於 Linux 是自由軟體,任何人都可以建立一個符合自己需求的 Linux 發行版",courseIndex = 208)
public void getCourseInfo(){
}
}
建立測試類AnnotationDemo,呼叫上面使用了自定義註解的類的方法,檢視執行資訊
package demo.annotation;
public class AnnotationDemo {
public static void main(String[] args) {
SelfStudyCourse selfStudyCourse = new SelfStudyCourse();
selfStudyCourse.getCourseInfo();
System.out.println("finish");
}
}
/**
* 執行結果:
* finish
* 根據執行結果可以看出,在程式中如果不對註解進行處理,和不加註解輸出的資訊是一致的,
* */
如果我們不對註解進行處理,那和不加是沒有區別的,那我們如何獲取註解上得資訊呢?通過前面說到得反射,我們檢視反射涉及到得幾個主要類(Field,Method,Constructor,Class)得原始碼可以知道,這些跟反射相關得類都實現了AnnotatedElement介面,我們通過檢視AnnotatedElement介面的原始碼,常用的有如下幾個方法:
- Annotation[] getAnnotations(); // 用來獲取物件上的所有註解,包括繼承過來的
T getAnnotation(Class annotationClass); // 獲取物件上單個指定的註解 - boolean isAnnotationPresent(Class<? extends Annotation> annotationClass); //用來判斷是否有某個指定的註解
現在我們通過反射來獲取類註解上的資訊,程式碼實現如下:
package demo.annotation;
import java.lang.annotation.Annotation;
public class AnnotationParse {
//解析類上面的註解
public static void parseTypeAnnotation() throws ClassNotFoundException {
Class<?> clazz = Class.forName("demo.annotation.SelfStudyCourse");
// 這裡獲取的是class物件的註解,而不是其裡面的方法和成員變數的註解
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
CourseInfoAnnotation courseInfoAnnotation = (CourseInfoAnnotation) annotation;
System.out.println("課程名: " +courseInfoAnnotation.courseName() + "\n" +
"課程標籤: " + courseInfoAnnotation.courseTag() + "\n" +
"課程簡介: "+ courseInfoAnnotation.courseProfile() + "\n" +
"課程代號: " + courseInfoAnnotation.courseIndex());
}
}
public static void main(String[] args) throws ClassNotFoundException {
parseTypeAnnotation();
}
}
/**
* 程式執行結果如下:
* 課程名: 計算機網路
* 課程標籤: 計算機基礎
* 課程簡介: 計算機網路學習的核心內容就是網路協議的學習。網路協議是為計算機網路中進行資料交換而建立的規則、標準或者說是約定的集合。因為不同使用者的資料終端可能採取的字符集是不同的,兩者需要進行通訊,必須要在一定的標準上進行
* 課程代號: 107
*/
現在通過來反射來獲取成員變數和方法上的註解資訊
// 解析欄位上的註解資訊
public static void parseFieldAnnotation() throws ClassNotFoundException {
Class<?> clazz = Class.forName("demo.annotation.SelfStudyCourse");
// 獲取該物件的所有成員變數
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
// 判斷成員變數中是否有指定註解型別的註解
boolean hasAnnotation = field.isAnnotationPresent(PersonInfoAnnotation.class);
if (hasAnnotation) {
PersonInfoAnnotation personInfoAnnotation = field.getAnnotation(PersonInfoAnnotation.class);
System.out.println("名字: " + personInfoAnnotation.name() + "\n" +
"年齡: " + personInfoAnnotation.age() + "\n" +
"性別: " + personInfoAnnotation.gender());
for (String language : personInfoAnnotation.language()) {
System.out.println("課程名: " + language);
}
}
}
}
// 解析方法上的註解資訊
public static void parseMethodAnnotation() throws ClassNotFoundException {
Class<?> clazz = Class.forName("demo.annotation.SelfStudyCourse");
// 獲取該物件的所有成員變數
Method[] methods = clazz.getMethods();
for (Method method : methods) {
// 判斷方法中是否有指定註解型別的註解
boolean hasAnnotation = method.isAnnotationPresent(CourseInfoAnnotation.class);
if (hasAnnotation) {
CourseInfoAnnotation methodAnnotation = method.getAnnotation(CourseInfoAnnotation.class);
System.out.println("課程名: " +methodAnnotation.courseName() + "\n" +
"課程標籤: " + methodAnnotation.courseTag() + "\n" +
"課程簡介: "+ methodAnnotation.courseProfile() + "\n" +
"課程代號: " + methodAnnotation.courseIndex());
}
}
}
註解獲取屬性值的底層實現,是通過JVM為註解生成代理物件。
註解的工作原理
- 通過鍵值對的形式為註解屬性賦值
- 編譯器檢查註解的使用範圍,將註解資訊寫入元素屬性表,
- 執行時JVM將RUNTIME的所有註解屬性取出並最終存入map裡
- 建立AnnotationInvocationHandler例項並傳入前面的map中
- JVM使用JDK動態代理為註解生成代理類,並初始化對應的處理器(AnnotationInvocationHandler)
- 呼叫invoke方法,通過傳入方法名返回註解對應的屬性值