一、註解篇 1.1、註解的基本概念 註解:一種代碼級別的說明,它是JDK1.5及以後版本引入的一個特性,與類、介面、枚舉是在同一個層次;它可以聲明在包、類、欄位、方法、局部變數、方法參數等的前面,用來對這些元素進行說明,註釋. 要將註解(annotation)和註釋(commnet)分開,註釋是給人 ...
一、註解篇
1.1、註解的基本概念
-
註解:一種代碼級別的說明,它是JDK1.5及以後版本引入的一個特性,與類、介面、枚舉是在同一個層次;它可以聲明在包、類、欄位、方法、局部變數、方法參數等的前面,用來對這些元素進行說明,註釋.
要將註解(annotation)和註釋(commnet)分開,註釋是給人看的,機器不會執行,而註解主要是是給機器“看的”;
比如多線程中重寫run()方法,會有一個
@Override
註解,該註解一是給人說明這個方式是一個重寫方法,而是給機器做檢查(如果你重寫的方法名等不正確會報錯!)註解和反射是許多java框架的底層,因此必須學好!
-
作用:
- 編寫文檔:通過代碼里標識的元數據生成文檔【生成文檔doc文檔】
- 代碼分析:通過代碼里標識的元數據對代碼進行分析【使用反射】
- 編譯檢查:通過代碼里標識的元數據讓編譯器能夠實現基本的編譯檢查【Override】
總結:註解就是java代碼的一種,可以認為是一種類型,並且主要是給機器看的
-
註解格式
@<註解名>[(參數)] # @Override # @SuppressWarning("all")
1.2、java內置註解
1.2.1、作用在代碼的註解
①、@Override
-
作用: 檢查該方法是否是重寫方法;如果發現其父類,或者是引用的介面中並沒有該方法時,會報編譯錯誤.
-
代碼演示:
package kuang.annotation.lesson1; /** * 測試Override的案例 */ public class TestOverride { @Override public String tostring() { // 重寫的toString方法,這裡將S改為小寫會報錯,因為Object中沒有該方法 return "TestOverride{}"; } public static void main(String[] args) { } }
-
效果展示:
上方代碼將
toString
方法的S
改為了小寫,@Overside
檢查後發現父類(Object類)中沒有該方法,會發生編譯錯誤
②、@Deprecated
-
作用:標記過時方法,標明該方法在該版本JDK過時或有更好的替代;如果使用該方法,會報編譯警告,但不影響運行。
-
代碼演示:
package kuang.annotation.lesson1; /** * 測試Deprecated的演示 */ public class TestDeprecated extends Thread { /** * 參數since,表示已註解的API元素已被棄用的版本 * 參數forRemoval,元素表示註解的API素在將來的版本中是否被刪除 * 這兩個參數是java9之後新增的,平時可以不用 */ @Deprecated(since = "9",forRemoval = true) // 使用廢棄註解標明該方法已經不推薦使用 public static void test() { System.out.println("廢棄註解測試"); } public static void main(String[] args) { test(); // 廢棄方法在同一個類中,會直接標明該方法 new Thread("小明").stop(); // 通過繼承等方式調用被棄用方法,會有個橫線劃掉方法 } }
-
效果展示
③、@SuppressWarnings
-
作用:指示編譯器去忽略註解中聲明的警告,可以使用參數標明鎮壓警告的對象,可以是構造器、方法、類等
-
代碼演示:
package kuang.annotation.lesson1; /** * 測試鎮壓警告註解的demo */ @SuppressWarnings("all") // 使用鎮壓警告後,idea不會提示警告代碼,並且代碼在編譯時,jvm也會包容警告 public class TestSuppressWarning { public static void main(String[] args) { // 未使用的變數發生警告 int a; int c; int[] array = new int[1024]; System.out.println(1 == 1.2); // 警告 } }
-
效果展示:
idea沒有提示警告代碼
1.2.2、元註解
元註解(meta-annotation),就是負責註解其他註解的註解,套娃滴幹活
-
元註解
@Target
:用來標明註解的使用範圍@Retention
:指定其所修飾的註解的保留策略,保留策略有三個:SOURCE(源碼級)、CLASS(位元組碼級)、RUNTIME(運行時級),最用範圍依次變大@Document
:該註解是一個標記註解,用於指示一個註解將被文檔化@Inherited
:該註解使父類的註解能被其子類繼承@Repeatable
:Java 8 開始支持,標識某註解可以在同一個聲明上使用多次、@FunctionalInterface
:Java 8 開始支持,標識一個匿名函數或函數式介面@SafeVarargs
: Java 7 開始支持,忽略任何使用參數為泛型變數的方法或構造函數調用產生的警告
-
代碼演示
package kuang.annotation.lesson1; import java.lang.annotation.*; public class TestMetaAnnotation { public static void main(String[] args) { } @Target(ElementType.METHOD) // 註解應用於方法上 @Retention(value = RetentionPolicy.RUNTIME) // 表示該註解一直到運行時有效 @Documented // 表示將註解生成在javaDoc中,不加則在javaDoc中不生成 @Inherited // 子類可以繼承父類的註解 @interface myAnnotation { // 我定義的註解 } }
1.3、自定義註解
-
規範
- 使用關鍵字
@interface <註解名>
定義註解 - 註解體中可以包含配置參數,配置參數的形式為
<基本類型> 配置參數名()
,必須加上括弧,類似一個方法 - 配置參數可以有多個,且名稱自定義,可以使用
default
關鍵字給配置參數加上預設值 - 帶配置參數的註解被引用時,參數部分必須以鍵值對形式寫出來,有預設值的可以不寫(也可以寫,就是覆蓋掉預設值),沒有預設值的配置參數必須寫上值
- 單個配置參數可以使用預設參數名
value
,該名稱在註解被時引用時可以省略
- 使用關鍵字
-
代碼演示
註解定義部分
package kuang.annotation.lesson1; import java.lang.annotation.*; /** * 註解定義: * 1.@interface關鍵字來定義註解 * 2.註解體中可以包含配置參數 * 3.配置參數需要有類型和配置參數名,參數類型都是基本數據類型,參數名類似方法名,需要用() * 4.自定義註解一般需要使用元註解,如@Target等來修飾,但可有可無 */ @Target(ElementType.METHOD) // 表示註解的使用範圍是方法,如果不加該註解則自定義註解可以應用在任何地方 @Retention(RetentionPolicy.RUNTIME) // 不寫該註解預設保留策略為CLASS級別 public @interface MyAnnotation { String name() default "張三"; // 配置參數名為name,類型為String int age() default 0; String[] school() default {"北大","清華"}; // default可以給配置參數帶上預設值 int id(); // 不帶預設值,在該註解被引用時,必須顯式寫上 } @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @interface MyAnno2 { // 單個配置參數,可以只用預設的參數名->value,預設參數名在註解引用時可以不寫出 // 見SuppressWarning註解,它只含有一個單個的配置參數,且使用預設的配置參數名value,在註解被引用時,可以省略預設參數名 String value(); }
引用部分
package kuang.annotation.lesson1; /** * 測試自定義註解的demo */ public class TestMyAnnotation { // 自定義配置名稱的註解,在寫入參數時必須使用鍵值對形式 // 這裡配置參數name有預設值,不寫的話,這裡也不會報錯 // 但不帶預設值必須顯式寫出來,即這裡的id @MyAnnotation(age = 18,school = {"家裡蹲"},id = 2022) public void test01(String name, int age, String[] school, int id) { System.out.println("自定義註解的測試"); } @MyAnno2("hello") // 預設參數名value的註解在被引用時,可以省略參數名 public void test02() { } }
二、反射篇
2.1、反射的基本概念
2.1.1、引入
-
動態語言:在運行時代碼可以根據某些條件改變自身結構,比如新的方法、對象、代碼的引入,已有的代碼可以在結構上發生變化;常見的動態語言比如:JS、PHP、Python等,可以發現他們都是解釋型語言,這類語言最大的特點就是在運行時才確定某些代碼的性質,比如數據類型等,並且運行時可以改變自身結構,比如PHP和JS可以使用執行函數eval()來執行一些非代碼的字元串等
-
靜態語言:運行時結構不可改變的語言,比如C/C++、java;常見的類C語言都是靜態語言,這類語言最大的特點就是運行前必須規定代碼的性質,比如必須規定一個變數的數據類型,否則連編譯都無法通過,並且已一個類在運行時無法獲取其內部信息
反射機制:讓程式可以訪問、檢測和修改它本身狀態或行為;java反射機制是指在Java程式運行狀態中,對於任何一個類,都可以獲得這個類的所有屬性和方法;對於給定的一個對象,都能夠調用它的任意一個屬性和方法。這種動態獲取類的內容以及動態調用對象的方法稱為反射機制;
-
反射機制的核心:能夠分析類的能力是java反射機制的核心,我們可以在運行時獲得程式或者程式集中每個類型的成員已經成員的信息
2.1.2、應用
java反射機制主要提供了以下功能:
- 在運行時判斷任意一個對象所屬的類。
- 在運行時構造任意一個類的對象。
- 在運行時判斷任意一個類所具有的成員變數和方法。
- 在運行時調用任意一個對象的方法。
- 生成動態代理。
- 獲取泛型信息。
- 獲取註解信息。
2.1.3 韓順平反射補充
2.1.3.1 反射機制原理圖
解釋:某類的源碼通過javac編譯成jvm位元組碼,我們通常在使用時通過new來將類實例化為對象,並通過對象調用成員方法等;在實例化為對象前,該類首先要被類載入器ClassLoader載入至堆區,併在堆區創建該類的Class類對象,該Class類對象包含被載入類的所有信息,且無論被載入類實例化出多少對象,它有且只有一個Class類對象;實例化出的被載入類對象記錄著自己屬於哪一個Class類對象;反射機制即是通過Class類對象來直接創建對象、操作屬性等
2.1.3.2 反射簡單應用舉例
-
需求
-
源碼實現
-
配置文件
re.properties
classfullpath = com.hspedu.Cat method = hi
-
Cat類
package com.hspedu; public class Cat { private String name = "tomcat"; public void hi() { System.out.println("hi, i am " + name); } }
-
ReflectionQuestion類
package com.hspedu.reflection.question; import com.hspedu.Cat; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Properties; /** * 韓java 01-02 */ public class ReflectionQuestion { public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { // 1、傳統方法,new出類的實例化對象 Cat cat = new Cat(); cat.hi(); System.out.println("_________________________"); // 2、通過I/O流的Properties類讀出配置文件 Properties properties = new Properties(); properties.load(Files.newInputStream(Paths.get("src\\re.properties"))); // 獲取配置文件鍵值對的值 String classpath = properties.get("classfullpath").toString(); String method = properties.get("method").toString(); System.out.println(classpath); System.out.println(method); System.out.println("_____________________________"); // 3、反射機制 Class<?> cls = Class.forName(classpath); // 載入Cat類並範圍其唯一的Class類對象 Object o = cls.newInstance(); // 通過Class類對象cls,得到載入類的實例對象 //System.out.println(o.getClass()); // 測試o真正的類型是否為Cat類 Method method1 = cls.getMethod(method);// 通過cls返回載入類的method的方法對象,即將Cat的hi方法通過對象形式返回並調用 method1.invoke(o); // 通過方法對象援引載入類的實例對象來調用方法 } }
當Cat類中新增了一個方法cry,我們僅需通過修改配置文件的method值即可,而不用修改源碼
-
修改的Cat類
package com.hspedu; public class Cat { private String name = "tomcat"; public void hi() { System.out.println("hi, i am " + name); } // 新增方法 public void cry() { System.out.println("tomcat is cry"); } }
-
修改的配置文件
classfullpath = com.hspedu.Cat method = cry // x
ReflectionQuestion類的修改
- 通過new的傳統方法
// 1、傳統方法,new出類的實例化對象 Cat cat = new Cat(); // cat.hi(); --> 修改源碼為 cat.cry(); System.out.println("_________________________");
- 而反射機制不需要修改源碼,僅需修改配置文件即可,符合ocp開閉原則(對修改封閉,對拓展開放)
-
2.2、反射的簡單使用
2.2.1、獲得反射對象
-
Java反射運行程式再執行期間藉助
refelction API
獲得任何類的內部信息,直接操作任意對象的內部屬性及其方法 -
反射對象獲取方式:
獲取方法 解釋 Class.forName(<全類名>) 編譯階段,一般用配置文件中已知全類名信息 <類名>.class 載入階段,一般用於參數傳遞 <實例化對象>.getClass() 運行階段,已經有載入類的實例化對象 classLoader = <實例化對象>.getClass().getClassLoader()
classLoader.loadClass(cls)通過類載入器獲取 <基本數據類型>.class 基本數據類型獲取Class對象 <包裝類>.TYPE 基本數據類型的包裝類獲取Class對象 -
載入完類後,會在記憶體中產生一個Class的對象,並且
一個類只有一個Class對象
,該Class對象包含整個類的完整結構,我們可以通過這個Class對象得到類的所有信息;這個Class對象就像一面鏡子,通過鏡子看到了類的結構,所以形象的稱之為反射 -
代碼演示
package kuang.Reflection.lesson2; /** * 通過反射獲得類的Class對象de體驗 */ public class TestObjectToClass { public static void main(String[] args) throws ClassNotFoundException { // 通過反射獲得類的class對象 Class<?> c1 = Class.forName("kuang.Reflection.lesson2.TestObjectToClass"); System.out.println(c1); // 一個類在記憶體中只有一個Class對象 // 一個類被載入後類的整個結構都會被封裝在Class對象中 Class<?> c2 = Class.forName("kuang.Reflection.lesson2.TestObjectToClass"); Class<?> c3 = Class.forName("kuang.Reflection.lesson2.TestObjectToClass"); Class<?> c4 = Class.forName("kuang.Reflection.lesson2.TestObjectToClass"); // 由hashcode相等可得,一個類在記憶體中只有一個Class對象 System.out.println(c2.hashCode()); System.out.println(c3.hashCode()); System.out.println(c4.hashCode()); } } /** * 實體類,pojo */ class Person { String name; int age; int id; public Person() {} public Person(String name, int age, int id) { this.name = name; this.age = age; this.id = id; } 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 int getId() { return id; } public void setId(int id) { this.id = id; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", id=" + id + '}'; } }
-
輸出結果
2.2.2 韓順平反射相關類補充(P4)
-
相關類及其作用
-
應用
package com.hspedu.reflection; import com.hspedu.Cat; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * 02韓順平反射相關類,p4 */ public class Reflection_P4 { public static void main(String[] args) throws Exception { // 01,反射相關類之Class類,表示某個載入類在記憶體中的Class對象,該Class對象有且只有一個 Class<?> aClass = Class.forName("com.hspedu.Cat"); // 和上方作用相同,都是返回對應的Class對象,不過下麵這個是通過載入類的實例化對象逆向得到的 Cat cat = new Cat(); Class<? extends Cat> aClass1 = cat.getClass(); // 通過載入類的Class對象實例化出被載入類的對象 Object o = aClass.newInstance(); Cat cat1 = aClass1.newInstance(); // 這裡直接指定為Cat類是因為它本身就是通過Cat的實例化對象逆向產生的 // 02,反射相關類之Method,創建某個載入類的成員方法的方法對象 Method hi = aClass.getMethod("hi"); // 得到Cat類的hi成員方法的方法對象 // 該方法對象援引載入類的實例化對象即可執行該方法,這也是反射之精妙所在 // 普通方式:實例化對象.成員方法 // 反射方式:成員方法對象.方法(實例化對象) hi.invoke(o); // 03,反射相關類之Field,創建載入類的成員屬性的對象 Field ageField = aClass.getField("age"); // 該方式不能得到私有屬性 System.out.println("age == " + ageField.get(o)); // 通過反射方式列印欄位數據 // 04,反射相關類之Constructor,創建載入類的構造器對象 Constructor<?> constructor1 = aClass.getConstructor(); // 返回無參構造器 System.out.println("無參構造器:"+ constructor1); // 創建有參構造器對象,要註意getConstructor方法的參數是Class類型,通過int.class即可得到int的Class類對象 Constructor<?> constructor2 = aClass.getConstructor(int.class); System.out.println("有參構造器:" + constructor2); } }
-
輸出截圖
2.2.3 反射機制優化
-
反射機制優點:可以動態的創建和使用對象,是所有框架的底層
-
反射機制缺點:反射機制的執行手段基本是解釋執行,且對載入類的欄位、方法、構造器有可訪問性檢測(訪問檢查)
-
優化手段:在使用反射機制調用載入類的欄位、方法、構造器時手動關閉訪問檢查
setAccessible(); // 傳入參數為true時關閉訪問檢查 // 使用方法舉例 Object o = aClass.newInstance(); Method hi = aClass.getMethod("hi"); hi.setAccessible(true); // 傳入參數true ···
2.3 認識Class類
2.3.1 Class類分析
-
Class類也是類,因此也繼承Object類
-
Class不能通過new創建,它是由系統創建在堆區中的
某載入類的位元組碼二進位數據首先會通過類載入器CLassLoader的
loadClass()
方法生成該類的Class類對象(類載入機制),然後再實例化出對象;並且類載入過程在同一程式中只會進行依次,下一次實例化對象會直接使用已經在堆區生成的唯一個Class對象 -
某個類的Class對象在記憶體中有且只有一個,並且類載入只進行一次
-
每個類的實例化對象記錄著自己由哪個Class對象所生成
-
通過Class類對象可以得到一個類的完成結構
-
類的位元組碼二進位數據存放在方法區(類的元數據),類的Class類對象存放在堆區
代碼印證第三條
package com.hspedu.reflection; import com.hspedu.Cat; /** * han _p6,反射機制之Class類 */ public class Reflection_P6 { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { Cat cat = new Cat(); // 通過new實例化對象,Class對象已經生成在堆區 /*public Class<?> loadClass(String name) throws ClassNotFoundException { return loadClass(name, false); }*/ Cat cat1 = new Cat(); // 打斷點調試,不會再執行loadClass來生成Class對象 // 通過反射機制創建類的實例化對象 Class<?> aClass1 = Class.forName("com.hspedu.Cat"); Class<?> aClass2 = Class.forName("com.hspedu.Cat"); // 哈希值相同,說明Class類對象相同,說明只有一個Class類對象 System.out.println(aClass1.hashCode()); System.out.println(aClass2.hashCode()); } }
2.3.2 Class常用方法
方法名 | 作用 |
---|---|
Class.forName(<載入類的全類名>) | 獲得載入類的Class類對象 Class<?> cls = Class.forName(classFullPath); |
cls.getClass() | 獲取運行類型 |
cls.getPackage() | 獲取包信息 |
cls.getName() | 獲取全類名 |
cls.newInstance()》 cls.getDeclaredConstructor().newInstance() |
通過反射創建對象(該方法再java9之後過時,添加下麵的方式) |
cls.getFields() | 獲取所有欄位信息,返回欄位集數組 |
package com.hspedu.reflection._Class;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
@SuppressWarnings("all")
public class Class01_p7 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
String classFullPath = "com.hspedu.Car";
// 獲取Car類的Class類對象
Class<?> cls = Class.forName(classFullPath);
// 顯示cls是哪個載入類的Class對象
System.out.println(cls);
// 獲得運行類型
System.out.println(cls.getClass());
// 獲取包名
System.out.println(cls.getPackage().getName());
// 獲取載入類的全類名
System.out.println(cls.getName());
// 通過反射創建對象
Object o = cls.getDeclaredConstructor().newInstance();
// 通過反射獲取所有欄位信息,獲取所有方法等方式類似
Field[] fields = cls.getFields();
for(Field f : fields) {
System.out.println(f.getName() + "==" + f.get(o));
}
}
}
2.4 動態和靜態載入
-
區別:靜態載入在程式編譯階段就將程式內所引用的資源全部載入進來,如果資源不存在則編譯不通過;動態載入則是在程式運行階段時,當程式中所引用的資源不存在時才會去載入,即使所引用資源不存在也可以通過編譯階段。
-
通過反射機制可以實現動態載入,即用時載入
-
代碼示例:
package com.hspedu.reflection._Class; import java.lang.reflect.Method; import java.util.Scanner; /** * 靜態載入和動態載入——p10 */ public class ClassLoaderTest { public static void main(String[] args) throws Exception { Scanner scanner = new Scanner(System.in); System.out.println("請輸入對應數字"); int num = scanner.nextInt(); switch (num) { case 1: // 通過反射實現動態載入,即使person不存在也可以通過編譯 Class<?> person = Class.forName("Person"); Object o = person.newInstance(); Method hi = person.getMethod("hi"); hi.invoke(o); break; case 2: // 靜態載入,當Car類不存在,編譯不通過 // Car car = new Car(); // 通過javac命令編譯,去掉註釋編譯不通過,因為是靜態載入 System.out.println("2"); default: System.out.println("do nothing"); } } }
上述代碼中,Person類不存在,但可以通過編譯,說明反射是動態載入解釋執行
2.5 反射爆破操作
即通過反射機制突破所訪問對象的限定符,主要是針對私有屬性無法直接獲取的問題
2.5.1 屬性
-
獲取屬性對象的兩種方式:
// 方式1: cls.getField() // 只能獲取public修飾的 cls.getDeclaredFiled() // 可以獲取所有屬性,包括私有的
-
私有屬性爆破方式
<屬性對象>.setAccessible(true)
-
變數賦值方式
<屬性對象>.set(o,<值>) // 如果是靜態屬性,o可以寫為null
-
案例代碼
Student類
package com.hspedu; public class Student { public String name; private int age; public static int num; @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
測試類
package com.hspedu.reflection; import java.lang.reflect.Field; /** * 通過反射爆破屬性_p17 */ public class ReflectAccessProperty { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException { Class<?> stuClass = Class.forName("com.hspedu.Student"); Object o = stuClass.newInstance(); // 通過反射獲取public屬性,並設置值 Field name = stuClass.getField("name"); name.set(o,"張三"); // 通過反射獲取私有屬性,並設置值 Field age = stuClass.getDeclaredField("age"); age.setAccessible(true); // 私有屬性爆破 age.set(o,18); // 通過反射獲取static變數,並設置值 Field num = stuClass.getField("num"); num.set(null,1433223); System.out.println(o); } }
2.5.2 方法
-
獲取方法對象的方式
cls.getMethod("<方法名>",[<XXX>.class···]) // 獲取公有方法,xxx.class即參數類型的class對象,無參則不寫 cls.getDeclaredMethod("<方法名>",<XXX>.class) // 獲取所有方法
-
爆破:
m.setAccessible(true)
-
調用方法
m.invoke(o,<實參列表>); // 針對不同方法有不同返回值 // 靜態方法o可以寫為null
-
案例代碼
Student類
package com.hspedu; public class Student { public String name; private int age; public static int num; public String setNameAndPrint(String name) { this.name = name; return "名字是" + name; } private String setAgeAndPrint(int age) { this.age = age; return "年齡是" + age; } public static void hi(String person) { System.out.println("你好" + person); } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
測試類
package com.hspedu.reflection; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class ReflectAccessMethod { public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException { Class<?> stuClass = Class.forName("com.hspedu.Student"); Object o = stuClass.newInstance(); // 獲取公有方法 Method name = stuClass.getMethod("setNameAndPrint", String.class); // 執行方法 Object returnName = name.invoke(o, "張三"); // 獲取私有方法 Method age = stuClass.getDeclaredMethod("setAgeAndPrint", int.class); // 爆破私有限定符 age.setAccessible(true); // 執行方法 Object returnAge = age.invoke(o, 18); // 獲取靜態方法 Method hi = stuClass.getMethod("hi",String.class); // 執行方法 hi.invoke(null,"瑪卡巴卡"); System.out.println(returnName); System.out.println(returnAge); } }
2.6 反射獲取註解信息
-
註解依附於不同的Target,因此要根據註解位置的不同,創建不同的Class類對象
例如某註解是TYPE上的註解,依附於某個類,就要先創建該類的Class對象;依附於某欄位屬性上,就要在該類的Class對象的基礎上創建Field對象
-
通過反射獲取註解信息是許多框架的底層操作,也是學習myBatis等的知識點
-
案例代碼
package com.hspedu.ReflectToAnno; import java.lang.annotation.Annotation; import java.lang.annotation.Target; import java.lang.reflect.Field; import java.util.Arrays; /** * 反射獲取註解信息測試 */ public class Test { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException { Class<?> c = Class.forName("com.hspedu.ReflectToAnno.Person"); // 獲取指定註解的value值 TableName table = c.getAnnotation(TableName.class); String v = table.value(); System.out.println("tableName is " + v); // 獲得指定位置上的註解,這裡獲取Person類屬性上的 Field name = c.getDeclaredField("name"); FieldInfo fieldInfo = name.getAnnotation(FieldInfo.class); System.out.println(fieldInfo.columnName()); System.out.println(fieldInfo.type()); System.out.println(fieldInfo.length()); // 獲取註解上的註解信息 Class<?> aClass = Class.forName("com.hspedu.ReflectToAnno.FieldInfo"); Annotation[] annotations = aClass.getAnnotations(); for (Annotation a : annotations) { System.out.println(a); } Target target = aClass.getAnnotation(Target.class); System.out.println(Arrays.toString(target.value())); } }