關於Java中泛型、反射和註解的掃盲篇
泛型
泛型概念
泛型是在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方法,通過傳入方法名返回註解對應的屬性值