目錄
#0.總覽
#1.?類的反射
①介紹
②獲取
③作用
獲取構造函數:
創建實例:
字段操作:?? ? ?
方法操作:
獲取修飾符:
#2.總結?
#0.總覽
反射,官方是這樣介紹它的:
Reflection is a feature in the Java programming language. It allows an executing Java program to examine or "introspect" upon itself, and manipulate internal properties of the program.
翻譯過來就是:反射是 Java 編程語言中的一項功能。它允許正在執行的 Java 程序檢查或“內省”自身,并作程序的內部屬性。
再翻譯成人話就是:反射可以讓程序可以把本身的各種方法,參數當作變量來使用。?
為什么翻譯成“反射”?因為這個過程就像是照鏡子:程序通過照鏡子知道自己有什么方法和參數。
詳細的說,有:
- 獲取類的信息(類名、方法、字段、構造方法等)。
- 創建類的實例。
- 調用類的方法、修改字段的值。
- 動態地加載類和進行方法調用。
- 與代理息息相關
#1.?類的反射
①介紹
? ? ? ? 首先得介紹一下Class類
Class類是反射的核心部分
Class類和真實的類對象不是一個東西!Class類只是包含了對應類關于其涉及反射的一系列操作的類。每個類都會有屬于自己的Class對象:
列如 String.class? ? ? ? ? ? ?Integer.class等
嘗試輸出:
System.out.println(String.class);
會得到?class java.lang.String
這是類的全限定類名,可以看成是類的標識。
②獲取
通過一些方法,我們能獲得一個類的Class對象:
Class ex1 = Class.forName("java.lang.String");
Class ex2 =?String.class;
String string = new String();
Class ex3 =?string.getClass();
那獲取到Class類有什么用呢??
③作用
獲取構造函數:
拿這個類來舉例:
public class Temp {public int a;public int b;private int c = 10;public Temp() {}public Temp(int a) {this.a = a;}private Temp(int a, int b) {this.a = a;this.b = b;}public void sing(){System.out.println("ahhhhhh!");}private void dance(){System.out.println("haaaaaa!");}
}
Class ex = Temp.class;ex.getConstructor();? ? ex.getConstructor(int.class);? ? ? ? ?
這會獲得其一個指定的公共構造方法,注意!括號中的參數要這樣寫上對應的Class類才能獲取到!
ex.getDeclaredConstructor();則可以獲取到所有構造方法
相對的,Constructor<?>[] constructors =?ex.getConstructors();則會獲取所有公用構造方法
getDeclaredConstructors同理
? ? ? ?
創建實例:
Constructor constructor = ex.getConstructor(int.class);
Temp obj = (Temp) constructor.newInstance( 30);這樣就根據指定的構造方法創建出了一個指定的實例
? ? ? ?
字段操作:?
Temp obj = (Temp) constructor.newInstance( 30);
Field field = ex.getField("a");
System.out.println(field);
int value = field.getInt(obj);?
System.out.println(value);這里會輸出 30
注意!除了getInt還有getchar等對應基本數據類型,其他的統一為get()
但其實get()也能處理基本數據類型
格式為?Field.get(已實例化的對象)
對于私有變量,無獨有偶:getDeclaredField(“c”)即可
但是只能System.out.println(field);,即獲取全限定字段名,無法獲得具體值!
解決方法在下:
但是對于字段修改:?
如果是公共字段:
field.set(obj, 666);?
就可以完成修改
私有字段是無法修改的!
必須先取消限制:
field.setAccessible(true);
這樣才可以讓私有字段像公共字段一樣可以被查看,修改。
? ? ? ? ?
方法操作:
對于方法,有了前面的鋪墊則簡單得多:
對于公共方法:
Class ex = Temp.class;
Constructor constructor = ex.getConstructor();
Temp obj = (Temp) constructor.newInstance();Method greetMethod = ex.getMethod("sing");
greetMethod.invoke(obj);?這樣就成功執行了sing方法
如果想要獲取所有方法
Method[] methods =?ex.getMethods();即可
但是必須要有一個已經實例化的對象obj才能正常的通過invoke操作來執行方法!
實際上,執行的方法實際上是已經實例化的obj里面對應的方法!
對于一些函數,可能名字相同但是參數不同,可以仿照構造函數那樣加以區分:
Method greetMethod = ex.getMethod("sing",int.class);
Method greetMethod = ex.getMethod("sing",String.class);類似這樣來區分
對于私有方法:?
和字段一模一樣
在方法名上加上Declared
如getDeclaredMethod,getDeclaredMethods
然后得加上greetMethod.setAccessible(true);才能正常操作
獲取修飾符:
可以通過類似于:
int x = greetMethod.getModifiers();
來獲取任何方法/字段的修飾符,也許你會疑惑為什么是int類型,原因是JAVA中的確是用數字來存儲修飾符的,比如說:
public
=1,
?private
=2
,protected
=3,
default
=4
?獲取接口:
獲取接口相對來說非常簡單:
Class<?>[] interfaces =?ex.getInterfaces();
這樣便可獲取所有ex類所實現的接口
獲取注釋:
Annotation[] annotations = ex.getAnnotations();
這樣就獲取了ex類所有的注釋內容!
如果想獲取方法的注釋也非常簡單:
Annotation[] annotations = ex.getMethod("xxx").getAnnotations();
(提一句,對于每個注釋都是一個單獨的類型,假如你需要@CY注釋,改成
CY?annotation?= ex.getMethod("xxx").getAnnotation();也行)接下來想要提取注釋變量什么的就隨你了
這種方法可以用來實現公共字段補全
#2.總結?
? ? ? ? 歸功于反射機制,程序得以對本身進行修改而不依賴于人工修改,也提供了更多的靈活性,
更是為實現動態代理提供了可能。
(我也寫了一篇介紹代理的文章,歡迎捧場!)
你也許會發現,這種獲取參數,接口,方法是不是很熟悉?是不是我們用的IDE就有點用了這種方式的意思?不然為什么我們寫了一個函數就能提示要放什么參數進去?
這的確很像,但實際上IDE是會實時構造一個抽象語法樹(AST),它是代碼結構的表示。這使得 IDE 能夠理解類、方法、字段和參數的存在及其類型。
盡管如此,反射非常重要,java,spring框架里面反射無處不在,
但是,反射的性能開銷很大,因為它要在運行中實時解析類,方法,字段,這些原本是在編譯時完成的,而且反射無法被編譯器進行優化!
所以謹慎使用!