摘要:通過反射獲取帶參無返回值成員方法并使用設置安全檢查,訪問私有構(gòu)造函數(shù)必須創(chuàng)建實例這種不行,注意和方法需要傳遞參數(shù)測試復制這個功能獲取私有方法,同樣注意和的區(qū)別賦予訪問權(quán)限調(diào)用方法。
反射 目錄介紹
1.反射概述
1.1 反射概述
1.2 獲取class文件對象的三種方式
1.3 反射常用的方法介紹
1.4 反射的定義
1.5 反射的組成
1.6 反射的作用有哪些
2.反射的相關使用
2.1.1 通過反射獲取無參構(gòu)造方法并使用
2.1.2 通過反射獲取帶參構(gòu)造方法并使用
2.1.3 通過反射獲取私有構(gòu)造方法并使用
2.1.4 通過反射獲取成員變量并使用
2.1.5 通過反射獲取無參無返回值成員方法并使用
2.1.6 通過反射獲取帶參無返回值成員方法并使用
2.1.7 通過反射獲取帶參帶返回值成員方法并使用
2.1.8 通過反射獲取無參帶返回值成員方法并使用
3.相關知識點
3.1.1 設置setAccessible(true)暴力訪問權(quán)限
3.1.2 獲取Filed兩個方法的區(qū)別
3.1.3 獲取Field的類型
3.1.4 Method獲取方法名,獲取方法參數(shù)
3.1.5 Method方法的invoke()方法執(zhí)行
關于鏈接1.技術博客匯總
2.開源項目匯總
3.生活博客匯總
4.喜馬拉雅音頻匯總
5.程序員聊天筆記匯總
5.其他匯總
0.問題答疑0.1 被反射的類是否一定需要無參構(gòu)造方法?為什么?
0.2 反射的使用有什么優(yōu)勢和劣勢?為什么說反射可以降低耦合?
0.3 反射比較損耗性能,為什么這樣說?能否通過案例對比說明反射機制損耗性能……
0.4 反射是一種具有與類進行動態(tài)交互能力的一種機制,為什么要強調(diào)動態(tài)交互呢?
0.5 Java反射中的setAccessible()方法是否破壞了類的訪問規(guī)則
0.2 反射的使用有什么優(yōu)勢和劣勢?
射的初衷不是方便你去創(chuàng)建一個對象,而是讓你在寫代碼的時候可以更加靈活,降低耦合,提高代碼的自適應能力。
0.4 反射是一種具有與類進行動態(tài)交互能力的一種機制,為什么要強調(diào)動態(tài)交互呢
動態(tài)加載,也就是在運行的時候才會加載,而不是在編譯的時候,在需要的時候才進行加載獲取,或者說你可以在任何時候加載一個不存在的類到內(nèi)存中,然后進行各種交互,或者獲取一個沒有公開的類的所有信息,換句話說,開發(fā)者可以隨時隨意的利用反射的這種機制動態(tài)進行一些特殊的事情。
1.反射概述 1.1 反射概述JAVA反射機制是在運行狀態(tài)中,對于任意一個類,都能夠知道這個類的所有屬性和方法;
對于任意一個對象,都能夠調(diào)用它的任意一個方法和屬性;
這種動態(tài)獲取的信息以及動態(tài)調(diào)用對象的方法的功能稱為java語言的反射機制。
要想解剖一個類,必須先要獲取到該類的字節(jié)碼文件對象。
而解剖使用的就是Class類中的方法,所以先要獲取到每一個字節(jié)碼文件對應的Class類型的對象
1.2 獲取class文件對象的三種方式1.2.1 這三種方式為:
a:Object類的getClass()方法
b:靜態(tài)屬性class
c:Class類中靜態(tài)方法forName()
1.2.2 第一種方法【Object類的getClass()方法】
1.在內(nèi)存中新建一個Person的實例,對象p對這個內(nèi)存地址進行引用
2.對象p調(diào)用getClass()返回對象p所對應的Class對
3.調(diào)用newInstance()方法讓Class對象在內(nèi)存中創(chuàng)建對應的實例,并且讓p2引用實例的內(nèi)存地址
Person p = new Person(); Class> cls=p.getClass(); Person p2=(Person)cls.newInstance();
1.2.3 第二種方法【靜態(tài)屬性class】
1.獲取指定類型的Class對象,這里是Person
2.調(diào)用newInstance()方法在讓Class對象在內(nèi)存中創(chuàng)建對應的實例,并且讓p引用實例的內(nèi)存地址
Class> cls=Person.Class(); Person p=(Person)cls.newInstance();
1.2.4 第三種方法【Class類中靜態(tài)方法forName()】
1.通過JVM查找并加載指定的類(上面的代碼指定加載了com.fanshe包中的Person類)
2.調(diào)用newInstance()方法讓加載完的類在內(nèi)存中創(chuàng)建對應的實例,并把實例賦值給p
注意:如果找不到時,它會拋出 ClassNotFoundException 這個異常,這個很好理解,因為如果查找的類沒有在 JVM 中加載的話,自然要告訴開發(fā)者。
Class> cls=Class.forName("com.yc.Person"); //forName(包名.類名) Person p= (Person) cls.newInstance();1.3 通過反射獲取無參構(gòu)造方法并使用
A:獲取所有構(gòu)造方法
public Constructor>[] getConstructors()
public Constructor>[] getDeclaredConstructors()
B:獲取單個構(gòu)造方法
public Constructor
public Constructor
方法關鍵字
getDeclareMethods() 獲取所有的方法
getReturnType() 獲取方法的返回值類型
getParameterTypes() 獲取方法的傳入?yún)?shù)類型
getDeclareMethod("方法名,參數(shù)類型.class,....") 獲得特定的方法
構(gòu)造方法關鍵字
getDeclaredConstructors() 獲取所有的構(gòu)造方法
getDeclaredConstructors(參數(shù)類型.class,....) 獲取特定的構(gòu)造方法
成員變量
getDeclaredFields 獲取所有成員變量
getDeclaredField(參數(shù)類型.class,....) 獲取特定的成員變量
父類和父接口
getSuperclass() 獲取某類的父類
getInterfaces() 獲取某類實現(xiàn)的接口
1.4 反射的定義JAVA反射機制是在運行狀態(tài)中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調(diào)用它的任意方法和屬性;這種動態(tài)獲取信息以及動態(tài)調(diào)用對象方法的功能稱為java語言的反射機制
1.5 反射的組成
由于反射最終也必須有類參與,因此反射的組成一般有下面幾個方面組成:
1.java.lang.Class.java:類對象;
2.java.lang.reflect.Constructor.java:類的構(gòu)造器對象;
3.java.lang.reflect.Method.java:類的方法對象;
4.java.lang.reflect.Field.java:類的屬性對象;
反射中類的加載過程
根據(jù)虛擬機的工作原理,一般情況下,類需要經(jīng)過:加載->驗證->準備->解析->初始化->使用->卸載這個過程,如果需要反射的類沒有在內(nèi)存中,那么首先會經(jīng)過加載這個過程,并在在內(nèi)存中生成一個class對象,有了這個class對象的引用,就可以發(fā)揮開發(fā)者的想象力,做自己想做的事情了。
1.6 反射的作用有哪些
前面只是說了反射是一種具有與Java類進行動態(tài)交互能力的一種機制,在Java和Android開發(fā)中,一般情況下下面幾種場景會用到反射機制.
需要訪問隱藏屬性或者調(diào)用方法改變程序原來的邏輯,這個在開發(fā)中很常見的,由于一些原因,系統(tǒng)并沒有開放一些接口出來,這個時候利用反射是一個有效的解決方法
自定義注解,注解就是在運行時利用反射機制來獲取的。
在開發(fā)中動態(tài)加載類,比如在Android中的動態(tài)加載解決65k問題等等,模塊化和插件化都離不開反射,離開了反射寸步難行。
2.反射的相關使用 2.1.4 通過反射獲取成員變量[包含私有]并使用clazz = Class.forName("com.ycbjie.ycpaidian.four.ReflexUtils"); Constructor constructor = clazz.getDeclaredConstructor(Context.class); //設置安全檢查,訪問私有構(gòu)造函數(shù)必須 constructor.setAccessible(true); Object[] obj = new Object[]{mContext}; //創(chuàng)建實例 ReflexUtils clazzObj = (ReflexUtils) constructor.newInstance(obj); //反射修改私有變量 // 獲取聲明的 code 字段,這里要注意 getField 和 getDeclaredField 的區(qū)別 Field gradeField = clazz.getDeclaredField("code"); // 如果是 private 或者 package 權(quán)限的,一定要賦予其訪問權(quán)限 gradeField.setAccessible(true); // 修改 student 對象中的 Grade 字段值 gradeField.set(clazzObj, 2); LogUtils.e("點擊3----"+clazzObj.getCode());2.1.5 通過反射獲取無參無返回值成員方法[包含私有]并使用
clazz = Class.forName("com.ycbjie.ycpaidian.four.ReflexUtils"); Constructor constructor = clazz.getDeclaredConstructor(Context.class); //設置安全檢查,訪問私有構(gòu)造函數(shù)必須 constructor.setAccessible(true); Object[] obj = new Object[]{mContext}; //創(chuàng)建實例 ReflexUtils clazzObj = (ReflexUtils) constructor.newInstance(obj); // 獲取私有方法,同樣注意 getMethod 和 getDeclaredMethod 的區(qū)別 Method goMethod = clazz.getDeclaredMethod("getMethod"); // 賦予訪問權(quán)限 goMethod.setAccessible(true); // 調(diào)用 goToSchool 方法。 goMethod.invoke(clazzObj);2.1.6 通過反射獲取帶參無返回值成員方法并使用
clazz = Class.forName("com.ycbjie.ycpaidian.four.ReflexUtils"); Constructor constructor = clazz.getDeclaredConstructor(Context.class); //設置安全檢查,訪問私有構(gòu)造函數(shù)必須 constructor.setAccessible(true); Object[] obj = new Object[]{mContext}; //創(chuàng)建實例 ReflexUtils clazzObj = (ReflexUtils) constructor.newInstance(obj); //這種不行,注意getDeclaredMethod和invoke方法需要傳遞參數(shù) /*Method copyText = clazz.getDeclaredMethod("copyText"); copyText.setAccessible(true); copyText.invoke(clazzObj,"測試復制這個功能");*/ // 獲取私有方法,同樣注意 getMethod 和 getDeclaredMethod 的區(qū)別 Method copyText = clazz.getDeclaredMethod("copyText",String.class); // 賦予訪問權(quán)限 copyText.setAccessible(true); // 調(diào)用 copyText 方法。 copyText.invoke(clazzObj,"測試復制這個功能");2.1.7 通過反射獲取帶參帶返回值成員方法并使用
clazz = Class.forName("com.ycbjie.ycpaidian.four.ReflexUtils"); Constructor constructor = clazz.getDeclaredConstructor(Context.class); //設置安全檢查,訪問私有構(gòu)造函數(shù)必須 constructor.setAccessible(true); Object[] obj = new Object[]{mContext}; //創(chuàng)建實例 ReflexUtils clazzObj = (ReflexUtils) constructor.newInstance(obj); //這種不行,注意getDeclaredMethod和invoke方法需要傳遞參數(shù) /*Method copyText = clazz.getDeclaredMethod("copyText"); copyText.setAccessible(true); copyText.invoke(clazzObj,"測試復制這個功能");*/ // 獲取私有方法,同樣注意 getMethod 和 getDeclaredMethod 的區(qū)別 Method copyText1 = clazz.getDeclaredMethod("copyText",String.class,String.class); // 賦予訪問權(quán)限 copyText1.setAccessible(true); // 調(diào)用 copyText 方法 boolean isSuccess = (boolean) copyText1.invoke(clazzObj,"測試復制這個功能","1111");3.相關知識點
一般情況下,我們并不能對類的私有字段進行操作,利用反射也不例外,但有的時候,例如要序列化的時候,我們又必須有能力去處理這些字段,這時候,我們就需要調(diào)用AccessibleObject上的setAccessible()方法來允許這種訪問,而由于反射類中的Field,Method和Constructor繼承自AccessibleObject,因此,通過在這些類上調(diào)用setAccessible()方法,我們可以實現(xiàn)對這些字段的操作。
Field gradeField = clazz.getDeclaredField("code"); // 如果是 private 或者 package 權(quán)限的,一定要賦予其訪問權(quán)限 gradeField.setAccessible(true); Method goMethod = clazz.getDeclaredMethod("getMethod"); // 賦予訪問權(quán)限 goMethod.setAccessible(true);3.1.2 獲取Filed兩個方法的區(qū)別
兩者的區(qū)別就是 getDeclaredField() 獲取的是 Class 中被 private 修飾的屬性。 getField() 方法獲取的是非私有屬性,并且 getField() 在當前 Class 獲取不到時會向祖先類獲取。
//獲取所有的屬性,但不包括從父類繼承下來的屬性 public Field[] getDeclaredFields() throws SecurityException {} //獲取自身的所有的 public 屬性,包括從父類繼承下來的。 public Field[] getFields() throws SecurityException {}3.1.3 獲取Field的類型
可以看到 getGenericType() 確實把泛型都打印出來了,它比 getType() 返回的內(nèi)容更詳細。
public Type getGenericType() {} public Class> getType() {} Field[] filed2 = clazz.getFields(); for ( Field f : filed2 ) { System.out.println("Field :"+f.getName()); System.out.println("Field type:"+f.getType()); System.out.println("Field generic type:"+f.getGenericType()); System.out.println("-------------------"); } //打印值 07-31 17:20:41.027 8700-8700/com.ycbjie.ycpaidian I/System.out: Field :cars 07-31 17:20:41.027 8700-8700/com.ycbjie.ycpaidian I/System.out: Field type:interface java.util.List 07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field generic type:java.util.List3.1.4 Method獲取方法名,獲取方法參數(shù)07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field :map 07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field type:class java.util.HashMap 07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field generic type:java.util.HashMap 07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field :name 07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field type:class java.lang.String 07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field generic type:class java.lang.String
//獲取方法名 //獲取方法參數(shù) //返回的是一個 Parameter 數(shù)組,在反射中 Parameter 對象就是用來映射方法中的參數(shù)。 public Parameter[] getParameters() {} //Method獲取方法名 Method[] declaredMethods1 = clazz.getDeclaredMethods(); for ( Method m : declaredMethods1 ) { System.out.println("method name:"+m.getName()); } //獲取方法參數(shù) for ( Method m : declaredMethods1 ) { System.out.println("獲取方法參數(shù)method name:"+m.getName()); //獲取參數(shù) Parameter[] paras; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { paras = m.getParameters(); for (Parameter c : paras ) { System.out.println("獲取參數(shù)parameter :"+c.getName()+" "+c.getType().getName()); } } //獲取所有的參數(shù)類型 Class[] pTypes = m.getParameterTypes(); for ( Class c : pTypes ) { System.out.print("參數(shù)類型method para types:"+ c.getName()); } System.out.println(); System.out.println("=========================================="); } //打印日志如下所示: 07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: ========================================== 07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 獲取方法參數(shù)method name:copyText 07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 獲取參數(shù)parameter :arg0 java.lang.String 07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 參數(shù)類型method para types:java.lang.String 07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: ========================================== 07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 獲取方法參數(shù)method name:copyText 07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 獲取參數(shù)parameter :arg0 java.lang.String 07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 獲取參數(shù)parameter :arg1 java.lang.String 07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 參數(shù)類型method para types:java.lang.String參數(shù)類型method para types:java.lang.String 07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: ========================================== 07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 獲取方法參數(shù)method name:getCode 07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: ========================================== 07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: 獲取方法參數(shù)method name:getUserInfo 07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: 獲取參數(shù)parameter :arg0 java.lang.String 07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: 參數(shù)類型method para types:java.lang.String 07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: ========================================== 07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: 獲取方法參數(shù)method name:setCode 07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: 獲取參數(shù)parameter :arg0 int 07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: 參數(shù)類型method para types:int 07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: ==========================================3.1.5 Method方法的invoke()方法執(zhí)行
Method 調(diào)用 invoke() 的時候,存在許多細節(jié):
invoke() 方法中第一個參數(shù) Object 實質(zhì)上是 Method 所依附的 Class 對應的類的實例,如果這個方法是一個靜態(tài)方法,那么 ojb 為 null,后面的可變參數(shù) Object 對應的自然就是參數(shù)。
invoke() 返回的對象是 Object,所以實際上執(zhí)行的時候要進行強制轉(zhuǎn)換。
在對Method調(diào)用invoke()的時候,如果方法本身會拋出異常,那么這個異常就會經(jīng)過包裝,由Method統(tǒng)一拋InvocationTargetException。而通過InvocationTargetException.getCause() 可以獲取真正的異常。
我的個人站點:www.yczbj.org,www.ycbjie.cn
github:https://github.com/yangchong211
知乎:https://www.zhihu.com/people/...
簡書:http://www.jianshu.com/u/b7b2...
csdn:http://my.csdn.net/m0_37700275
喜馬拉雅聽書:http://www.ximalaya.com/zhubo...
開源中國:https://my.oschina.net/zbj161...
泡在網(wǎng)上的日子:http://www.jcodecraeer.com/me...
郵箱:yangchong211@163.com
阿里云博客:https://yq.aliyun.com/users/a... 239.headeruserinfo.3.dT4bcV
segmentfault頭條:https://segmentfault.com/u/xi...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/76581.html
摘要:使用反射可以在運行時檢視類。類名類修飾符等包信息超類所實現(xiàn)的接口構(gòu)造函數(shù)方法屬性注解類中附加了很多信息,你可以在獲得一個完整列表。全限定名包含所有的包名。構(gòu)造函數(shù)你可以訪問類的構(gòu)造函數(shù),代碼如下構(gòu)造函數(shù)的詳細教程在章節(jié)。 使用反射可以在運行時檢視Java類。檢視類通常是使用反射時所做的第一件事情。從類中可以獲得下面的信息。 類名 類修飾符(private、public、synchro...
摘要:本文是作者自己對中線程的狀態(tài)線程間協(xié)作相關使用的理解與總結(jié),不對之處,望指出,共勉。當中的的數(shù)目而不是已占用的位置數(shù)大于集合番一文通版集合番一文通版垃圾回收機制講得很透徹,深入淺出。 一小時搞明白自定義注解 Annotation(注解)就是 Java 提供了一種元程序中的元素關聯(lián)任何信息和著任何元數(shù)據(jù)(metadata)的途徑和方法。Annotion(注解) 是一個接口,程序可以通過...
摘要:動態(tài)代理有多種不同的用途,例如,數(shù)據(jù)庫連接和事務管理用于單元測試的動態(tài)模擬對象其他類似的方法攔截。調(diào)用序列和下面的流程類似單元測試動態(tài)對象模擬利用動態(tài)代理實現(xiàn)單元測試的動態(tài)存根代理和代理。框架把包裝成動態(tài)代理。 使用反射可以在運行時動態(tài)實現(xiàn)接口。這可以使用類java.lang.reflect.Proxy。這個類的名稱是我將這些動態(tài)接口實現(xiàn)稱之為動態(tài)代理的原因。動態(tài)代理有多種不同的用途,...
閱讀 3078·2021-11-24 09:38
閱讀 1330·2021-09-22 15:27
閱讀 2968·2021-09-10 10:51
閱讀 1504·2021-09-09 09:33
閱讀 917·2021-08-09 13:47
閱讀 2072·2019-08-30 13:05
閱讀 892·2019-08-29 15:15
閱讀 2425·2019-08-29 12:21