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