Java 高级特性 --- 反射
From:Java 高級特性 --- 反射:https://www.jianshu.com/p/9be58ee20dee
From:Java 基礎(chǔ)之 --- 反射(非常重要):https://blog.csdn.net/sinat_38259539/article/details/71799078
From:Java?高級?/?反射機(jī)制:https://how2j.cn/k/reflection/reflection-class/108.html
From:Java 中反射機(jī)制詳解:https://www.cnblogs.com/whgk/p/6122036.html
From:深入分析 Java 方法反射的實(shí)現(xiàn)原理:https://www.jianshu.com/p/3ea4a6b57f87
Java 反射 - 超詳細(xì)講解(附源碼):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. 概述
反射是框架設(shè)計(jì)的靈魂
使用前提條件:必須先得到代表的字節(jié)碼的Class,Class類?用于表示 .class文件?( 字節(jié)碼 )
注意:在運(yùn)行期間,一個(gè)類,只有一個(gè)Class對象產(chǎn)生。
1.1 定義
JAVA反射機(jī)制(?https://baike.baidu.com/item/JAVA反射機(jī)制/6015990 )是:
?在運(yùn)行狀態(tài)中,
- 對于任意一個(gè)類,都能夠獲取到這個(gè)類的所有屬性和方法;
- 對于任意一個(gè)對象 ( 即 類的實(shí)例 ),都能夠調(diào)用它的任意方法和屬性?(包括 私有的方法 和 屬性);
- 這種 動態(tài)獲取信息 以及 動態(tài)調(diào)用對象方法 的功能 稱為 java 語言的反射機(jī)制。
想要使用反射機(jī)制,必須先 獲取到該類的 字節(jié)碼文件對象(.class),而反射使用的就是 Class類 中的方法。
通過 字節(jié)碼文件對象 就能夠通過該類中的方法獲取到我們想要的所有信息 ( 方法,屬性,類名,父類名,實(shí)現(xiàn)的所有接口等等)
每一個(gè)類 對應(yīng)著 一個(gè)字節(jié)碼文件?也就對應(yīng)著 一個(gè)Class類型的對象,也就是 字節(jié)碼文件對象。
反射就是把 編寫的java類 中的各種成分映射成一個(gè)個(gè)的Java對象
例如:一個(gè)類有:成員變量、方法、構(gòu)造方法、包等等信息,利用反射技術(shù)可以對一個(gè)類進(jìn)行解剖,把個(gè)個(gè)組成部分映射成一個(gè)個(gè)對象。
? ? ? ? ? ( 其實(shí):一個(gè)類中的這些成員方法、構(gòu)造方法、在加入類中都有一個(gè)類來描述 )
如圖是類的正常加載過程:反射的原理在與class對象。
熟悉一下加載的時(shí)候:Class對象 的由來是將 class文件 讀入內(nèi)存,并為之創(chuàng)建一個(gè) Class對象。
其中這個(gè) Class對象 很特殊。我們先了解一下這個(gè) Class類
二、查看 Class類 在 java 中的 api 詳解(1.7的API)
Java8 API 中文:Java 8 中文版 - 在線API中文手冊 - 碼工具? ??Java8 API 英文:Java Platform SE 8
如何閱讀 java 中的 api 詳見 java基礎(chǔ)之 --- String字符串處理
Class?類的實(shí)例表示正在運(yùn)行的 Java 應(yīng)用程序中的類和接口。也就是jvm中有N多的實(shí)例每個(gè)類都有該Class對象。(包括基本數(shù)據(jù)類型)
Class?沒有公共構(gòu)造方法。Class?對象是在加載類時(shí)由 Java 虛擬機(jī)以及通過調(diào)用類加載器中的defineClass?方法自動構(gòu)造的。也就是這不需要我們自己去處理創(chuàng)建,JVM已經(jīng)幫我們創(chuàng)建好了。
沒有公共的構(gòu)造方法,方法共有64個(gè)太多了。下面用到哪個(gè)就詳解哪個(gè)吧
1.2 反射的用途和使用
在日常的第三方應(yīng)用開發(fā)過程中,經(jīng)常會遇到 某個(gè)類 的 某個(gè)成員變量、方法?或是 屬性?是私有的或是只對系統(tǒng)應(yīng)用開放,這時(shí)候就可以利用 Java 的反射機(jī)制通過反射來獲取所需的私有成員或是方法。當(dāng)然,也不是所有的都適合反射,之前就遇到一個(gè)案例,通過反射得到的結(jié)果與預(yù)期不符。閱讀源碼發(fā)現(xiàn),經(jīng)過層層調(diào)用后在最終返回結(jié)果的地方對應(yīng)用的權(quán)限進(jìn)行了校驗(yàn),對于沒有權(quán)限的應(yīng)用返回值是沒有意義的缺省值,否則返回實(shí)際值,這樣就起到保護(hù)用戶隱私的目的。
假設(shè)我們現(xiàn)在有一個(gè)?Hero類
package pojo;public class Hero {public String name; //姓名public float hp; //血量public float armor; //護(hù)甲public int moveSpeed; //移動速度 }1、獲取類對象
獲取 類對象 ( 即?字節(jié)碼文件對象 ) 的 3種方式
- Class.forName()? ? ? ? ? ? ? ? // 常用。傳入 類名的字符串 即可得到 類對象
? ? ? ? 通過Class類的靜態(tài)方法: forName(String className)
? ? ? ? // 通過Class類中的靜態(tài)方法forName,直接獲取到一個(gè)類的字節(jié)碼文件對象,
? ? ? ? // 此時(shí)該類還是源文件階段,并沒有變?yōu)樽止?jié)碼文件。
? ? ? ? Class clazz1 = Class.forName("全限定類名"); - Hero.class? ? ? ? ? ? ? ? ? ? ? ? ?//?需要導(dǎo)入類的包,依賴太強(qiáng),不導(dǎo)包就拋編譯錯誤
? ? ? ?(?任何數(shù)據(jù)類型(包括基本數(shù)據(jù)類型)都有一個(gè) "靜態(tài)" 的 class屬性 )
? ? ? // 當(dāng)類被加載成.class文件時(shí),此時(shí)Person類變成了.class,
? ? ? // 在獲取該字節(jié)碼文件對象,也就是獲取自己, 該類處于字節(jié)碼階段。
? ? ? Class clazz2??= Person.class; - new Hero().getClass()? ? ? ?//?對象都有了還要反射干什么。。。
? ? ? ? (?Object??-->??getClass();? 因?yàn)?所有類 都繼承 Object類。從而調(diào)用 Object類 的 getClass方法 來獲取 )
? ? ? ?// 通過類的實(shí)例獲取該類的字節(jié)碼文件對象,該類處于創(chuàng)建對象階段
? ? ? ?Class clazz3 = p.getClass();
有了 字節(jié)碼文件對象 才能獲得類中所有的信息,上面最常用的是:?Class clazz1 = Class.forName("全限定類名");
在一個(gè)JVM中,一種類,只會有一個(gè)類對象存在。所以以上 3種方式取出來的類對象,都是一樣。(此處準(zhǔn)確是在ClassLoader下,只有一個(gè)類對象)
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();}} }注意:在運(yùn)行期間,一個(gè)類,只有一個(gè)Class對象產(chǎn)生。
2、Class類 的 API 詳解
2.1、通過字節(jié)碼對象創(chuàng)建實(shí)例對象
2.2、獲取指定構(gòu)造器方法。constructor 如果沒有無參構(gòu)造,只有有參構(gòu)造如何創(chuàng)建實(shí)例呢?
總結(jié)上面創(chuàng)建實(shí)例對象:
Class類 的 newInstance() 方法是使用該類無參的構(gòu)造函數(shù)創(chuàng)建對象,如果一個(gè)類沒有無參的構(gòu)造函數(shù),?就不能這樣創(chuàng)建了。
可以調(diào)用Class類的 getConstructor(String.class, int.class) 方法獲取一個(gè)指定的構(gòu)造函數(shù),然后再調(diào)用 Constructor類的 newInstance("張三",20)方法創(chuàng)建對象
獲取全部構(gòu)造方法
2.3、獲取 成員變量 并使用 Field對象
獲取指定成員變量
Class.getField(String)方法可以獲取類中的指定字段(可見的),?如果是私有的可以用 getDeclaedField("name")方法獲取,通過 set(obj, "李四")方法可以設(shè)置指定對象上該字段的值,?如果是私有的需要先調(diào)用 setAccessible(true) 設(shè)置訪問權(quán)限,用獲取的指定的字段調(diào)用 get(obj) 可以獲取指定對象中該字段的值
獲取全部成員變量
2.4、獲得方法并使用 Method
Class.getMethod(String, Class...) 和 Class.getDeclaredMethod(String, Class...)方法可以獲取類中的指定方法,
如果為私有方法,則需要使用?setAccessible(true); 打開一個(gè)權(quán)限。用 invoke(Object, Object...) 可以調(diào)用該方法,跟上面同理,也能一次性獲得所有的方法
2.5、獲得該類的所有接口
? ? ? ? Class[] getInterfaces():確定此對象所表示的類或接口實(shí)現(xiàn)的接口
? ? ? ? 返回值:接口的字節(jié)碼文件對象的數(shù)組
2.6、獲取指定資源的輸入流
? ? ? ? InputStream?getResourceAsStream(String name)
? ? ? ? return:一個(gè) InputStream 對象;如果找不到帶有該名稱的資源,則返回?null
? ? ? ? 參數(shù):所需資源的名稱,如果以"/"開始,則絕對資源名為"/"后面的一部分。
2.7、動態(tài)代理的概述和實(shí)現(xiàn)
動態(tài)代理:一種設(shè)計(jì)模式,其非常簡單,很容易理解,你自己可以做這件事,但是覺得自己做非常麻煩或者不方便,所以就叫另一個(gè)人(代理)來幫你做這個(gè)事情,而你就不用管了,這就是動態(tài)代理。舉個(gè)例子,買火車票叫人代買。?
程序運(yùn)行過程中產(chǎn)生的這個(gè)對象,其實(shí)就是我們剛才反射講解的內(nèi)容,所以,動態(tài)代理其實(shí)就是通過反射來生成一個(gè)代理
在Java中 java.lang.reflect 包下提供了一個(gè) Proxy類 和 一個(gè)InvocationHandler接口,通過使用 這個(gè)類 和 接口 就可以生成動態(tài)代理對象。
JDK提供的代理只能針對接口做代理。我們有更強(qiáng)大的代理 cglib,Proxy類中的方法創(chuàng)建動態(tài)代理類對象分三步,但是注意JDK提供的代理只能針對接口做代理,也就是下面的第二步返回的必須要是一個(gè)接口。
- 1、new出代理對象,通過實(shí)現(xiàn)InvacationHandler接口,然后new出代理對象來。
- 2、通過Proxy類中的靜態(tài)方法newProxyInstance,來將代理對象假裝成那個(gè)被代理的對象,也就是如果叫人幫我們代買火車票一樣,那個(gè)代理就假裝成我們自己本人
- 3、執(zhí)行方法,代理成功
將代理對象中的內(nèi)容進(jìn)行實(shí)現(xiàn)
1、2、3步
注意 newProxyInstance 的三個(gè)參數(shù),第一個(gè),類加載器,第二個(gè)被代理對象的接口,第三個(gè)代理對象。
2.8、還有很多方法,比如獲得類加載器,等等,具體還需要別的,就通過查看API文檔來解決。
3、反射機(jī)制的應(yīng)用實(shí)例
應(yīng)用實(shí)例 1:
3.1、利用反射,在泛型為 int 的 arryaList 集合中存放一個(gè) String 類型的對象
原理:集合中的泛型只在編譯器有效,而到了運(yùn)行期,泛型則會失效,
3.2、利用反射,簡化編寫Servlet的個(gè)數(shù)。
什么意思呢?每當(dāng)我們寫一個(gè)功能時(shí),就需要寫一個(gè)對應(yīng)的Servlet,導(dǎo)致最后Servlet有很多,自己都看不過來,所以對其進(jìn)行了優(yōu)化,兩種方式,
3.2.1、每次從頁面?zhèn)鬟^來一個(gè)參數(shù),method="xxx"; 然后編寫一個(gè)Servlet,獲得其參數(shù)method的值,進(jìn)行判斷,如果是add,則調(diào)用add方法,如果是delete,則調(diào)用delete方法,這樣就可以寫在一個(gè)servlet中實(shí)現(xiàn)所有的功能了。
3.2.2、利用反射
編寫一個(gè)BaseServlet 繼承 HttpServlet,這是一個(gè)通用的 BaseServlet。需要明白 servlet 的生命周期
編寫具體實(shí)現(xiàn)的方法 servlet 類。
MySerlvet001 extends BaseServlet
解釋:需要明白servlet的生命周期,也就是service方法,因?yàn)槭莝ervlet,所以在訪問的時(shí)候,會經(jīng)過service方法,而子類MyServlet001中并沒有,所以就到父類BaseServlet中找,發(fā)現(xiàn)有,然后獲取參數(shù)即知道了需要調(diào)用什么方法,因?yàn)榉椒ǖ木帉懚荚谧宇愔?#xff0c;所以通過反射,獲取到子類中對應(yīng)的方法并運(yùn)行,其中需要注意的是this這個(gè)參數(shù)在BaseServlet中的用法。需要理解它。才能理解我們這個(gè)程序。
對其一些api進(jìn)行講解,不懂的就查看API,重要的思想,就要在實(shí)際中遇到了才能得到更好的理解。
應(yīng)用實(shí)例 2:
反射非常強(qiáng)大,但是學(xué)習(xí)了之后,會不知道該如何使用,反而覺得還不如直接調(diào)用方法來的直接和方便。
通常來說,需要在學(xué)習(xí)了?Spring?的依賴注入,反轉(zhuǎn)控制之后,才會對反射有更好的理解,但是剛學(xué)到這里的同學(xué),不一定接觸了Spring,所以在這里舉兩個(gè)例子,來演示一下反射的一種實(shí)際運(yùn)用。
步驟?1?:?業(yè)務(wù)類: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è)務(wù)類 ??
首先準(zhǔn)備兩個(gè)業(yè)務(wù)類,這兩個(gè)業(yè)務(wù)類很簡單,就是各自都有一個(gè)業(yè)務(wù)方法,分別打印不同的字符串
Service1.java
package reflection;public class Service1 {public void doService2(){System.out.println("111111111111 業(yè)務(wù)方法1");} }Service2.java
package reflection;public class Service2 {public void doService2(){System.out.println("222222222222 業(yè)務(wù)方法2");} }- 步驟 2 : 非反射方式 ??
當(dāng)需要從第一個(gè)業(yè)務(wù)方法切換到第二個(gè)業(yè)務(wù)方法的時(shí)候,使用非反射方式,必須修改代碼,并且重新編譯運(yùn)行,才可以達(dá)到效果
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 : 反射方式 ??
使用反射方式,首先準(zhǔn)備一個(gè)配置文件,就叫做 spring.txt 吧,放在 src 目錄下。 里面存放的是類的名稱,和要調(diào)用的方法名。
spring.txt
class=reflection.Service1 method=doService1在測試類Test中,首先取出類名稱和方法名,然后通過反射去調(diào)用這個(gè)方法。
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)造器,實(shí)例化出對象Object service = c.newInstance();//調(diào)用對象的指定方法m.invoke(service);} }當(dāng)需要從調(diào)用第一個(gè)業(yè)務(wù)方法,切換到調(diào)用第二個(gè)業(yè)務(wù)方法的時(shí)候,不需要修改一行代碼,也不需要重新編譯,只需要修改配置文件 spring.txt,再運(yùn)行即可。
這也是?Spring框架?的最基本的原理,只是它做的更豐富,安全,健壯。
1.3 反射機(jī)制的相關(guān)類
與 Java 反射相關(guān)的類如下:
| Class 類 | 代表 類的實(shí)體,在運(yùn)行的 Java 應(yīng)用程序中表示 類和接口 |
| Field 類 | 代表 類的成員變量(成員變量?也稱為 類的屬性) |
| Method 類 | 代表 類的方法 |
| Constructor 類 | 代表 類的構(gòu)造方法 |
1.4 利用反射機(jī)制 創(chuàng)建對象
基本步驟
? ? ? ? 與傳統(tǒng)的通過 new 來獲取對象的方式不同反射機(jī)制,反射會先拿到 Hero 的 "類對象",然后通過類對象獲取 "構(gòu)造器對象" ,再通過構(gòu)造器對象創(chuàng)建一個(gè)對象,具體步驟:
1. 獲取類對象? ? ? ? ?Class class = Class.forName("pojo.Hero");
2. 獲取構(gòu)造器對象??Constructor con = clazz.getConstructor(形參.class);
3. 獲取對象? ? ? ? ? ? ?Hero hero =con.newInstance(實(shí)參);
上面是最簡單的獲取方法,當(dāng) Hero 的 構(gòu)造方法 不是無參構(gòu)造方法時(shí),獲取構(gòu)造器對象略有不同,見下面測試:
構(gòu)造方法不同時(shí),獲取構(gòu)造器對象的方法
示例:
- Hero 類 添加 6 種構(gòu)造方法
- 通過反射機(jī)制獲取對象
輸出:
**********************所有公有構(gòu)造方法********************************* public pojo.Hero(java.lang.String,float) public pojo.Hero(char) public pojo.Hero() ************所有的構(gòu)造方法(包括:私有、受保護(hù)、默認(rèn)、公有)*************** 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)造方法 ( 包括 私有、受保護(hù)、默認(rèn)、公有)
? ? 2). 獲取單個(gè)的方法:
? ? ? ? ? ? ? ? public Constructor getConstructor(Class… parameterTypes):? ? ? ? ? ? ? ?獲取單個(gè)的 "公有的" 構(gòu)造方法
? ? ? ? ? ? ? ? public Constructor getDeclaredConstructor(Class…parameterTypes):? 獲取 "某個(gè)構(gòu)造方法" 可以是 私有的,或受保護(hù)、默認(rèn)、公有;
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í)例,并用指定的初始化參數(shù)初始化該實(shí)例。
? ? ? ? ? ? 它的返回值是T類型,所以newInstance是創(chuàng)建了一個(gè)構(gòu)造方法的聲明類的新實(shí)例對象。并為之調(diào)用
1.5?獲取成員變量并使用
基本步驟
1. 獲取 HeroPlus類 的對象?new方法/第2章中的方法?h
2. 獲取屬性?Field f1 = h.getDeclaredField("屬性名")
3. 修改屬性?f1.set(h,實(shí)參),注意這里的 h 是 對象,不是 類對象
示例:
- 新增 HeroPlus 類
- 獲取屬性并修改
補(bǔ)充: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():獲取所有字段,包括:私有、受保護(hù)、默認(rèn)、公有;* 2.獲取單個(gè)的:* 1).public Field getField(String fieldName):獲取某個(gè)"公有的"字段;* 2).public Field getDeclaredField(String fieldName):獲取某個(gè)字段(可以是私有的)** 設(shè)置字段的值:* Field --> public void set(Object obj,Object value):* 參數(shù)說明:* 1.obj:要設(shè)置的字段所在的對象;* 2.value:要為字段設(shè)置的值;**/ 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("************獲取所有的字段(包括私有、受保護(hù)、默認(rèn)的)********************");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);//獲取一個(gè)對象Object obj = stuClass.getConstructor().newInstance();//產(chǎn)生Student對象--》Student stu = new Student();//為字段設(shè)置值f.set(obj, "劉德華");//為Student對象中的name屬性賦值--》stu.name = "劉德華"//驗(yàn)證Student stu = (Student) obj;System.out.println("驗(yàn)證姓名:" + 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("驗(yàn)證電話:" + stu);} }后臺輸出:
************獲取所有公有的字段******************** public java.lang.String fanshe.field.Student.name ************獲取所有的字段(包括私有、受保護(hù)、默認(rèn)的)******************** 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 驗(yàn)證姓名:劉德華 **************獲取私有字段****并調(diào)用******************************** private java.lang.String fanshe.field.Student.phoneNum 驗(yàn)證電話:Student [name=劉德華, age=0, sex=由此可見
調(diào)用字段時(shí):需要傳遞兩個(gè)參數(shù):
- 第一個(gè)參數(shù):要傳入設(shè)置的對象
- 第二個(gè)參數(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)用方式時(shí)所傳遞的實(shí)參;
示例:
package test;public class MethodTest {public static void main(String[] args) {HeroPlus h = new HeroPlus();try {// 獲取這個(gè)名字叫做setName,參數(shù)類型是String的方法Method m = h.getClass().getMethod("setName", String.class);// 對h對象,調(diào)用這個(gè)方法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)用了:受保護(hù)的,無參的show2()");}void show3() {System.out.println("調(diào)用了:默認(rèn)的,無參的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.獲取單個(gè)的:* 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)用方式時(shí)所傳遞的實(shí)參; ):*/ 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);//實(shí)例化一個(gè)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);//需要兩個(gè)參數(shù),一個(gè)是要調(diào)用的對象(獲取有反射),一個(gè)是實(shí)參System.out.println("返回值:" + result);} }由此可見:
// 調(diào)用制定方法(所有包括私有的),需要傳入兩個(gè)參數(shù), // 第一個(gè)是調(diào)用的方法名稱, // 第二個(gè)是方法的形參類型,切記是類型。 m = stuClass.getDeclaredMethod("show4", int.class); System.out.println(m);// 解除私有限定 m.setAccessible(true); // 需要兩個(gè)參數(shù),一個(gè)是要調(diào)用的對象(獲取有反射),一個(gè)是實(shí)參 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其實(shí)這里的成員方法:在模型中有屬性一詞,就是那些 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方法、不要與當(dāng)前的main方法搞混了*/ public class Main {public static void main(String[] args) {try {//1、獲取Student對象的字節(jié)碼Class clazz = Class.forName("fanshe.main.Student");//2、獲取main方法。 // 第一個(gè)參數(shù):方法名稱,第二個(gè)參數(shù):方法形參的類型,Method methodMain = clazz.getMethod("main", String[].class);//3、調(diào)用main方法// methodMain.invoke(null, new String[]{"a","b","c"});// 第一個(gè)參數(shù),對象類型,因?yàn)榉椒ㄊ莝tatic靜態(tài)的,所以為null可以,// 第二個(gè)參數(shù)是String數(shù)組,這里要注意在jdk1.4時(shí)是數(shù)組,jdk1.5之后是可變參數(shù)// 這里拆的時(shí)候?qū)? new String[]{"a","b","c"} 拆成3個(gè)對象。。。所以需要將它強(qiáng)轉(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 方法反射的實(shí)現(xiàn)原理
From:https://www.jianshu.com/p/3ea4a6b57f87
從一起GC血案談到反射原理 (?https://mp.weixin.qq.com/s/5H6UHcP6kvR2X5hTj_SBjA )?,就把 Java 方法的反射機(jī)制實(shí)現(xiàn)擼了一遍。
方法反射實(shí)例
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 的反射機(jī)制,可以在運(yùn)行期間調(diào)用對象的任何方法,如果大量使用這種方式進(jìn)行調(diào)用,會有性能或內(nèi)存隱患么?為了徹底了解方法的反射機(jī)制,只能從底層代碼入手了。
Method 獲取
調(diào)用 Class 類的 getDeclaredMethod 可以獲取 指定方法名和參數(shù) 的 方法對象 Method。
getDeclaredMethod
其中?privateGetDeclaredMethods?方法從 緩存 或 JVM中 獲取該Class中申明的方法列表,searchMethods方法將從返回的方法列表里找到一個(gè)匹配名稱和參數(shù)的方法對象。
searchMethods
如果找到一個(gè)匹配的?Method,則重新 copy 一份返回,即?Method.copy()方法
所次每次調(diào)用getDeclaredMethod方法返回的Method對象其實(shí)都是一個(gè)新的對象,且新對象的root屬性都指向原來的Method對象,如果需要頻繁調(diào)用,最好把Method對象緩存起來。
privateGetDeclaredMethods
從緩存或 JVM 中獲取該?Class?中申明的方法列表,實(shí)現(xiàn)如下:
其中?reflectionData()方法實(shí)現(xiàn)如下:
這里有個(gè)比較重要的數(shù)據(jù)結(jié)構(gòu)?ReflectionData,用來緩存從JVM中讀取類的如下屬性數(shù)據(jù):
從reflectionData()方法實(shí)現(xiàn)可以看出:reflectionData對象是SoftReference類型的,說明在內(nèi)存緊張時(shí)可能會被回收,不過也可以通過-XX:SoftRefLRUPolicyMSPerMB參數(shù)控制回收的時(shí)機(jī),只要發(fā)生GC就會將其回收,如果reflectionData被回收之后,又執(zhí)行了反射方法,那只能通過newReflectionData方法重新創(chuàng)建一個(gè)這樣的對象了,newReflectionData方法實(shí)現(xiàn)如下:
通過unsafe.compareAndSwapObject方法重新設(shè)置reflectionData字段;
在privateGetDeclaredMethods方法中,如果通過reflectionData()獲得的ReflectionData對象不為空,則嘗試從ReflectionData對象中獲取declaredMethods屬性,如果是第一次,或則被GC回收之后,重新初始化后的類屬性為空,則需要重新到JVM中獲取一次,并賦值給ReflectionData,下次調(diào)用就可以使用緩存數(shù)據(jù)了。
Method 調(diào)用
獲取到指定的方法對象Method之后,就可以調(diào)用它的invoke方法了,invoke實(shí)現(xiàn)如下:
應(yīng)該注意到:這里的MethodAccessor對象是invoke方法實(shí)現(xiàn)的關(guān)鍵,一開始methodAccessor為空,需要調(diào)用acquireMethodAccessor生成一個(gè)新的MethodAccessor對象,MethodAccessor本身就是一個(gè)接口,實(shí)現(xiàn)如下:
在acquireMethodAccessor方法中,會通過ReflectionFactory類的newMethodAccessor創(chuàng)建一個(gè)實(shí)現(xiàn)了MethodAccessor接口的對象,實(shí)現(xiàn)如下:
在ReflectionFactory類中,有2個(gè)重要的字段:noInflation(默認(rèn)false)和inflationThreshold(默認(rèn)15),在checkInitted方法中可以通過-Dsun.reflect.inflationThreshold=xxx和-Dsun.reflect.noInflation=true對這兩個(gè)字段重新設(shè)置,而且只會設(shè)置一次;
如果noInflation為false,方法newMethodAccessor都會返回DelegatingMethodAccessorImpl對象,DelegatingMethodAccessorImpl的類實(shí)現(xiàn)
其實(shí),DelegatingMethodAccessorImpl對象就是一個(gè)代理對象,負(fù)責(zé)調(diào)用被代理對象delegate的invoke方法,其中delegate參數(shù)目前是NativeMethodAccessorImpl對象,所以最終Method的invoke方法調(diào)用的是NativeMethodAccessorImpl對象invoke方法,實(shí)現(xiàn)如下:
這里用到了ReflectionFactory類中的inflationThreshold,當(dāng)delegate調(diào)用了15次invoke方法之后,如果繼續(xù)調(diào)用就通過MethodAccessorGenerator類的generateMethod方法生成MethodAccessorImpl對象,并設(shè)置為delegate對象,這樣下次執(zhí)行Method.invoke時(shí),就調(diào)用新建的MethodAccessor對象的invoke()方法了。
這里需要注意的是:
generateMethod方法在生成MethodAccessorImpl對象時(shí),會在內(nèi)存中生成對應(yīng)的字節(jié)碼,并調(diào)用ClassDefiner.defineClass創(chuàng)建對應(yīng)的class對象,實(shí)現(xiàn)如下:
在ClassDefiner.defineClass方法實(shí)現(xiàn)中,每被調(diào)用一次都會生成一個(gè)DelegatingClassLoader類加載器對象
這里每次都生成新的類加載器,是為了性能考慮,在某些情況下可以卸載這些生成的類,因?yàn)轭惖男遁d是只有在類加載器可以被回收的情況下才會被回收的,如果用了原來的類加載器,那可能導(dǎo)致這些新創(chuàng)建的類一直無法被卸載,從其設(shè)計(jì)來看本身就不希望這些類一直存在內(nèi)存里的,在需要的時(shí)候有就行了。
Class類
Class?代表類的實(shí)體,在運(yùn)行的 Java 應(yīng)用程序中表示 類 和 接口。在這個(gè)類中提供了很多有用的方法,這里只簡單的分類介紹下。
獲取類相關(guān)的方法
| asSubclass(Class<U> clazz) | 把傳遞的類的對象轉(zhuǎn)換成代表其子類的對象 |
| Cast | 把對象轉(zhuǎn)換成代表類或是接口的對象 |
| getClassLoader() | 獲得類的加載器 |
| getClasses() | 返回一個(gè)數(shù)組,數(shù)組中包含該類中所有公共類和接口類的對象 |
| getDeclaredClasses() | 返回一個(gè)數(shù)組,數(shù)組中包含該類中所有類和接口類的對象 |
| forName(String className) | 根據(jù)類名返回類的對象 |
| getName() | 獲得類的完整路徑名字 |
| newInstance() | 創(chuàng)建類的實(shí)例 |
| getPackage() | 獲得類的包 |
| getSimpleName() | 獲得類的名字 |
| getSuperclass() | 獲得當(dāng)前類繼承的父類的名字 |
| getInterfaces() | 獲得當(dāng)前類實(shí)現(xiàn)的類或是接口 |
獲取?類中屬性相關(guān)的方法
| getField(String name) | 獲得某個(gè)公有的屬性對象 |
| getFields() | 獲得所有公有的屬性對象 |
| getDeclaredField(String name) | 獲得某個(gè)屬性對象 |
| getDeclaredFields() | 獲得所有屬性對象 |
獲取?類中注解相關(guān)的方法
| getAnnotation(Class<A> annotationClass) | 返回該類中與參數(shù)類型匹配的公有注解對象 |
| getAnnotations() | 返回該類所有的公有注解對象 |
| getDeclaredAnnotation(Class<A> annotationClass) | 返回該類中與參數(shù)類型匹配的所有注解對象 |
| getDeclaredAnnotations() | 返回該類所有的注解對象 |
獲得?類中構(gòu)造器相關(guān)的方法
| getConstructor(Class...<?> parameterTypes) | 獲得該類中與參數(shù)類型匹配的公有構(gòu)造方法 |
| getConstructors() | 獲得該類的所有公有構(gòu)造方法 |
| getDeclaredConstructor(Class...<?> parameterTypes) | 獲得該類中與參數(shù)類型匹配的構(gòu)造方法 |
| getDeclaredConstructors() | 獲得該類所有構(gòu)造方法 |
獲取?類中方法相關(guān)的方法
| getMethod(String name, Class...<?> parameterTypes) | 獲得該類某個(gè)公有的方法 |
| getMethods() | 獲得該類所有公有的方法 |
| getDeclaredMethod(String name, Class...<?> parameterTypes) | 獲得該類某個(gè)方法 |
| getDeclaredMethods() | 獲得該類所有方法 |
類中 其他重要的方法
| isAnnotation() | 如果是注解類型則返回 true |
| isAnnotationPresent(Class<? extends Annotation> annotationClass) | 如果是指定類型注解類型則返回 true |
| isAnonymousClass() | 如果是匿名類則返回 true |
| isArray() | 如果是一個(gè)數(shù)組類則返回 true |
| isEnum() | 如果是枚舉類則返回 true |
| isInstance(Object obj) | 如果obj是該類的實(shí)例則返回 true |
| isInterface() | 如果是接口類則返回 true |
| isLocalClass() | 如果是局部類則返回 true |
| isMemberClass() | 如果是內(nèi)部類則返回 true |
Field 類
Field?代表 類的成員變量(成員變量也稱為類的屬性)。
| equals(Object obj) | 屬性與 obj 相等則返回 true |
| get(Object obj) | 獲得 obj 中對應(yīng)的屬性值 |
| set(Object obj, Object value) | 設(shè)置 obj 中對應(yīng)屬性值 |
Method 類
Method?代表 類的方法 。
| invoke(Object obj, Object... args) | 傳遞 object 對象及參數(shù)調(diào)用該對象對應(yīng)的方法 |
Constructor類
Constructor?代表?類的構(gòu)造方法。
| newInstance(Object... initargs) | 根據(jù)傳遞的參數(shù)創(chuàng)建類的對象 |
示例 1:演示反射的使用
為了演示反射的使用,首先構(gòu)造一個(gè)與書籍相關(guān)的 model ---?Book.java,然后通過反射方法示例創(chuàng)建對象、反射私有構(gòu)造方法、反射私有屬性、反射私有方法,最后給出兩個(gè)比較復(fù)雜的反射示例 ---?獲得當(dāng)前 ZenMode 和 關(guān)機(jī)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進(jìn)階之光");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ā)藝術(shù)探索","任玉剛");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;}// 關(guān)閉手機(jī)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)用相應(yīng)反射邏輯方法
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進(jìn)階之光', author='劉望舒'} 08-27 15:11:38.000 11987-11987/com.android.peter.reflectdemo D/peter.log.ReflectClass: reflectPrivateConstructor book = Book{name='Android開發(fā)藝術(shù)探索', 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:關(guān)于反射的用法舉例
??反射非常強(qiáng)大,但是從上面的記錄來看,反而覺得還不如直接調(diào)用方法來的直接和方便2。
??通常來說,需要在學(xué)習(xí)了Spring 的依賴注入,反轉(zhuǎn)控制之后,才會對反射有更好的理解,所以先這里舉兩個(gè)例子,來演示一下反射的一種實(shí)際運(yùn)用3。
1、通過反射運(yùn)行配置文件內(nèi)容
1. 首先準(zhǔn)備兩個(gè)業(yè)務(wù)類
package service; public class Service1 {public void doService1(){System.out.println("業(yè)務(wù)方法1");} } package service; public class Service2 {public void doService2(){System.out.println("業(yè)務(wù)方法2");} }2. 當(dāng)需要從第一個(gè)業(yè)務(wù)方法切換到第二個(gè)業(yè)務(wù)方法的時(shí)候,使用非反射方式,必須修改代碼,并且重新編譯運(yùn)行,才可以達(dá)到效果
package service; public class CommonTest {public static void main(String[] args) {//new Service1().doService1();//必須重新修改代碼new Service2().doService2();} }3. 使用反射方式則方便很多
里面存放的是類的名稱,和要調(diào)用的方法名。首先準(zhǔn)備一個(gè)配置文件,就叫做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)造器,實(shí)例化出對象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;/** 我們利用反射和配置文件,可以使:應(yīng)用程序更新時(shí),對源碼無需進(jìn)行任何修改* 我們只需要將新類發(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());}//此方法接收一個(gè)key,在配置文件中獲取相應(yīng)的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()
需求:當(dāng)我們升級這個(gè)系統(tǒng)時(shí),不要 Student 類,而需要新寫一個(gè) Student2 的類時(shí),這時(shí)只需要更改 pro.txt 的文件內(nèi)容就可以了。代碼就一點(diǎn)不用改動
要替換的 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);報(bào)錯/********** 越過泛型檢查 **************///獲取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;/** 通過反射越過泛型檢查** 例如:有一個(gè)String泛型的集合,怎樣能向這個(gè)集合中添加一個(gè)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é)
本文列舉了反射機(jī)制使用過程中常用的、重要的一些類及其方法,更多信息和用法需要近一步的閱讀 Google 提供的相關(guān)文檔和示例。
在閱讀 Class類 文檔時(shí)發(fā)現(xiàn)一個(gè)特點(diǎn),以通過反射獲得 Method 對象為例,一般會提供四種方法,
- getMethod(parameterTypes)? ? ? ? ? ? ? ? ? ? ? ?// 獲取某個(gè)公有的方法的對象,
- getMethods()? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 獲取該類所有公有的方法。
- getDeclaredMethod(parameterTypes)? ? ? ? // 獲取該類某個(gè)方法。
- getDeclaredMethods()。? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 獲取該類所有方法。
帶有 Declared 修飾的方法可以反射到私有的方法,沒有Declared修飾的只能用來反射公有的方法。其他的 Annotation、Field、Constructor 也是如此。
在 ReflectClass類中還提供了兩種反射 PowerManager.shutdown() 的方法,在調(diào)用的時(shí)候會輸出如下 log,提示沒有相關(guān)權(quán)限。之前在項(xiàng)目中嘗試反射其他方法的時(shí)候還遇到過有權(quán)限和沒權(quán)限返回的值不一樣的情況。如果源碼中明確進(jìn)行了權(quán)限驗(yàn)證,而你的應(yīng)用又無法獲得這個(gè)權(quán)限的話,建議就不要浪費(fèi)時(shí)間反射了。
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
參考文獻(xiàn)
敬業(yè)的小碼哥? ??How2jJava
認(rèn)識反射機(jī)制(Reflection)
Java 反射機(jī)制
一個(gè)例子讓你了解Java反射機(jī)制
Java反射機(jī)制的原理及在Android下的簡單應(yīng)用
java中的反射機(jī)制
Android注解與反射機(jī)制
java.lang.reflect.Method
?
總結(jié)
以上是生活随笔為你收集整理的Java 高级特性 --- 反射的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python 生成器 和 yield 关
- 下一篇: supervisor安装和配置