前言
HI亏镰,歡迎來到《每周一博》。今天是十二月第二周拯爽,我給大家介紹一下反射的知識(shí)索抓,為什么要介紹這些呢,因?yàn)椴寮夹g(shù)需要它作為基礎(chǔ)某抓。
一. 反射機(jī)制
JAVA反射機(jī)制是在運(yùn)行狀態(tài)中纸兔,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法否副,對(duì)于任意一個(gè)對(duì)象汉矿,都能夠調(diào)用它的任意一個(gè)方法和屬性,這種在運(yùn)行時(shí)動(dòng)態(tài)的獲取信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱為Java的反射機(jī)制备禀。
加載一個(gè)類的時(shí)候洲拇,jvm會(huì)去尋找Class文件并載入到內(nèi)存中,在運(yùn)行期間一個(gè)Class對(duì)象在內(nèi)存里只有一個(gè)曲尸,反射就是在運(yùn)行時(shí)從內(nèi)存中解析Class對(duì)象赋续,把屬性和方法映射成一個(gè)個(gè)的Java對(duì)象,原理如圖另患。
反射就好比察看一個(gè)人的內(nèi)臟器官纽乱,然后判斷出這個(gè)人的健康狀況,反射是Java作為一種動(dòng)態(tài)語言的關(guān)鍵性質(zhì)昆箕,利用它可以實(shí)現(xiàn)動(dòng)態(tài)編譯鸦列。
二. 獲取Class對(duì)象
類運(yùn)行時(shí)的類型信息就是用Class對(duì)象表示的,它包含了與類有關(guān)的信息鹏倘。每一個(gè)類都有一個(gè)Class對(duì)象薯嗤,每當(dāng)編譯一個(gè)新類就產(chǎn)生一個(gè)Class對(duì)象,基本類型 (boolean, byte, char, short, int, long, float, and double)有Class對(duì)象纤泵,數(shù)組有Class對(duì)象骆姐,就連關(guān)鍵字void也有Class對(duì)象(void.class)。
Class對(duì)象對(duì)應(yīng)著java.lang.Class類,它沒有公共的構(gòu)造方法玻褪,Class對(duì)象是在類加載的時(shí)候由Java虛擬機(jī)以及通過調(diào)用類加載器中的 defineClass 方法自動(dòng)構(gòu)造的肉渴,因此不能顯式地聲明一個(gè)Class對(duì)象。一個(gè)類被加載到內(nèi)存并供我們使用需要經(jīng)歷如下三個(gè)階段:
加載:這是由類加載器(ClassLoader)執(zhí)行的归园。通過一個(gè)類的全限定名來獲取其定義的二進(jìn)制字節(jié)流(Class字節(jié)碼)黄虱,將這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法去的運(yùn)行時(shí)數(shù)據(jù)接口,根據(jù)字節(jié)碼在java堆中生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象庸诱。
鏈接:在鏈接階段將驗(yàn)證Class文件中的字節(jié)流包含的信息是否符合當(dāng)前虛擬機(jī)的要求,為靜態(tài)域分配存儲(chǔ)空間并設(shè)置類變量的初始值晤揣,并且如果必需的話桥爽,將常量池中的符號(hào)引用轉(zhuǎn)化為直接引用。
初始化:到了此階段昧识,才真正開始執(zhí)行類中定義的java程序代碼钠四。用于執(zhí)行該類的靜態(tài)初始器和靜態(tài)初始?jí)K,如果該類有父類的話跪楞,則優(yōu)先對(duì)其父類進(jìn)行初始化缀去。
所有的類都是在對(duì)其第一次使用時(shí),動(dòng)態(tài)加載到JVM中的甸祭。當(dāng)程序創(chuàng)建第一個(gè)對(duì)類的靜態(tài)成員的引用時(shí)缕碎,就會(huì)加載這個(gè)類,使用new創(chuàng)建類對(duì)象的時(shí)候也會(huì)被當(dāng)作對(duì)類的靜態(tài)成員的引用池户。因此Java程序程序在它開始運(yùn)行之前并非被完全加載咏雌,其各個(gè)類都是在必需時(shí)才加載的。
在類加載階段校焦,類加載器首先檢查這個(gè)類的Class對(duì)象是否已經(jīng)被加載赊抖,如果尚未加載,默認(rèn)的類加載器就會(huì)根據(jù)類的全限定名查找.class文件寨典。在這個(gè)類的字節(jié)碼被加載時(shí)氛雪,它們會(huì)接受驗(yàn)證,以確保其沒有被破壞耸成,并且不包含不良Java代碼报亩。一旦某個(gè)類的Class對(duì)象被載入內(nèi)存,我們就可以它來創(chuàng)建這個(gè)類的所有對(duì)象墓猎。
要想反射一個(gè)類捆昏,必須先要獲取到該類的Class對(duì)象,可以通過三種方法獲取Class對(duì)象毙沾。
- 通過Object類中的 getClass() 方法骗卜,這是有了對(duì)象時(shí)候調(diào)用的,但是有了對(duì)象其實(shí)沒必要反射了,除非調(diào)用私有屬性寇仓;
Car car = new Car()举户;
Class<?> s = car.getClass();
- 每個(gè)類都有一個(gè)靜態(tài)的屬性class遍烦,可以直接通過該屬性獲得Class對(duì)象俭嘁,這種情況是需要導(dǎo)入包的,依賴性太強(qiáng)服猪;
Class<?> s = Car.class;
- 通過Class.forName()方法完成供填,必須要指定類的全名含包名,適用于不知道類的情況罢猪,當(dāng)然它會(huì)拋ClassNotFoundException異常近她,一般會(huì)把字符串寫入配置文件中來實(shí)現(xiàn)模塊解耦。
Class<?> c = Class.forName("com.refelct.Car");
還有個(gè)好處是通過Class.forName()會(huì)初始化靜態(tài)塊膳帕,而前兩者不會(huì)粘捎。我們知道當(dāng)一個(gè)類的靜態(tài)塊被調(diào)用的時(shí)候會(huì)進(jìn)行首次加載,但如果一個(gè)字段被static final修飾危彩,那么在調(diào)用這個(gè)字段的時(shí)候是不會(huì)對(duì)類進(jìn)行初始化的攒磨。因?yàn)楸籹tatic和final修飾的字段,在編譯期就把結(jié)果放入了常量池中了汤徽,但是如果只是將一個(gè)域設(shè)置為static 或final的娩缰,還是會(huì)對(duì)類進(jìn)行初始化的。
Class的方法有很多泻骤,和反射的方法很類似漆羔,這里列舉一些常用的。
- forName:產(chǎn)生Class引用狱掂,forName立即就進(jìn)行了初始化演痒;
- Object-getClass:獲取Class對(duì)象的一個(gè)引用,返回表示該對(duì)象的實(shí)際類型的Class引用趋惨;
- getName:取全限定的類名(包括包名)鸟顺;
- getSimpleName:獲取類名(不包括包名);
- isInterface:判斷Class對(duì)象是否是表示一個(gè)接口器虾;
- getInterfaces:返回Class對(duì)象數(shù)組讯嫂,表示Class對(duì)象所引用的類所實(shí)現(xiàn)的所有接口;
- getSupercalss:返回Class對(duì)象兆沙,表示Class對(duì)象所引用的類所繼承的直接基類欧芽,應(yīng)用該方法可在運(yùn)行時(shí)發(fā)現(xiàn)一個(gè)對(duì)象完整的繼承結(jié)構(gòu);
- newInstance:返回一個(gè)Oject對(duì)象葛圃,是實(shí)現(xiàn)“虛擬構(gòu)造器”的一種途徑千扔,使用該方法創(chuàng)建的類憎妙,必須帶有無參的構(gòu)造器;
- getFields:獲得某個(gè)類的所有的public字段曲楚,包括繼承自父類的所有公共字段厘唾。 類似的還有g(shù)etMethods和getConstructors;
- getDeclaredFields:獲得某個(gè)類的自己聲明的字段龙誊,即包括public抚垃、private和proteced,默認(rèn)不包括父類聲明的任何字段趟大。類似的還有g(shù)etDeclaredMethods和getDeclaredConstructors鹤树;
三. 反射的使用
- 反射的API主要涉及這么幾個(gè)類:Constructor描述構(gòu)造函數(shù),F(xiàn)ield描述變量逊朽,Method描述方法魂迄。我們一般用getDeclaredXXX來獲取對(duì)應(yīng)的類型,getDeclaredXXX和getXXX的區(qū)別是前者可以獲取到私有類型惋耙,下面一個(gè)例子打印出了String類的信息。
package com.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class Demo {
public static void main(String[] args) throws ClassNotFoundException {
System.out.println("printFieldInfo start===========");
printFieldInfo("hello");
System.out.println("printFieldInfo end=============");
System.out.println("printMethodInfo start===========");
printMethodInfo("hello");
System.out.println("printMethodInfo end===========");
System.out.println("printConstructorInfo start===========");
printConstructorInfo("hello");
System.out.println("printConstructorInfo end===========");
}
public static void printFieldInfo(Object o){
Class<?> clazz = o.getClass();
Field[] declaredFields = clazz.getDeclaredFields();
for (int i = 0; i < declaredFields.length; i++) {
Field field = declaredFields[i];
System.out.print((i+1)+" ");
System.out.print(Modifier.toString(field.getModifiers())+" ");
System.out.print(field.getType().getSimpleName()+" ");
System.out.print(field.getName());
System.out.println(";");
}
}
public static void printMethodInfo(Object o){
Class<?> clazz = o.getClass();
Method[] declaredMethods = clazz.getDeclaredMethods();
for (int i = 0; i < declaredMethods.length; i++) {
Method method = declaredMethods[i];
System.out.print((i+1)+" ");
System.out.print(Modifier.toString(method.getModifiers())+" ");
System.out.print(method.getReturnType().getSimpleName());
System.out.print("(");
Class<?>[] parameterTypes = method.getParameterTypes();
for (int j = 0; j < parameterTypes.length; j++) {
Class<?> parameterType =parameterTypes[j];
if(j==parameterTypes.length-1){
System.out.print(parameterType.getSimpleName()+" arg"+j);
}else{
System.out.print(parameterType.getSimpleName()+" arg"+j+",");
}
}
System.out.println(");");
}
}
public static void printConstructorInfo(Object o){
Class<?> clazz = o.getClass();
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
for (int i = 0; i < declaredConstructors.length; i++) {
Constructor<?> constructor = declaredConstructors[i];
System.out.print((i+1)+" ");
System.out.print(Modifier.toString(constructor.getModifiers())+" ");
System.out.print(clazz.getSimpleName());
System.out.print("(");
Class<?>[] parameterTypes = constructor.getParameterTypes();
for (int j = 0; j < parameterTypes.length; j++) {
Class<?> parameterType =parameterTypes[j];
if(j==parameterTypes.length-1){
System.out.print(parameterType.getSimpleName()+" arg"+j);
}else{
System.out.print(parameterType.getSimpleName()+" arg"+j+",");
}
}
System.out.println(");");
}
}
}
打印結(jié)果如下熊昌;
printFieldInfo start===========
1 private final char[] value;
2 private int hash;
3 private static final long serialVersionUID;
4 private static final ObjectStreamField[] serialPersistentFields;
5 public static final Comparator CASE_INSENSITIVE_ORDER;
printFieldInfo end=============
printMethodInfo start===========
1 public boolean(Object arg0);
2 public String();
……
76 public String(Locale arg0);
77 public String();
printMethodInfo end===========
printConstructorInfo start===========
1 public String(byte[] arg0,int arg1,int arg2);
2 public String(byte[] arg0,Charset arg1);
……
15 public String(byte[] arg0,int arg1);
16 public String(byte[] arg0,int arg1,int arg2,int arg3);
printConstructorInfo end===========
- 實(shí)際操練绽榛,編寫測(cè)試類,定義了有參和無參的構(gòu)造函數(shù)婿屹,普通變量灭美,靜態(tài)變量,普通方法昂利,靜態(tài)方法届腐。
public class BeReflected {
// 私有普通變量
private String field1 = "I am field1";
// 私有靜態(tài)變量
private static String staticField = "I am staticField";
// 無參構(gòu)造方法
public BeReflected() {
}
// 有參構(gòu)造方法
public BeReflected(String s) {
field1 = s;
}
// 普通無參方法
private void method1() {
System.out.println("I am method1");
}
// 普通帶1個(gè)參數(shù)方法
private void method2(String param) {
System.out.println("I am method1--param = " + param);
}
// 普通帶多個(gè)參數(shù)方法
private void method3(String param, String param2, int param3) {
System.out.println("param = " + param + " param2 = " + param2 + " param3 = " + param3);
}
// 靜態(tài)無參方法
public static void staticMethod() {
System.out.println("I am staticMethod");
}
// 靜態(tài)帶參數(shù)方法
public static void staticMethod(String s) {
System.out.println("I am staticMethod:s:" + s);
}
}
- 反射創(chuàng)建對(duì)象:通過Class的newInstance對(duì)象就可以創(chuàng)建一個(gè)對(duì)象,所以new只是創(chuàng)建對(duì)象的一種方式蜂奸。當(dāng)然通過newInstance要求改類需要有一個(gè)空的無參構(gòu)造方法犁苏。
Class<?> c = Class.forName("com.refelct.BeReflected");
Object obj = c.newInstance();
- 反射調(diào)用構(gòu)造函數(shù):如果想調(diào)用有參數(shù)的構(gòu)造函數(shù),就要用到Constructor這個(gè)類了扩所,通過Class的getDeclaredConstructor方法可以獲得Constructor對(duì)象围详,傳入的參數(shù)是方法參數(shù)類型,比如int要傳入int.class祖屏,字符串傳String.class助赞,字符串?dāng)?shù)組傳String[].class;
Class<?> c = Class.forName("com.refelct.BeReflected");
Constructor ss = c.getDeclaredConstructor(String.class);
Object tt = ss.newInstance("測(cè)試構(gòu)造函數(shù)");
- 反射獲取靜態(tài)變量:調(diào)用屬性就要用到Field這個(gè)類了袁勺,通過getDeclaredField方法獲取到屬性后需要設(shè)置setAccessible(true)雹食。由于靜態(tài)變量是屬于類的,所以不需要類的實(shí)例期丰,直接調(diào)用Field類的get(null)即可獲得群叶;
Class<?> c = Class.forName("com.refelct.BeReflected");
Field field = c.getDeclaredField("staticField");
if (field != null) {
field.setAccessible(true);
Object o = field.get(null);
System.out.println("o:" + o);
}
- 反射獲取私有普通變量:由于普通變量是屬于對(duì)象的吃挑,所以需要先獲得類的實(shí)例,然后再調(diào)用Field類的get(obj)盖呼;
Class<?> c = Class.forName("com.refelct.BeReflected");
Object obj = c.newInstance();
Field field = c.getDeclaredField("field1");
if (field != null) {
field.setAccessible(true);
Object o = field.get(obj);
System.out.println("o:" + o);
}
所以靜態(tài)和非靜態(tài)的區(qū)別在于是否需要傳入對(duì)象儒鹿,當(dāng)然我試了靜態(tài)方法和屬性傳入obj也可以獲取到。
- 反射設(shè)置私有變量:通過Field類的set(obj几晤,value)可以修改私有變量约炎。
Class<?> c = Class.forName("com.refelct.BeReflected");
Object obj = c.newInstance();
Field field = c.getDeclaredField("field1");
if (field != null) {
field.setAccessible(true);
field.set(obj, "測(cè)試變量");
System.out.println("o:" + field.get(obj));
}
- 反射調(diào)用無參方法:方法主要是用到Method類,先通過getDeclaredMethod()獲取方法蟹瘾,然后setAccessible(true)圾浅,接著執(zhí)行invoke函數(shù),同理靜態(tài)方法不需要對(duì)象憾朴。
Class<?> c = Class.forName("com.refelct.BeReflected");
// 調(diào)用靜態(tài)無參方法
Method method = c.getDeclaredMethod("method1");
if (method != null) {
method.setAccessible(true);
method.invoke(null);
}
// 調(diào)用普通無參方法
Object obj = c.newInstance();
Method method = c.getDeclaredMethod("method1");
if (method != null) {
method.setAccessible(true);
method.invoke(obj);
}
- 反射調(diào)用有參方法:有參是需要傳入?yún)?shù)類型Class<?>... parameterTypes和參數(shù)值Object... args的狸捕。
Class<?> c = Class.forName("com.refelct.BeReflected");
// 調(diào)用普通有1個(gè)參數(shù)的方法
Object obj = c.newInstance();
Method method = c.getDeclaredMethod("method2",String.class);
if (method != null) {
method.setAccessible(true);
method.invoke(obj,"我是測(cè)試值");
}
// 調(diào)用普通有多個(gè)參數(shù)的方法
Object obj = c.newInstance();
Method method = c.getDeclaredMethod("method3",
new Class<?>[]{String.class, String.class, int.class});
if (method != null) {
method.setAccessible(true);
method.invoke(obj, new Object[]{"1", "2", 3});
}
四. 反射的其他知識(shí)
- final變量可以反射嗎?
如果是直接聲明的众雷,是無法反射的灸拍,因?yàn)榫幾g期間final類型的數(shù)據(jù)自動(dòng)被優(yōu)化了,即所有用到該變量的地方都被替換成了常量砾省。所以 get方法在編譯后自動(dòng)優(yōu)化成了return "gps"鸡岗,而不是 return GPS_PROVIDER。
private static final String GPS_PROVIDER = "gps";
但如果不是直接定義的就可以反射
private static final String GPS_PROVIDER ;
public LocationManager(){
GPS_PROVIDER = "gps";
}
- 只能反射自己jvm所包含的class编兄,不能反射別的進(jìn)程里的類轩性,比如想反射微信里的某個(gè)字段,那是不可能的狠鸳。
五. 反射的問題
- 反射的效率問題:反射比直接調(diào)用實(shí)例要慢揣苏,getMethod和getDeclaredField方法會(huì)比invoke和set方法耗時(shí),詳細(xì)介紹可以參考這篇文章件舵。這里我們做個(gè)測(cè)試卸察,反射調(diào)用靜態(tài)方法正常調(diào)用靜態(tài)方法各100000遍。
private static void testTime() throws Exception {
{
long t1 = System.currentTimeMillis();
Class<?> c = Class.forName("com.refelct.BeReflected");
Object obj = c.newInstance();
Method m = c.getDeclaredMethod("method1");
m.setAccessible(true);
for (int i = 0; i < 100000; i++) {
m.invoke(obj);
}
long t2 = System.currentTimeMillis() - t1;
System.out.println("反射消耗:" + t2);
}
{
long t1 = System.currentTimeMillis();
BeReflected beReflected = new BeReflected();
for (int i = 0; i < 100000; i++) {
beReflected.method1();
}
long t2 = System.currentTimeMillis() - t1;
System.out.println("正常消耗:" + t2);
}
}
結(jié)果打勇:
正常消耗:8ms
反射消耗:65ms
可以看到但是調(diào)用invoke就很耗時(shí)了蛾派,還沒有把getMethod和Class.forName加入循環(huán),那么有什么可以提高反射效率的方法嗎个少?這里我想到這么幾點(diǎn)洪乍;
- 使用接口代替Object:在用反射創(chuàng)建對(duì)象時(shí)轉(zhuǎn)成類的實(shí)例或者接口,然后執(zhí)行方法調(diào)用夜焦,避免調(diào)用invoke壳澳;
IRe obj = (IRe) c.newInstance();
// BeReflected obj = (BeReflected) c.newInstance();
for (int i = 0; i < 100000; i++) {
obj.method1();
}
結(jié)果打印:
正常消耗:7ms
反射消耗:5ms
使用緩存:對(duì)于中間產(chǎn)物使用緩存存儲(chǔ)下來茫经,比如class對(duì)象巷波,方法名萎津,變量,可以用一個(gè)map來做緩存抹镊;
使用getDeclaredMethod(param)是要由于先調(diào)用getMethod然后去遍歷方法名的锉屈,getDeclaredField也是;
反射的安全問題:因?yàn)榭梢噪S意修改類的所有狀態(tài)和行為垮耳,包括private方法和實(shí)例颈渊,所以如果不熟悉被反射類的實(shí)現(xiàn)原理,隨意修改可能導(dǎo)致潛在的邏輯問題终佛;
兼容性問題:反射會(huì)涉及到直接訪問類的方法名和實(shí)例名俊嗽,不同版本的API如果有變動(dòng),反射時(shí)找不到對(duì)應(yīng)的屬性和方法時(shí)會(huì)報(bào)異常铃彰,最常見的就是針對(duì)安卓版本的適配绍豁;
其他問題:有用到反射的類不能被混淆,靜態(tài)編譯時(shí)沒有用到的類和資源不能被刪除牙捉,否則反射找不到竹揍;
反射的特點(diǎn):
反射自由度高,不受類的訪問權(quán)限限制邪铲;
反射存在性能問題鬼佣,但使用不頻繁時(shí)對(duì)程序性能影響并不大;
反射是改不了方法霜浴,攔截方法需要采用動(dòng)態(tài)代理;
六. 反射的用途
為什么需要反射呢蓝纲,這用途說來就很多了阴孟。
- 構(gòu)建框架:一般構(gòu)建框架的時(shí)候會(huì)用到反射,比如創(chuàng)建Activity的時(shí)候就用到了newInstance方法税迷,Java當(dāng)中的很多框架都采用反射永丝。
- 構(gòu)建設(shè)計(jì)模式:比如一個(gè)采用反射來創(chuàng)建對(duì)象的工廠模式。
public class Factory {
public static <T extends Product> T getProduct(String className){
Class<?> cls = Class.forName(className);
Product product = (Product) cls.newInstance();
return (T) product;
}
}
- 按需加載類箭养,節(jié)省編譯和初始化APK的時(shí)間慕嚷。動(dòng)態(tài)加載第三方j(luò)ar包,解決安卓開發(fā)中方法數(shù)不能超過65536個(gè)的問題毕泌;
- 通過反射運(yùn)行配置文件喝检,實(shí)現(xiàn)解耦,其實(shí)也是設(shè)計(jì)框架的思想撼泛;
- 跳過泛型檢查:如果我們往List里添加元素挠说,必須符合類型檢查,如果不符合就編譯不過愿题,泛型是在編譯期檢查的损俭,通過反射就可以在運(yùn)行時(shí)跳過這個(gè)限制蛙奖。
ArrayList<String> strList = new ArrayList<>();
strList.add("aaa");
strList.add("bbb");
// 正常情況下添加一個(gè)100是無法通過編譯的
// strList.add(100);
//獲取ArrayList的Class對(duì)象,反向的調(diào)用add()方法杆兵,添加數(shù)據(jù)
Class listClass = strList.getClass();
Method m = listClass.getMethod("add", Object.class);
m.invoke(strList, 100);
// 輸出的時(shí)候注意這里用了Object雁仲,而不是String
for(Object obj : strList){
System.out.println(obj);
}
- 反射執(zhí)行main方法
Class clazz = Class.forName("fanshe.main.Student");
Method methodMain = clazz.getMethod("main", String[].class);
// 方式一
methodMain.invoke(null, (Object)new String[]{"a","b","c"});
// 方式二
// methodMain.invoke(null, new Object[]{new String[]{"a","b","c"}});
- 寫一個(gè)簡(jiǎn)單的反射工具類ReflectUtils,但實(shí)際做框架時(shí)還是要轉(zhuǎn)成接口的琐脏,還要考慮泛型攒砖,不能一味的使用Object。
public class ReflectUtils {
// 反射一個(gè)對(duì)象的無參方法骆膝,靜態(tài)方法obj可以傳null
// ReflectUtils.invoke(c, "staticMethod", obj)
public static Object invoke(Class<?> c, String methodName, Object obj) {
try {
Method method = c.getDeclaredMethod(methodName);
if (method != null) {
method.setAccessible(true);
return method.invoke(obj);
} else {
p("該方法不存在");
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// 反射一個(gè)對(duì)象的有一個(gè)參數(shù)方法祭衩,靜態(tài)方法obj可以傳null
// ReflectUtils.invoke(c, "staticMethod", obj, String.class, "test");
public static void invoke(Class<?> c, String methodName, Object obj, Class<?> target, Object params) {
try {
Method method = c.getDeclaredMethod(methodName, target);
if (method != null) {
method.setAccessible(true);
method.invoke(obj, params);
} else {
p("該方法不存在");
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 反射一個(gè)對(duì)象的有多個(gè)參數(shù)方法,靜態(tài)方法obj可以傳null
// ReflectUtils.invoke(c, "method4", obj, new Class<?>[]{String.class, String.class},
// new String[]{"1", "2"});
// ReflectUtils.invoke(c, "method3", obj, new Class<?>[]{String.class, String.class, int.class},
// new Object[]{"1", "2", 3});
public static void invoke(Class<?> c, String methodName, Object obj, Class<?>[] target, Object[] params) {
try {
Method method = c.getDeclaredMethod(methodName, target);
if (method != null) {
method.setAccessible(true);
method.invoke(obj, params);
} else {
p("該方法不存在");
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 獲取一個(gè)對(duì)象的值阅签,靜態(tài)變量obj可以傳null
// ReflectUtils.getValue(c, "pubfield1", obj);
public static Object getValue(Class<?> c, String fieldName, Object obj) {
try {
Field field = c.getDeclaredField(fieldName);
if (field != null) {
field.setAccessible(true);
return field.get(obj);
} else {
p("該變量不存在");
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// 修改一個(gè)對(duì)象的值掐暮,靜態(tài)變量obj可以傳null
// ReflectUtils.setValue(c, "staticField", obj, "測(cè)試變量")
public static Object setValue(Class<?> c, String fieldName, Object obj, Object value) {
try {
Field field = c.getDeclaredField(fieldName);
if (field != null) {
field.setAccessible(true);
field.set(obj, value);
return field.get(obj);
} else {
p("該變量不存在");
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// 傳入被代理對(duì)象的classloader,實(shí)現(xiàn)的接口,還有DynamicProxyHandler的對(duì)象即可
public static Object newProxyInstance(Object object, InvocationHandler invocationHandler) {
return Proxy.newProxyInstance(object.getClass().getClassLoader(),
object.getClass().getInterfaces(), invocationHandler);
}
public static void p(String s) {
System.out.println("" + s);
// Log.e("gzq",""+s);
}
}
七. 總結(jié)
本文介紹了反射的一些基本知識(shí),也是為下篇?jiǎng)討B(tài)代理做一個(gè)鋪墊政钟,感謝大家的閱讀路克,我們下周再見。