Java 高级特性 --- 反射
From:Java 高級特性 --- 反射:https://www.jianshu.com/p/9be58ee20dee
From:Java 基礎之 --- 反射(非常重要):https://blog.csdn.net/sinat_38259539/article/details/71799078
From:Java?高級?/?反射機制:https://how2j.cn/k/reflection/reflection-class/108.html
From:Java 中反射機制詳解:https://www.cnblogs.com/whgk/p/6122036.html
From:深入分析 Java 方法反射的實現(xiàn)原理:https://www.jianshu.com/p/3ea4a6b57f87
Java 反射 - 超詳細講解(附源碼):https://blog.csdn.net/lililuni/article/details/83449088? ??https://github.com/lililuni/reflect-demo
ReflectDemo:https://gitee.com/peter_RD_nj/DemoAllInOne/tree/master/ReflectDemo
Java中泛型?Class<T>、T?與?Class<?>、 Object類?和?Class類、?object.getClass()?和?Object.class:https://blog.csdn.net/freeking101/article/details/109247406
1. 概述
反射是框架設計的靈魂
使用前提條件:必須先得到代表的字節(jié)碼的Class,Class類?用于表示 .class文件?( 字節(jié)碼 )
注意:在運行期間,一個類,只有一個Class對象產(chǎn)生。
1.1 定義
JAVA反射機制(?https://baike.baidu.com/item/JAVA反射機制/6015990 )是:
?在運行狀態(tài)中,
- 對于任意一個類,都能夠獲取到這個類的所有屬性和方法;
- 對于任意一個對象 ( 即 類的實例 ),都能夠調(diào)用它的任意方法和屬性?(包括 私有的方法 和 屬性);
- 這種 動態(tài)獲取信息 以及 動態(tài)調(diào)用對象方法 的功能 稱為 java 語言的反射機制。
想要使用反射機制,必須先 獲取到該類的 字節(jié)碼文件對象(.class),而反射使用的就是 Class類 中的方法。
通過 字節(jié)碼文件對象 就能夠通過該類中的方法獲取到我們想要的所有信息 ( 方法,屬性,類名,父類名,實現(xiàn)的所有接口等等)
每一個類 對應著 一個字節(jié)碼文件?也就對應著 一個Class類型的對象,也就是 字節(jié)碼文件對象。
反射就是把 編寫的java類 中的各種成分映射成一個個的Java對象
例如:一個類有:成員變量、方法、構(gòu)造方法、包等等信息,利用反射技術可以對一個類進行解剖,把個個組成部分映射成一個個對象。
? ? ? ? ? ( 其實:一個類中的這些成員方法、構(gòu)造方法、在加入類中都有一個類來描述 )
如圖是類的正常加載過程:反射的原理在與class對象。
熟悉一下加載的時候:Class對象 的由來是將 class文件 讀入內(nèi)存,并為之創(chuàng)建一個 Class對象。
其中這個 Class對象 很特殊。我們先了解一下這個 Class類
二、查看 Class類 在 java 中的 api 詳解(1.7的API)
Java8 API 中文:Java 8 中文版 - 在線API中文手冊 - 碼工具? ??Java8 API 英文:Java Platform SE 8
如何閱讀 java 中的 api 詳見 java基礎之 --- String字符串處理
Class?類的實例表示正在運行的 Java 應用程序中的類和接口。也就是jvm中有N多的實例每個類都有該Class對象。(包括基本數(shù)據(jù)類型)
Class?沒有公共構(gòu)造方法。Class?對象是在加載類時由 Java 虛擬機以及通過調(diào)用類加載器中的defineClass?方法自動構(gòu)造的。也就是這不需要我們自己去處理創(chuàng)建,JVM已經(jīng)幫我們創(chuàng)建好了。
沒有公共的構(gòu)造方法,方法共有64個太多了。下面用到哪個就詳解哪個吧
1.2 反射的用途和使用
在日常的第三方應用開發(fā)過程中,經(jīng)常會遇到 某個類 的 某個成員變量、方法?或是 屬性?是私有的或是只對系統(tǒng)應用開放,這時候就可以利用 Java 的反射機制通過反射來獲取所需的私有成員或是方法。當然,也不是所有的都適合反射,之前就遇到一個案例,通過反射得到的結(jié)果與預期不符。閱讀源碼發(fā)現(xiàn),經(jīng)過層層調(diào)用后在最終返回結(jié)果的地方對應用的權(quán)限進行了校驗,對于沒有權(quán)限的應用返回值是沒有意義的缺省值,否則返回實際值,這樣就起到保護用戶隱私的目的。
假設我們現(xiàn)在有一個?Hero類
package pojo;public class Hero {public String name; //姓名public float hp; //血量public float armor; //護甲public int moveSpeed; //移動速度 }1、獲取類對象
獲取 類對象 ( 即?字節(jié)碼文件對象 ) 的 3種方式
- Class.forName()? ? ? ? ? ? ? ? // 常用。傳入 類名的字符串 即可得到 類對象
? ? ? ? 通過Class類的靜態(tài)方法: forName(String className)
? ? ? ? // 通過Class類中的靜態(tài)方法forName,直接獲取到一個類的字節(jié)碼文件對象,
? ? ? ? // 此時該類還是源文件階段,并沒有變?yōu)樽止?jié)碼文件。
? ? ? ? Class clazz1 = Class.forName("全限定類名"); - Hero.class? ? ? ? ? ? ? ? ? ? ? ? ?//?需要導入類的包,依賴太強,不導包就拋編譯錯誤
? ? ? ?(?任何數(shù)據(jù)類型(包括基本數(shù)據(jù)類型)都有一個 "靜態(tài)" 的 class屬性 )
? ? ? // 當類被加載成.class文件時,此時Person類變成了.class,
? ? ? // 在獲取該字節(jié)碼文件對象,也就是獲取自己, 該類處于字節(jié)碼階段。
? ? ? Class clazz2??= Person.class; - new Hero().getClass()? ? ? ?//?對象都有了還要反射干什么。。。
? ? ? ? (?Object??-->??getClass();? 因為 所有類 都繼承 Object類。從而調(diào)用 Object類 的 getClass方法 來獲取 )
? ? ? ?// 通過類的實例獲取該類的字節(jié)碼文件對象,該類處于創(chuàng)建對象階段
? ? ? ?Class clazz3 = p.getClass();
有了 字節(jié)碼文件對象 才能獲得類中所有的信息,上面最常用的是:?Class clazz1 = Class.forName("全限定類名");
在一個JVM中,一種類,只會有一個類對象存在。所以以上 3種方式取出來的類對象,都是一樣。(此處準確是在ClassLoader下,只有一個類對象)
package pojo;public class ObjectTest {public static void main(String[] args) {String className = "pogo.Hero";try {// 獲取類對象的第一種方式Class pClass1 = Class.forName(className);// 獲取類對象的第二種方式Class pClass2 = Hero.class;// 獲取類對象的第三種方式Class pClass3 = new Hero().getClass();System.out.println(pClass1 == pClass2); //輸出trueSystem.out.println(pClass1 == pClass3); //輸出true} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}} }注意:在運行期間,一個類,只有一個Class對象產(chǎn)生。
2、Class類 的 API 詳解
2.1、通過字節(jié)碼對象創(chuàng)建實例對象
2.2、獲取指定構(gòu)造器方法。constructor 如果沒有無參構(gòu)造,只有有參構(gòu)造如何創(chuàng)建實例呢?
總結(jié)上面創(chuàng)建實例對象:
Class類 的 newInstance() 方法是使用該類無參的構(gòu)造函數(shù)創(chuàng)建對象,如果一個類沒有無參的構(gòu)造函數(shù),?就不能這樣創(chuàng)建了。
可以調(diào)用Class類的 getConstructor(String.class, int.class) 方法獲取一個指定的構(gòu)造函數(shù),然后再調(diào)用 Constructor類的 newInstance("張三",20)方法創(chuàng)建對象
獲取全部構(gòu)造方法
2.3、獲取 成員變量 并使用 Field對象
獲取指定成員變量
Class.getField(String)方法可以獲取類中的指定字段(可見的),?如果是私有的可以用 getDeclaedField("name")方法獲取,通過 set(obj, "李四")方法可以設置指定對象上該字段的值,?如果是私有的需要先調(diào)用 setAccessible(true) 設置訪問權(quán)限,用獲取的指定的字段調(diào)用 get(obj) 可以獲取指定對象中該字段的值
獲取全部成員變量
2.4、獲得方法并使用 Method
Class.getMethod(String, Class...) 和 Class.getDeclaredMethod(String, Class...)方法可以獲取類中的指定方法,
如果為私有方法,則需要使用?setAccessible(true); 打開一個權(quán)限。用 invoke(Object, Object...) 可以調(diào)用該方法,跟上面同理,也能一次性獲得所有的方法
2.5、獲得該類的所有接口
? ? ? ? Class[] getInterfaces():確定此對象所表示的類或接口實現(xiàn)的接口
? ? ? ? 返回值:接口的字節(jié)碼文件對象的數(shù)組
2.6、獲取指定資源的輸入流
? ? ? ? InputStream?getResourceAsStream(String name)
? ? ? ? return:一個 InputStream 對象;如果找不到帶有該名稱的資源,則返回?null
? ? ? ? 參數(shù):所需資源的名稱,如果以"/"開始,則絕對資源名為"/"后面的一部分。
2.7、動態(tài)代理的概述和實現(xiàn)
動態(tài)代理:一種設計模式,其非常簡單,很容易理解,你自己可以做這件事,但是覺得自己做非常麻煩或者不方便,所以就叫另一個人(代理)來幫你做這個事情,而你就不用管了,這就是動態(tài)代理。舉個例子,買火車票叫人代買。?
程序運行過程中產(chǎn)生的這個對象,其實就是我們剛才反射講解的內(nèi)容,所以,動態(tài)代理其實就是通過反射來生成一個代理
在Java中 java.lang.reflect 包下提供了一個 Proxy類 和 一個InvocationHandler接口,通過使用 這個類 和 接口 就可以生成動態(tài)代理對象。
JDK提供的代理只能針對接口做代理。我們有更強大的代理 cglib,Proxy類中的方法創(chuàng)建動態(tài)代理類對象分三步,但是注意JDK提供的代理只能針對接口做代理,也就是下面的第二步返回的必須要是一個接口。
- 1、new出代理對象,通過實現(xiàn)InvacationHandler接口,然后new出代理對象來。
- 2、通過Proxy類中的靜態(tài)方法newProxyInstance,來將代理對象假裝成那個被代理的對象,也就是如果叫人幫我們代買火車票一樣,那個代理就假裝成我們自己本人
- 3、執(zhí)行方法,代理成功
將代理對象中的內(nèi)容進行實現(xiàn)
1、2、3步
注意 newProxyInstance 的三個參數(shù),第一個,類加載器,第二個被代理對象的接口,第三個代理對象。
2.8、還有很多方法,比如獲得類加載器,等等,具體還需要別的,就通過查看API文檔來解決。
3、反射機制的應用實例
應用實例 1:
3.1、利用反射,在泛型為 int 的 arryaList 集合中存放一個 String 類型的對象
原理:集合中的泛型只在編譯器有效,而到了運行期,泛型則會失效,
3.2、利用反射,簡化編寫Servlet的個數(shù)。
什么意思呢?每當我們寫一個功能時,就需要寫一個對應的Servlet,導致最后Servlet有很多,自己都看不過來,所以對其進行了優(yōu)化,兩種方式,
3.2.1、每次從頁面?zhèn)鬟^來一個參數(shù),method="xxx"; 然后編寫一個Servlet,獲得其參數(shù)method的值,進行判斷,如果是add,則調(diào)用add方法,如果是delete,則調(diào)用delete方法,這樣就可以寫在一個servlet中實現(xiàn)所有的功能了。
3.2.2、利用反射
編寫一個BaseServlet 繼承 HttpServlet,這是一個通用的 BaseServlet。需要明白 servlet 的生命周期
編寫具體實現(xiàn)的方法 servlet 類。
MySerlvet001 extends BaseServlet
解釋:需要明白servlet的生命周期,也就是service方法,因為是servlet,所以在訪問的時候,會經(jīng)過service方法,而子類MyServlet001中并沒有,所以就到父類BaseServlet中找,發(fā)現(xiàn)有,然后獲取參數(shù)即知道了需要調(diào)用什么方法,因為方法的編寫都在子類中,所以通過反射,獲取到子類中對應的方法并運行,其中需要注意的是this這個參數(shù)在BaseServlet中的用法。需要理解它。才能理解我們這個程序。
對其一些api進行講解,不懂的就查看API,重要的思想,就要在實際中遇到了才能得到更好的理解。
應用實例 2:
反射非常強大,但是學習了之后,會不知道該如何使用,反而覺得還不如直接調(diào)用方法來的直接和方便。
通常來說,需要在學習了?Spring?的依賴注入,反轉(zhuǎn)控制之后,才會對反射有更好的理解,但是剛學到這里的同學,不一定接觸了Spring,所以在這里舉兩個例子,來演示一下反射的一種實際運用。
步驟?1?:?業(yè)務類:https://how2j.cn/k/reflection/reflection-usage/1111.html#step4262
步驟?2?:?非反射方式:https://how2j.cn/k/reflection/reflection-usage/1111.html#step4263
步驟?3?:?反射方式:https://how2j.cn/k/reflection/reflection-usage/1111.html#step4264
- 步驟 1 : 業(yè)務類 ??
首先準備兩個業(yè)務類,這兩個業(yè)務類很簡單,就是各自都有一個業(yè)務方法,分別打印不同的字符串
Service1.java
package reflection;public class Service1 {public void doService2(){System.out.println("111111111111 業(yè)務方法1");} }Service2.java
package reflection;public class Service2 {public void doService2(){System.out.println("222222222222 業(yè)務方法2");} }- 步驟 2 : 非反射方式 ??
當需要從第一個業(yè)務方法切換到第二個業(yè)務方法的時候,使用非反射方式,必須修改代碼,并且重新編譯運行,才可以達到效果
package reflection;public class Test {public static void main(String[] args) {new Service1().doService1();} }修改后代碼:
package reflection;public class Test {public static void main(String[] args) {// new Service1().doService1();new Service1().doService2(); // 這里修改為調(diào)用 doService2} }- 步驟 3 : 反射方式 ??
使用反射方式,首先準備一個配置文件,就叫做 spring.txt 吧,放在 src 目錄下。 里面存放的是類的名稱,和要調(diào)用的方法名。
spring.txt
class=reflection.Service1 method=doService1在測試類Test中,首先取出類名稱和方法名,然后通過反射去調(diào)用這個方法。
package reflection;import java.io.File; import java.io.FileInputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.Properties;public class Test {@SuppressWarnings({"rawtypes", "unchecked"})public static void main(String[] args) throws Exception {//從spring.txt中獲取類名稱和方法名稱File springConfigFile = new File("e:\\project\\j2se\\src\\spring.txt");Properties springConfig = new Properties();springConfig.load(new FileInputStream(springConfigFile));String className = (String) springConfig.get("class");String methodName = (String) springConfig.get("method");//根據(jù)類名稱獲取類對象Class clazz = Class.forName(className);//根據(jù)方法名稱,獲取方法對象Method m = clazz.getMethod(methodName);//獲取構(gòu)造器Constructor c = clazz.getConstructor();//根據(jù)構(gòu)造器,實例化出對象Object service = c.newInstance();//調(diào)用對象的指定方法m.invoke(service);} }當需要從調(diào)用第一個業(yè)務方法,切換到調(diào)用第二個業(yè)務方法的時候,不需要修改一行代碼,也不需要重新編譯,只需要修改配置文件 spring.txt,再運行即可。
這也是?Spring框架?的最基本的原理,只是它做的更豐富,安全,健壯。
1.3 反射機制的相關類
與 Java 反射相關的類如下:
| Class 類 | 代表 類的實體,在運行的 Java 應用程序中表示 類和接口 |
| Field 類 | 代表 類的成員變量(成員變量?也稱為 類的屬性) |
| Method 類 | 代表 類的方法 |
| Constructor 類 | 代表 類的構(gòu)造方法 |
1.4 利用反射機制 創(chuàng)建對象
基本步驟
? ? ? ? 與傳統(tǒng)的通過 new 來獲取對象的方式不同反射機制,反射會先拿到 Hero 的 "類對象",然后通過類對象獲取 "構(gòu)造器對象" ,再通過構(gòu)造器對象創(chuàng)建一個對象,具體步驟:
1. 獲取類對象? ? ? ? ?Class class = Class.forName("pojo.Hero");
2. 獲取構(gòu)造器對象??Constructor con = clazz.getConstructor(形參.class);
3. 獲取對象? ? ? ? ? ? ?Hero hero =con.newInstance(實參);
上面是最簡單的獲取方法,當 Hero 的 構(gòu)造方法 不是無參構(gòu)造方法時,獲取構(gòu)造器對象略有不同,見下面測試:
構(gòu)造方法不同時,獲取構(gòu)造器對象的方法
示例:
- Hero 類 添加 6 種構(gòu)造方法
- 通過反射機制獲取對象
輸出:
**********************所有公有構(gòu)造方法********************************* public pojo.Hero(java.lang.String,float) public pojo.Hero(char) public pojo.Hero() ************所有的構(gòu)造方法(包括:私有、受保護、默認、公有)*************** private pojo.Hero(float) protected pojo.Hero(boolean) public pojo.Hero(java.lang.String,float) public pojo.Hero(char) public pojo.Hero() pojo.Hero(java.lang.String) *****************獲取公有、無參的構(gòu)造方法******************************* con = public pojo.Hero() 調(diào)用了公有、無參構(gòu)造方法執(zhí)行了。。。 ******************獲取私有構(gòu)造方法,并調(diào)用******************************* private pojo.Hero(float) 私有的構(gòu)造方法 血量:100.0總結(jié):
1. 獲取 構(gòu)造器對象方法:
? ? 1). 批量的方法:
? ? ? ? ? ? ? ? public Constructor[] getConstructors():? ? ? ? ? ? ? ?所有 "公有的" 構(gòu)造方法
? ? ? ? ? ? ? ? public Constructor[] getDeclaredConstructors(): 獲取所有的構(gòu)造方法 ( 包括 私有、受保護、默認、公有)
? ? 2). 獲取單個的方法:
? ? ? ? ? ? ? ? public Constructor getConstructor(Class… parameterTypes):? ? ? ? ? ? ? ?獲取單個的 "公有的" 構(gòu)造方法
? ? ? ? ? ? ? ? public Constructor getDeclaredConstructor(Class…parameterTypes):? 獲取 "某個構(gòu)造方法" 可以是 私有的,或受保護、默認、公有;
2、 調(diào)用構(gòu)造方法:Constructor --> newInstance(Object... initargs)
newInstance 是 Constructor類的方法(管理構(gòu)造函數(shù)的類)
api 的解釋為:
? ? ? ? newInstance(Object... initargs)
? ? ? ? ? ? 使用此 Constructor 對象表示的構(gòu)造方法來創(chuàng)建該構(gòu)造方法的聲明類的新實例,并用指定的初始化參數(shù)初始化該實例。
? ? ? ? ? ? 它的返回值是T類型,所以newInstance是創(chuàng)建了一個構(gòu)造方法的聲明類的新實例對象。并為之調(diào)用
1.5?獲取成員變量并使用
基本步驟
1. 獲取 HeroPlus類 的對象?new方法/第2章中的方法?h
2. 獲取屬性?Field f1 = h.getDeclaredField("屬性名")
3. 修改屬性?f1.set(h,實參),注意這里的 h 是 對象,不是 類對象
示例:
- 新增 HeroPlus 類
- 獲取屬性并修改
補充:getField 和 getDeclaredField 的區(qū)別:
- getField?只能獲取public?的,包括從父類繼承來的字段。
- getDeclaredField 可以獲取本類所有的字段,包括 private 的,但是?不能獲取繼承來的字段。 ( 注: 這里只能獲取到private的字段,但并不能訪問該private字段的值,除非加上setAccessible(true))
示例 2:
student類:
package fanshe.field;public class Student {public Student() {}//**********字段*************//public String name;protected int age;char sex;private String phoneNum;@Overridepublic String toString() {return "Student [name=" + name + ", age=" + age + ", sex=" + sex+ ", phoneNum=" + phoneNum + "]";} }測試類:
package fanshe.field;import java.lang.reflect.Field;/** 獲取成員變量并調(diào)用:** 1.批量的* 1).Field[] getFields():獲取所有的"公有字段"* 2).Field[] getDeclaredFields():獲取所有字段,包括:私有、受保護、默認、公有;* 2.獲取單個的:* 1).public Field getField(String fieldName):獲取某個"公有的"字段;* 2).public Field getDeclaredField(String fieldName):獲取某個字段(可以是私有的)** 設置字段的值:* Field --> public void set(Object obj,Object value):* 參數(shù)說明:* 1.obj:要設置的字段所在的對象;* 2.value:要為字段設置的值;**/ public class Fields {public static void main(String[] args) throws Exception {//1.獲取Class對象Class stuClass = Class.forName("fanshe.field.Student");//2.獲取字段System.out.println("************獲取所有公有的字段********************");Field[] fieldArray = stuClass.getFields();for (Field f : fieldArray) {System.out.println(f);}System.out.println("************獲取所有的字段(包括私有、受保護、默認的)********************");fieldArray = stuClass.getDeclaredFields();for (Field f : fieldArray) {System.out.println(f);}System.out.println("*************獲取公有字段**并調(diào)用***********************************");Field f = stuClass.getField("name");System.out.println(f);//獲取一個對象Object obj = stuClass.getConstructor().newInstance();//產(chǎn)生Student對象--》Student stu = new Student();//為字段設置值f.set(obj, "劉德華");//為Student對象中的name屬性賦值--》stu.name = "劉德華"//驗證Student stu = (Student) obj;System.out.println("驗證姓名:" + stu.name);System.out.println("**************獲取私有字段****并調(diào)用********************************");f = stuClass.getDeclaredField("phoneNum");System.out.println(f);f.setAccessible(true);//暴力反射,解除私有限定f.set(obj, "18888889999");System.out.println("驗證電話:" + stu);} }后臺輸出:
************獲取所有公有的字段******************** public java.lang.String fanshe.field.Student.name ************獲取所有的字段(包括私有、受保護、默認的)******************** public java.lang.String fanshe.field.Student.name protected int fanshe.field.Student.age char fanshe.field.Student.sex private java.lang.String fanshe.field.Student.phoneNum *************獲取公有字段**并調(diào)用*********************************** public java.lang.String fanshe.field.Student.name 驗證姓名:劉德華 **************獲取私有字段****并調(diào)用******************************** private java.lang.String fanshe.field.Student.phoneNum 驗證電話:Student [name=劉德華, age=0, sex=由此可見
調(diào)用字段時:需要傳遞兩個參數(shù):
- 第一個參數(shù):要傳入設置的對象
- 第二個參數(shù):要傳入實參
1.6?獲取成員方法并使用
public Method getMethod(String name ,Class<?>… parameterTypes):獲取"公有方法";(包含了父類的方法也包含Object類)
public Method getDeclaredMethods(String name ,Class<?>… parameterTypes) :獲取成員方法,包括私有的(不包括繼承的)
參數(shù)解釋:
??name : 方法名;
??Class … : 形參的Class類型對象
Method --> public Object invoke(Object obj,Object… args):
參數(shù)說明:
??obj : 要調(diào)用方法的對象;
??args:調(diào)用方式時所傳遞的實參;
示例:
package test;public class MethodTest {public static void main(String[] args) {HeroPlus h = new HeroPlus();try {// 獲取這個名字叫做setName,參數(shù)類型是String的方法Method m = h.getClass().getMethod("setName", String.class);// 對h對象,調(diào)用這個方法m.invoke(h, "蓋倫");// 使用傳統(tǒng)的方式,調(diào)用getName方法System.out.println(h.getName());} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}} }示例 2:
student類:
package fanshe.method;public class Student {//**************成員方法***************//public void show1(String s) {System.out.println("調(diào)用了:公有的,String參數(shù)的show1(): s = " + s);}protected void show2() {System.out.println("調(diào)用了:受保護的,無參的show2()");}void show3() {System.out.println("調(diào)用了:默認的,無參的show3()");}private String show4(int age) {System.out.println("調(diào)用了,私有的,并且有返回值的,int參數(shù)的show4(): age = " + age);return "abcd";} }測試類:
package fanshe.method;import java.lang.reflect.Method;/** 獲取成員方法并調(diào)用:* * 1.批量的:* public Method[] getMethods():獲取所有"公有方法";(包含了父類的方法也包含Object類)* public Method[] getDeclaredMethods():獲取所有的成員方法,包括私有的(不包括繼承的)* 2.獲取單個的:* public Method getMethod(String name,Class<?>... parameterTypes):* 參數(shù):* name : 方法名;* Class ... : 形參的Class類型對象* public Method getDeclaredMethod(String name,Class<?>... parameterTypes)* * 調(diào)用方法:* Method --> public Object invoke(Object obj,Object... args):* 參數(shù)說明:* obj : 要調(diào)用方法的對象;* args:調(diào)用方式時所傳遞的實參; ):*/ public class MethodClass {public static void main(String[] args) throws Exception {//1.獲取Class對象Class stuClass = Class.forName("fanshe.method.Student");//2.獲取所有公有方法System.out.println("***************獲取所有的”公有“方法*******************");stuClass.getMethods();Method[] methodArray = stuClass.getMethods();for (Method m : methodArray) {System.out.println(m);}System.out.println("***************獲取所有的方法,包括私有的*******************");methodArray = stuClass.getDeclaredMethods();for (Method m : methodArray) {System.out.println(m);}System.out.println("***************獲取公有的show1()方法*******************");Method m = stuClass.getMethod("show1", String.class);System.out.println(m);//實例化一個Student對象Object obj = stuClass.getConstructor().newInstance();m.invoke(obj, "劉德華");System.out.println("***************獲取私有的show4()方法******************");m = stuClass.getDeclaredMethod("show4", int.class);System.out.println(m);m.setAccessible(true);//解除私有限定Object result = m.invoke(obj, 20);//需要兩個參數(shù),一個是要調(diào)用的對象(獲取有反射),一個是實參System.out.println("返回值:" + result);} }由此可見:
// 調(diào)用制定方法(所有包括私有的),需要傳入兩個參數(shù), // 第一個是調(diào)用的方法名稱, // 第二個是方法的形參類型,切記是類型。 m = stuClass.getDeclaredMethod("show4", int.class); System.out.println(m);// 解除私有限定 m.setAccessible(true); // 需要兩個參數(shù),一個是要調(diào)用的對象(獲取有反射),一個是實參 Object result = m.invoke(obj, 20); System.out.println("返回值:" + result);//控制臺輸出:
***************獲取所有的”公有“方法******************* public void fanshe.method.Student.show1(java.lang.String) 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 final void java.lang.Object.wait() throws java.lang.InterruptedException public boolean java.lang.Object.equals(java.lang.Object) public java.lang.String java.lang.Object.toString() 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 void fanshe.method.Student.show1(java.lang.String) private java.lang.String fanshe.method.Student.show4(int) protected void fanshe.method.Student.show2() void fanshe.method.Student.show3() ***************獲取公有的show1()方法******************* public void fanshe.method.Student.show1(java.lang.String) 調(diào)用了:公有的,String參數(shù)的show1(): s = 劉德華 ***************獲取私有的show4()方法****************** private java.lang.String fanshe.method.Student.show4(int) 調(diào)用了,私有的,并且有返回值的,int參數(shù)的show4(): age = 20 返回值:abcd其實這里的成員方法:在模型中有屬性一詞,就是那些 setter()方法 和 getter()方法。還有字段組成,這些內(nèi)容在 內(nèi)省 中詳解
1.7?獲取 main 方法并使用
示例:
- HeroPlus 新增main方法
示例 2:
student類:
package fanshe.main;public class Student {public static void main(String[] args) {System.out.println("main方法執(zhí)行了。。。");} }測試類:
package fanshe.main;import java.lang.reflect.Method;/*** 獲取Student類的main方法、不要與當前的main方法搞混了*/ public class Main {public static void main(String[] args) {try {//1、獲取Student對象的字節(jié)碼Class clazz = Class.forName("fanshe.main.Student");//2、獲取main方法。 // 第一個參數(shù):方法名稱,第二個參數(shù):方法形參的類型,Method methodMain = clazz.getMethod("main", String[].class);//3、調(diào)用main方法// methodMain.invoke(null, new String[]{"a","b","c"});// 第一個參數(shù),對象類型,因為方法是static靜態(tài)的,所以為null可以,// 第二個參數(shù)是String數(shù)組,這里要注意在jdk1.4時是數(shù)組,jdk1.5之后是可變參數(shù)// 這里拆的時候?qū)? new String[]{"a","b","c"} 拆成3個對象。。。所以需要將它強轉(zhuǎn)。methodMain.invoke(null, (Object) new String[]{"a", "b", "c"});//方式一// methodMain.invoke(null, new Object[]{new String[]{"a","b","c"}});//方式二} catch (Exception e) {e.printStackTrace();}} }控制臺輸出:main方法執(zhí)行了。。。
深入分析 Java 方法反射的實現(xiàn)原理
From:https://www.jianshu.com/p/3ea4a6b57f87
從一起GC血案談到反射原理 (?https://mp.weixin.qq.com/s/5H6UHcP6kvR2X5hTj_SBjA )?,就把 Java 方法的反射機制實現(xiàn)擼了一遍。
方法反射實例
public class ReflectCase {public static void main(String[] args) throws Exception {Proxy target = new Proxy();Method method = Proxy.class.getDeclaredMethod("run");method.invoke(target);}static class Proxy {public void run() {System.out.println("run");}} }通過 Java 的反射機制,可以在運行期間調(diào)用對象的任何方法,如果大量使用這種方式進行調(diào)用,會有性能或內(nèi)存隱患么?為了徹底了解方法的反射機制,只能從底層代碼入手了。
Method 獲取
調(diào)用 Class 類的 getDeclaredMethod 可以獲取 指定方法名和參數(shù) 的 方法對象 Method。
getDeclaredMethod
其中?privateGetDeclaredMethods?方法從 緩存 或 JVM中 獲取該Class中申明的方法列表,searchMethods方法將從返回的方法列表里找到一個匹配名稱和參數(shù)的方法對象。
searchMethods
如果找到一個匹配的?Method,則重新 copy 一份返回,即?Method.copy()方法
所次每次調(diào)用getDeclaredMethod方法返回的Method對象其實都是一個新的對象,且新對象的root屬性都指向原來的Method對象,如果需要頻繁調(diào)用,最好把Method對象緩存起來。
privateGetDeclaredMethods
從緩存或 JVM 中獲取該?Class?中申明的方法列表,實現(xiàn)如下:
其中?reflectionData()方法實現(xiàn)如下:
這里有個比較重要的數(shù)據(jù)結(jié)構(gòu)?ReflectionData,用來緩存從JVM中讀取類的如下屬性數(shù)據(jù):
從reflectionData()方法實現(xiàn)可以看出:reflectionData對象是SoftReference類型的,說明在內(nèi)存緊張時可能會被回收,不過也可以通過-XX:SoftRefLRUPolicyMSPerMB參數(shù)控制回收的時機,只要發(fā)生GC就會將其回收,如果reflectionData被回收之后,又執(zhí)行了反射方法,那只能通過newReflectionData方法重新創(chuàng)建一個這樣的對象了,newReflectionData方法實現(xiàn)如下:
通過unsafe.compareAndSwapObject方法重新設置reflectionData字段;
在privateGetDeclaredMethods方法中,如果通過reflectionData()獲得的ReflectionData對象不為空,則嘗試從ReflectionData對象中獲取declaredMethods屬性,如果是第一次,或則被GC回收之后,重新初始化后的類屬性為空,則需要重新到JVM中獲取一次,并賦值給ReflectionData,下次調(diào)用就可以使用緩存數(shù)據(jù)了。
Method 調(diào)用
獲取到指定的方法對象Method之后,就可以調(diào)用它的invoke方法了,invoke實現(xiàn)如下:
應該注意到:這里的MethodAccessor對象是invoke方法實現(xiàn)的關鍵,一開始methodAccessor為空,需要調(diào)用acquireMethodAccessor生成一個新的MethodAccessor對象,MethodAccessor本身就是一個接口,實現(xiàn)如下:
在acquireMethodAccessor方法中,會通過ReflectionFactory類的newMethodAccessor創(chuàng)建一個實現(xiàn)了MethodAccessor接口的對象,實現(xiàn)如下:
在ReflectionFactory類中,有2個重要的字段:noInflation(默認false)和inflationThreshold(默認15),在checkInitted方法中可以通過-Dsun.reflect.inflationThreshold=xxx和-Dsun.reflect.noInflation=true對這兩個字段重新設置,而且只會設置一次;
如果noInflation為false,方法newMethodAccessor都會返回DelegatingMethodAccessorImpl對象,DelegatingMethodAccessorImpl的類實現(xiàn)
其實,DelegatingMethodAccessorImpl對象就是一個代理對象,負責調(diào)用被代理對象delegate的invoke方法,其中delegate參數(shù)目前是NativeMethodAccessorImpl對象,所以最終Method的invoke方法調(diào)用的是NativeMethodAccessorImpl對象invoke方法,實現(xiàn)如下:
這里用到了ReflectionFactory類中的inflationThreshold,當delegate調(diào)用了15次invoke方法之后,如果繼續(xù)調(diào)用就通過MethodAccessorGenerator類的generateMethod方法生成MethodAccessorImpl對象,并設置為delegate對象,這樣下次執(zhí)行Method.invoke時,就調(diào)用新建的MethodAccessor對象的invoke()方法了。
這里需要注意的是:
generateMethod方法在生成MethodAccessorImpl對象時,會在內(nèi)存中生成對應的字節(jié)碼,并調(diào)用ClassDefiner.defineClass創(chuàng)建對應的class對象,實現(xiàn)如下:
在ClassDefiner.defineClass方法實現(xiàn)中,每被調(diào)用一次都會生成一個DelegatingClassLoader類加載器對象
這里每次都生成新的類加載器,是為了性能考慮,在某些情況下可以卸載這些生成的類,因為類的卸載是只有在類加載器可以被回收的情況下才會被回收的,如果用了原來的類加載器,那可能導致這些新創(chuàng)建的類一直無法被卸載,從其設計來看本身就不希望這些類一直存在內(nèi)存里的,在需要的時候有就行了。
Class類
Class?代表類的實體,在運行的 Java 應用程序中表示 類 和 接口。在這個類中提供了很多有用的方法,這里只簡單的分類介紹下。
獲取類相關的方法
| asSubclass(Class<U> clazz) | 把傳遞的類的對象轉(zhuǎn)換成代表其子類的對象 |
| Cast | 把對象轉(zhuǎn)換成代表類或是接口的對象 |
| getClassLoader() | 獲得類的加載器 |
| getClasses() | 返回一個數(shù)組,數(shù)組中包含該類中所有公共類和接口類的對象 |
| getDeclaredClasses() | 返回一個數(shù)組,數(shù)組中包含該類中所有類和接口類的對象 |
| forName(String className) | 根據(jù)類名返回類的對象 |
| getName() | 獲得類的完整路徑名字 |
| newInstance() | 創(chuàng)建類的實例 |
| getPackage() | 獲得類的包 |
| getSimpleName() | 獲得類的名字 |
| getSuperclass() | 獲得當前類繼承的父類的名字 |
| getInterfaces() | 獲得當前類實現(xiàn)的類或是接口 |
獲取?類中屬性相關的方法
| getField(String name) | 獲得某個公有的屬性對象 |
| getFields() | 獲得所有公有的屬性對象 |
| getDeclaredField(String name) | 獲得某個屬性對象 |
| getDeclaredFields() | 獲得所有屬性對象 |
獲取?類中注解相關的方法
| getAnnotation(Class<A> annotationClass) | 返回該類中與參數(shù)類型匹配的公有注解對象 |
| getAnnotations() | 返回該類所有的公有注解對象 |
| getDeclaredAnnotation(Class<A> annotationClass) | 返回該類中與參數(shù)類型匹配的所有注解對象 |
| getDeclaredAnnotations() | 返回該類所有的注解對象 |
獲得?類中構(gòu)造器相關的方法
| getConstructor(Class...<?> parameterTypes) | 獲得該類中與參數(shù)類型匹配的公有構(gòu)造方法 |
| getConstructors() | 獲得該類的所有公有構(gòu)造方法 |
| getDeclaredConstructor(Class...<?> parameterTypes) | 獲得該類中與參數(shù)類型匹配的構(gòu)造方法 |
| getDeclaredConstructors() | 獲得該類所有構(gòu)造方法 |
獲取?類中方法相關的方法
| getMethod(String name, Class...<?> parameterTypes) | 獲得該類某個公有的方法 |
| getMethods() | 獲得該類所有公有的方法 |
| getDeclaredMethod(String name, Class...<?> parameterTypes) | 獲得該類某個方法 |
| getDeclaredMethods() | 獲得該類所有方法 |
類中 其他重要的方法
| isAnnotation() | 如果是注解類型則返回 true |
| isAnnotationPresent(Class<? extends Annotation> annotationClass) | 如果是指定類型注解類型則返回 true |
| isAnonymousClass() | 如果是匿名類則返回 true |
| isArray() | 如果是一個數(shù)組類則返回 true |
| isEnum() | 如果是枚舉類則返回 true |
| isInstance(Object obj) | 如果obj是該類的實例則返回 true |
| isInterface() | 如果是接口類則返回 true |
| isLocalClass() | 如果是局部類則返回 true |
| isMemberClass() | 如果是內(nèi)部類則返回 true |
Field 類
Field?代表 類的成員變量(成員變量也稱為類的屬性)。
| equals(Object obj) | 屬性與 obj 相等則返回 true |
| get(Object obj) | 獲得 obj 中對應的屬性值 |
| set(Object obj, Object value) | 設置 obj 中對應屬性值 |
Method 類
Method?代表 類的方法 。
| invoke(Object obj, Object... args) | 傳遞 object 對象及參數(shù)調(diào)用該對象對應的方法 |
Constructor類
Constructor?代表?類的構(gòu)造方法。
| newInstance(Object... initargs) | 根據(jù)傳遞的參數(shù)創(chuàng)建類的對象 |
示例 1:演示反射的使用
為了演示反射的使用,首先構(gòu)造一個與書籍相關的 model ---?Book.java,然后通過反射方法示例創(chuàng)建對象、反射私有構(gòu)造方法、反射私有屬性、反射私有方法,最后給出兩個比較復雜的反射示例 ---?獲得當前 ZenMode 和 關機Shutdown。
被反射類 Book.java
public class Book{private final static String TAG = "BookTag";private String name;private String author;@Overridepublic String toString() {return "Book{" +"name='" + name + '\'' +", author='" + author + '\'' +'}';}public Book() {}private Book(String name, String author) {this.name = name;this.author = author;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAuthor() {return author;}public void setAuthor(String author) {this.author = author;}private String declaredMethod(int index) {String string = null;switch (index) {case 0:string = "I am declaredMethod 1 !";break;case 1:string = "I am declaredMethod 2 !";break;default:string = "I am declaredMethod 1 !";}return string;} }反射邏輯封裝在 ReflectClass.java
public class ReflectClass {private final static String TAG = "peter.log.ReflectClass";// 創(chuàng)建對象public static void reflectNewInstance() {try {Class<?> classBook = Class.forName("com.android.peter.reflectdemo.Book");Object objectBook = classBook.newInstance();Book book = (Book) objectBook;book.setName("Android進階之光");book.setAuthor("劉望舒");Log.d(TAG,"reflectNewInstance book = " + book.toString());} catch (Exception ex) {ex.printStackTrace();}}// 反射私有的構(gòu)造方法public static void reflectPrivateConstructor() {try {Class<?> classBook = Class.forName("com.android.peter.reflectdemo.Book");Constructor<?> declaredConstructorBook = classBook.getDeclaredConstructor(String.class,String.class);declaredConstructorBook.setAccessible(true);Object objectBook = declaredConstructorBook.newInstance("Android開發(fā)藝術探索","任玉剛");Book book = (Book) objectBook;Log.d(TAG,"reflectPrivateConstructor book = " + book.toString());} catch (Exception ex) {ex.printStackTrace();}}// 反射私有屬性public static void reflectPrivateField() {try {Class<?> classBook = Class.forName("com.android.peter.reflectdemo.Book");Object objectBook = classBook.newInstance();Field fieldTag = classBook.getDeclaredField("TAG");fieldTag.setAccessible(true);String tag = (String) fieldTag.get(objectBook);Log.d(TAG,"reflectPrivateField tag = " + tag);} catch (Exception ex) {ex.printStackTrace();}}// 反射私有方法public static void reflectPrivateMethod() {try {Class<?> classBook = Class.forName("com.android.peter.reflectdemo.Book");Method methodBook = classBook.getDeclaredMethod("declaredMethod",int.class);methodBook.setAccessible(true);Object objectBook = classBook.newInstance();String string = (String) methodBook.invoke(objectBook,0);Log.d(TAG,"reflectPrivateMethod string = " + string);} catch (Exception ex) {ex.printStackTrace();}}// 獲得系統(tǒng)Zenmode值public static int getZenMode() {int zenMode = -1;try {Class<?> cServiceManager = Class.forName("android.os.ServiceManager");Method mGetService = cServiceManager.getMethod("getService", String.class);Object oNotificationManagerService = mGetService.invoke(null, Context.NOTIFICATION_SERVICE);Class<?> cINotificationManagerStub = Class.forName("android.app.INotificationManager$Stub");Method mAsInterface = cINotificationManagerStub.getMethod("asInterface",IBinder.class);Object oINotificationManager = mAsInterface.invoke(null,oNotificationManagerService);Method mGetZenMode = cINotificationManagerStub.getMethod("getZenMode");zenMode = (int) mGetZenMode.invoke(oINotificationManager);} catch (Exception ex) {ex.printStackTrace();}return zenMode;}// 關閉手機public static void shutDown() {try {Class<?> cServiceManager = Class.forName("android.os.ServiceManager");Method mGetService = cServiceManager.getMethod("getService",String.class);Object oPowerManagerService = mGetService.invoke(null,Context.POWER_SERVICE);Class<?> cIPowerManagerStub = Class.forName("android.os.IPowerManager$Stub");Method mShutdown = cIPowerManagerStub.getMethod("shutdown",boolean.class,String.class,boolean.class);Method mAsInterface = cIPowerManagerStub.getMethod("asInterface",IBinder.class);Object oIPowerManager = mAsInterface.invoke(null,oPowerManagerService);mShutdown.invoke(oIPowerManager,true,null,true);} catch (Exception ex) {ex.printStackTrace();}}public static void shutdownOrReboot(final boolean shutdown, final boolean confirm) {try {Class<?> ServiceManager = Class.forName("android.os.ServiceManager");// 獲得ServiceManager的getService方法Method getService = ServiceManager.getMethod("getService", java.lang.String.class);// 調(diào)用getService獲取RemoteServiceObject oRemoteService = getService.invoke(null, Context.POWER_SERVICE);// 獲得IPowerManager.Stub類Class<?> cStub = Class.forName("android.os.IPowerManager$Stub");// 獲得asInterface方法Method asInterface = cStub.getMethod("asInterface", android.os.IBinder.class);// 調(diào)用asInterface方法獲取IPowerManager對象Object oIPowerManager = asInterface.invoke(null, oRemoteService);if (shutdown) {// 獲得shutdown()方法Method shutdownMethod = oIPowerManager.getClass().getMethod("shutdown", boolean.class, String.class, boolean.class);// 調(diào)用shutdown()方法shutdownMethod.invoke(oIPowerManager, confirm, null, false);} else {// 獲得reboot()方法Method rebootMethod = oIPowerManager.getClass().getMethod("reboot",boolean.class, String.class, boolean.class);// 調(diào)用reboot()方法rebootMethod.invoke(oIPowerManager, confirm, null, false);}} catch (Exception e) {e.printStackTrace();}} }調(diào)用相應反射邏輯方法
try {// 創(chuàng)建對象ReflectClass.reflectNewInstance();// 反射私有的構(gòu)造方法ReflectClass.reflectPrivateConstructor();// 反射私有屬性ReflectClass.reflectPrivateField();// 反射私有方法ReflectClass.reflectPrivateMethod();} catch (Exception ex) {ex.printStackTrace();}Log.d(TAG," zenmode = " + ReflectClass.getZenMode());Log 輸出結(jié)果如下:
08-27 15:11:37.999 11987-11987/com.android.peter.reflectdemo D/peter.log.ReflectClass: reflectNewInstance book = Book{name='Android進階之光', author='劉望舒'} 08-27 15:11:38.000 11987-11987/com.android.peter.reflectdemo D/peter.log.ReflectClass: reflectPrivateConstructor book = Book{name='Android開發(fā)藝術探索', author='任玉剛'} 08-27 15:11:38.000 11987-11987/com.android.peter.reflectdemo D/peter.log.ReflectClass: reflectPrivateField tag = BookTag 08-27 15:11:38.000 11987-11987/com.android.peter.reflectdemo D/peter.log.ReflectClass: reflectPrivateMethod string = I am declaredMethod 1 ! 08-27 15:11:38.004 11987-11987/com.android.peter.reflectdemo D/peter.log.ReflectDemo: zenmode = 0示例 2:關于反射的用法舉例
??反射非常強大,但是從上面的記錄來看,反而覺得還不如直接調(diào)用方法來的直接和方便2。
??通常來說,需要在學習了Spring 的依賴注入,反轉(zhuǎn)控制之后,才會對反射有更好的理解,所以先這里舉兩個例子,來演示一下反射的一種實際運用3。
1、通過反射運行配置文件內(nèi)容
1. 首先準備兩個業(yè)務類
package service; public class Service1 {public void doService1(){System.out.println("業(yè)務方法1");} } package service; public class Service2 {public void doService2(){System.out.println("業(yè)務方法2");} }2. 當需要從第一個業(yè)務方法切換到第二個業(yè)務方法的時候,使用非反射方式,必須修改代碼,并且重新編譯運行,才可以達到效果
package service; public class CommonTest {public static void main(String[] args) {//new Service1().doService1();//必須重新修改代碼new Service2().doService2();} }3. 使用反射方式則方便很多
里面存放的是類的名稱,和要調(diào)用的方法名。首先準備一個配置文件,就叫做spring.txt吧, 放在src目錄下。里面存放的是類的名稱,和要調(diào)用的方法名。
示例:spring.txt 內(nèi)容
class=reflection.Service1 method=doService1測試類
package service;public class ReflectTest {@SuppressWarnings({"rawtypes", "unchecked"})public static void main(String[] args) throws Exception {//從spring.txt中獲取類名稱和方法名稱File springConfigFile = new File("H:\\eclpise-workspace\\reflect-demo\\src\\spring.txt");Properties springConfig = new Properties();springConfig.load(new FileInputStream(springConfigFile));String className = (String) springConfig.get("class");String methodName = (String) springConfig.get("method");//根據(jù)類名稱獲取類對象Class clazz = Class.forName(className);//根據(jù)方法名稱,獲取方法對象Method m = clazz.getMethod(methodName);//獲取構(gòu)造器Constructor c = clazz.getConstructor();//根據(jù)構(gòu)造器,實例化出對象Object service = c.newInstance();//調(diào)用對象的指定方法m.invoke(service);} }示例 2:
student 類:
public class Student {public void show() {System.out.println("is show()");} }配置文件以txt文件為例子(pro.txt):
className = cn.fanshe.Student methodName = show測試類:
import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.lang.reflect.Method; import java.util.Properties;/** 我們利用反射和配置文件,可以使:應用程序更新時,對源碼無需進行任何修改* 我們只需要將新類發(fā)送給客戶端,并修改配置文件即可*/ public class Demo {public static void main(String[] args) throws Exception {//通過反射獲取Class對象Class stuClass = Class.forName(getValue("className"));//"cn.fanshe.Student"//2獲取show()方法Method m = stuClass.getMethod(getValue("methodName"));//show//3.調(diào)用show()方法m.invoke(stuClass.getConstructor().newInstance());}//此方法接收一個key,在配置文件中獲取相應的valuepublic static String getValue(String key) throws IOException {Properties pro = new Properties();//獲取配置文件的對象FileReader in = new FileReader("pro.txt");//獲取輸入流pro.load(in);//將流加載到配置文件對象中in.close();return pro.getProperty(key);//返回根據(jù)key獲取的value值} }控制臺輸出:is show()
需求:當我們升級這個系統(tǒng)時,不要 Student 類,而需要新寫一個 Student2 的類時,這時只需要更改 pro.txt 的文件內(nèi)容就可以了。代碼就一點不用改動
要替換的 student2類:
public class Student2 {public void show2() {System.out.println("is show2()");} }配置文件更改為:
className = cn.fanshe.Student2 methodName = show2控制臺輸出:is show2();
2:通過反射越過泛型檢查
??泛型是在編譯期間起作用的。在編譯后的.class文件中是沒有泛型的。所有比如T或者E類型啊,本質(zhì)都是通過Object處理的。所以可以通過使用反射來越過泛型。
示例:
package test;public class GenericityTest {public static void main(String[] args) throws Exception {ArrayList<String> list = new ArrayList<>();list.add("this");list.add("is");// strList.add(5);報錯/********** 越過泛型檢查 **************///獲取ArrayList的Class對象,反向的調(diào)用add()方法,添加數(shù)據(jù)Class listClass = list.getClass();//獲取add()方法Method m = listClass.getMethod("add", Object.class);//調(diào)用add()方法m.invoke(list, 5);//遍歷集合for (Object obj : list) {System.out.println(obj);}} }示例 2:
泛型用在編譯期,編譯過后泛型擦除(消失掉)。所以是可以通過反射越過泛型檢查的
測試類:
import java.lang.reflect.Method; import java.util.ArrayList;/** 通過反射越過泛型檢查** 例如:有一個String泛型的集合,怎樣能向這個集合中添加一個Integer類型的值?*/ public class Demo {public static void main(String[] args) throws Exception {ArrayList<String> strList = new ArrayList<>();strList.add("aaa");strList.add("bbb");// strList.add(100);// 獲取ArrayList的Class對象,反向的調(diào)用add()方法,添加數(shù)據(jù)Class listClass = strList.getClass(); //得到 strList 對象的字節(jié)碼 對象// 獲取add()方法Method m = listClass.getMethod("add", Object.class);// 調(diào)用add()方法m.invoke(strList, 100);// 遍歷集合for (Object obj : strList) {System.out.println(obj);}} }控制臺輸出:
aaa
bbb
100
總結(jié)
本文列舉了反射機制使用過程中常用的、重要的一些類及其方法,更多信息和用法需要近一步的閱讀 Google 提供的相關文檔和示例。
在閱讀 Class類 文檔時發(fā)現(xiàn)一個特點,以通過反射獲得 Method 對象為例,一般會提供四種方法,
- getMethod(parameterTypes)? ? ? ? ? ? ? ? ? ? ? ?// 獲取某個公有的方法的對象,
- getMethods()? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 獲取該類所有公有的方法。
- getDeclaredMethod(parameterTypes)? ? ? ? // 獲取該類某個方法。
- getDeclaredMethods()。? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 獲取該類所有方法。
帶有 Declared 修飾的方法可以反射到私有的方法,沒有Declared修飾的只能用來反射公有的方法。其他的 Annotation、Field、Constructor 也是如此。
在 ReflectClass類中還提供了兩種反射 PowerManager.shutdown() 的方法,在調(diào)用的時候會輸出如下 log,提示沒有相關權(quán)限。之前在項目中嘗試反射其他方法的時候還遇到過有權(quán)限和沒權(quán)限返回的值不一樣的情況。如果源碼中明確進行了權(quán)限驗證,而你的應用又無法獲得這個權(quán)限的話,建議就不要浪費時間反射了。
W/System.err: java.lang.reflect.InvocationTargetExceptionW/System.err: at java.lang.reflect.Method.invoke(Native Method)W/System.err: at .ReflectClass.shutDown(ReflectClass.java:104)W/System.err: at .MainActivity$1.onClick(MainActivity.java:25)W/System.err: at android.view.View.performClick(View.java:6259)W/System.err: at android.view.View$PerformClick.run(View.java:24732)W/System.err: at android.os.Handler.handleCallback(Handler.java:789)W/System.err: at android.os.Handler.dispatchMessage(Handler.java:98)W/System.err: at android.os.Looper.loop(Looper.java:164)W/System.err: at android.app.ActivityThread.main(ActivityThread.java:6592)W/System.err: at java.lang.reflect.Method.invoke(Native Method)W/System.err: at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:769)W/System.err: Caused by: java.lang.SecurityException: Neither user 10224 nor current process has android.permission.REBOOT.W/System.err: at android.os.Parcel.readException(Parcel.java:1942)W/System.err: at android.os.Parcel.readException(Parcel.java:1888)W/System.err: at android.os.IPowerManager$Stub$Proxy.shutdown(IPowerManager.java:787)W/System.err: ... 12 moreReflectDemo:https://gitee.com/peter_RD_nj/DemoAllInOne/tree/master/ReflectDemo
參考文獻
敬業(yè)的小碼哥? ??How2jJava
認識反射機制(Reflection)
Java 反射機制
一個例子讓你了解Java反射機制
Java反射機制的原理及在Android下的簡單應用
java中的反射機制
Android注解與反射機制
java.lang.reflect.Method
?
總結(jié)
以上是生活随笔為你收集整理的Java 高级特性 --- 反射的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python 生成器 和 yield 关
- 下一篇: 安卓逆向_4 --- Java 学习