詳解Andorid開發(fā)中反射機(jī)制是怎么一回事
1. 背景
在andorid開發(fā)中,經(jīng)常遇見在某些工具類中沒有Context上下文對象時,一些系統(tǒng)服務(wù)的代理對象無法創(chuàng)建出來,舉個例子:比如在源碼(framework/base/graphics/java/android/graphics)路徑下的Canvas.java Bitmap.javaPicture.java Paint.java 類就沒有上下文對象。當(dāng)時有需要調(diào)用AMS的API獲取當(dāng)前界面activity的名稱作為判斷條件去修改這些工具類中的參數(shù),但是這幾個工具類中又沒有上下文對象,怎么辦呢?
一般情況下,獲取頂層activity名稱的方法如下:
//1. 獲取 AMS 的代理對象 ActivityManager
ActivityManager am = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
//2. 調(diào)用相關(guān)API 獲取頂層activity的名稱
String activityName = am.getRunningTasks(1).get(0).topActivity.getClassName();上述代碼是在有上下文對象context的情況下,直接獲取。
而在上面提及到的Picture.java等類中, 根本就沒有上下文,怎么搞? 這個時候我們通過java反射來實(shí)現(xiàn),接下來,本篇文章就來講解一下java反射機(jī)制,并利用反射實(shí)現(xiàn)獲取頂層activity。
2. java反射
2.1 什么是反射
反射(Reflection)是程序的自我分析能力,通過反射可以確定類中有哪些方法、有哪些構(gòu)造方法以及有哪些成員變量,在運(yùn)行狀態(tài)中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調(diào)用它的任意方法和屬性;這種動態(tài)獲取信息以及動態(tài)調(diào)用對象方法的功能稱為java語言的反射機(jī)制。
2.2 什么情況下要用反射
首先java是面向?qū)ο缶幊痰?,萬物皆對象, 當(dāng)我們拿到這個類對象后,就可以訪問類中的成員變量,調(diào)用類中的方法了,但是呢?對于private變量和方法,它的訪問權(quán)限作用域只在本類中,在外部類或者繼承類中,就算你創(chuàng)建了該類對象,也是無法直接調(diào)用的。來看下這個例子:
普通的一個User 類
public class User {
private String name = "墨子"; //私有變量
public int age = 18;
//私有方法
private int test() {
System.out.println("私有成員方法");
return 1;
}
protected String test2() {
System.out.println("protected成員方法");
return "protected";
}
public static void main(String[] args) {
//如果在本類中創(chuàng)建了對象,則所有的方法和變量都可以直接訪問
User user = new User();
System.out.println(user.name); //訪問私有變量
System.out.println(user.test());//調(diào)用私有方法
}
}打印結(jié)果如下:
墨子
私有成員方法
1
如果在外部內(nèi)中創(chuàng)建該類對象,還能直接訪問私有方法和變量嗎? 測試一下:
public class PrivateTest {
public static void main(String[] args) {
User user = new User();
//外部內(nèi)中創(chuàng)建的對象直接私有變量 報錯
//System.out.println(user.name);
//外部內(nèi)中創(chuàng)建的對象,直接調(diào)用private方法,也報錯
//System.out.println(user.test());
//但是可以直接訪問 protected public的方法和變量
System.out.println(user.age);
System.out.println(user.test2());
}
}報錯的兩行代碼注釋掉了,可以把代碼復(fù)制過去驗(yàn)證一下。
當(dāng)然了,在andorid開發(fā)中,還有其他場景會使用到反射, 很多類或方法中經(jīng)常加上了“@hide”注釋標(biāo)記,這些API是不允許在第三方應(yīng)用直接調(diào)用的比如:SystemProperties類在android.os下,但這個類是隱藏的,沒有系統(tǒng)權(quán)限(android:sharedUserId="android.uid.system")的app無法直接使用,只能通過java反射來調(diào)用。
還有 Andorid中動態(tài)代理模式 , Android插件化(Hook)這些都會用到反射,這里不一一說明,因?yàn)槊總€技術(shù)點(diǎn)都需要去深挖才能理解透徹。
2.3 反射的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
1.可以調(diào)用私有方法、變量
2.可以調(diào)用隱藏api接口
3.能夠運(yùn)行時動態(tài)獲取類的實(shí)例,提高代碼靈活性
缺點(diǎn):
1.安全問題:反射可以獲取到類對應(yīng)的所有方法和屬性,如果存在外部調(diào)用的情況,可能出現(xiàn)超出預(yù)期的調(diào)用,從而導(dǎo)致安全風(fēng)險, 而且也破壞java面向?qū)ο缶幊痰姆庋b這一特性。
2.性能問題:無論是通過字符串獲取Class、Method還是Field,都需要JVM的動態(tài)鏈接機(jī)制動態(tài)的進(jìn)行解析和匹配,勢必造成性能開銷。每一次的反射調(diào)用都會造成Java安全機(jī)制進(jìn)行額外的安全性驗(yàn)證,造成性能開銷。反射代碼使得許多JVM的運(yùn)行時優(yōu)化無法進(jìn)行。
3. Java反射機(jī)制API
Java反射機(jī)制API主要是 java.lang.Class類 和 java.lang.reflect包。
| 類 | 說明 |
| java.lang.Class | 代表整個字節(jié)碼。代表一個類型,代表整個類。 |
| java.lang.reflect.Constructor | 代表字節(jié)碼中的構(gòu)造方法字節(jié)碼。代表類中的構(gòu)造方法。 |
| java.lang.reflect.Method | 代表字節(jié)碼中的方法字節(jié)碼。代表類中的方法。 |
| java.lang.reflect.Field | 代表字節(jié)碼中的屬性字節(jié)碼。代表類中的成員變量(靜態(tài)變量+實(shí)例變量) |
3.1 獲取Class對象
| 方式 | 說明 |
| 對象.getClass(); | |
| 類名.class | |
| Class.forName(“類的全路徑”); | 推薦此種寫法 |
package com.example.javademo.reflect;
public class ClassObject {
public static void main(String[] args) throws ClassNotFoundException {
//第一種: 對象.getClass();
People people = new People();
Class clazz1 = people.getClass();
System.out.println(clazz1);
//第二種: 類名.class
Class clazz2 = People.class;
System.out.println(clazz2);
//第三種: Class.forName(包名.類名)
Class clazz3 = Class.forName("com.example.javademo.reflect.People");
System.out.println(clazz3);
}
}第一種:直接new出對象,然后通過對象獲取class對象, 但是你想想類對象都可以直接new出來,在外部內(nèi)中除了private 變量和方法不能直接訪問外,都可以直接調(diào)用,我還反射干嘛?寫那么多代碼豈不是為難自己,沒事找事,不推薦!
第二種:通過類名.class 獲取字節(jié)碼class對象,需要導(dǎo)入包,依賴性比較強(qiáng),不然會編譯報錯
第三種:Class的靜態(tài)方法,安全性強(qiáng), 參數(shù)為:類包名+類名
運(yùn)行結(jié)果如下:
class com.example.javademo.reflect.People
class com.example.javademo.reflect.People
class com.example.javademo.reflect.People
三個都是同一個Class對象,在運(yùn)行期間,一個類,只有一個Class對象產(chǎn)生
3.2 反射調(diào)用類構(gòu)造方法
方法名解釋說明
| 方法名 | 解釋說明 |
public Constructor[] getConstructors() | 所有"公有的"構(gòu)造方法 |
| public Constructor getConstructor(Class... parameterTypes) | 獲取指定參數(shù)的共有構(gòu)造方法 |
| public Constructor[] getDeclaredConstructors() | 獲取所有的構(gòu)造方法(包括私有、受保護(hù)、默認(rèn)、公有) |
| public Constructor getDeclaredConstructor(Class... parameterTypes) | :獲取"指定參數(shù)的構(gòu)造方法"可以是私有的,或受保護(hù)、默認(rèn)、公有; |
| newInstance(Object... initargs) | 使用此 Constructor 對象表示的構(gòu)造方法來創(chuàng)建該構(gòu)造方法的聲明類的新實(shí)例,并用指定的初始化參數(shù)初始化該實(shí)例。 它的返回值是T類型,所以newInstance是創(chuàng)建了一個構(gòu)造方法的聲名類的新實(shí)例對象,相當(dāng)于People p = new People(); |
我們看下Demo
package com.example.javademo.reflect;
public class People {
private String name;
private int age;
public String sex;
private long height;
public People() {
System.out.println("調(diào)用了public無參構(gòu)造方法 執(zhí)行完畢");
}
public People(String name) {
this.name = name;
System.out.println("調(diào)用public有參構(gòu)造方法 String:name = " + name);
}
private People(int age, String sex) {
this.age = age;
this.sex = sex;
System.out.println("調(diào)用private有參數(shù)構(gòu)造方法 age :"+ age + " sex:" + sex);
}
People(long height) {
this.height = height;
System.out.println("調(diào)用default有參數(shù)構(gòu)造方法 height :"+ height );
}
}反射測試類:
package com.example.javademo.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflectPeople {
public static void main(String[] args) {
try {
//獲取People class對象
Class peopleclass = Class.forName("com.example.javademo.reflect.People");
System.out.println("**********************獲取反射后類對象*********************************");
System.out.println("反射類對象: " + peopleclass);
//獲取所有構(gòu)造方法
System.out.println("**********************打印所有構(gòu)造方法*********************************");
Constructor[] conArray = peopleclass.getDeclaredConstructors();
for(Constructor c : conArray){
System.out.println(c);
}
System.out.println("**************第一種:通過反射調(diào)用默認(rèn)無參構(gòu)造方法并創(chuàng)建people類實(shí)例對象***************************");
//返回一個 Constructor對象, 該對象反映Constructor對象表示的類的指定的公共類函數(shù)。
Object constructorPublic = peopleclass.getConstructor();
System.out.println("無參構(gòu)造方法的對象:" + peopleclass.getConstructor());
//使用此 Constructor對象表示的構(gòu)造函數(shù),使用指定的初始化參數(shù)來創(chuàng)建和初始化構(gòu)造函數(shù)的聲明類的新實(shí)例。
//newInstance()方法內(nèi)部實(shí)際上調(diào)用了無參數(shù)構(gòu)造方法,必須保證無參構(gòu)造存在才可以
Object peopleobject1 = ((Constructor) constructorPublic).newInstance();
System.out.println("創(chuàng)建people1對象新實(shí)例: " + (People)peopleobject1);
System.out.println("**************第二種:通過反射調(diào)用public有參構(gòu)造方法并創(chuàng)建People類實(shí)例對象***************************");
Object peopleobject2 = peopleclass.getConstructor(String.class).newInstance("甲方");
System.out.println("創(chuàng)建people2對象新實(shí)例: " + (People)peopleobject2);
System.out.println("**************第三種:通過反射調(diào)用private有參構(gòu)造方法并創(chuàng)建People類實(shí)例對象***************************");
//注意,如果訪問protect private相關(guān)的構(gòu)造方法,需要用getDeclaredConstructor這個API
//返回一個 Constructor對象,該對象反映 Constructor對象表示的類或接口的指定類函數(shù)。
Constructor constructorPrivate = peopleclass.getDeclaredConstructor(int.class, String.class);
//訪問私有構(gòu)造方法,這句話一定要寫,不然就會報錯java.lang.IllegalAccessException
constructorPrivate.setAccessible(true);
System.out.println("有參private私有構(gòu)造方法的對象:" + peopleclass.getDeclaredConstructor(int.class, String.class));
Object peopleobject3 = constructorPrivate.newInstance(20, "men");
System.out.println("創(chuàng)建people3對象新實(shí)例: " + peopleobject3);
System.out.println("**************第四種:通過反射調(diào)用default有參構(gòu)造方法并創(chuàng)建People類實(shí)例對象***************************");
Constructor constructorDefault = peopleclass.getDeclaredConstructor(long.class);
//訪問public protect default構(gòu)造方法,這句話可以不用寫
//constructorDefault.setAccessible(true);
System.out.println("有參default構(gòu)造方法的對象:" + peopleclass.getDeclaredConstructor(long.class));
Object peopleobject4 = constructorDefault.newInstance(185);
System.out.println("創(chuàng)建people4對象新實(shí)例: " + peopleobject4);
} catch (ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}打印log:
**********************獲取反射后類對象*********************************
反射類對象: class com.example.javademo.reflect.People
**********************打印所有構(gòu)造方法*********************************
com.example.javademo.reflect.People(long)
private com.example.javademo.reflect.People(int,java.lang.String)
public com.example.javademo.reflect.People(java.lang.String)
public com.example.javademo.reflect.People()
**************第一種:通過反射調(diào)用默認(rèn)無參構(gòu)造方法并創(chuàng)建people類實(shí)例對象***************************
無參構(gòu)造方法的對象:public com.example.javademo.reflect.People()
調(diào)用了public無參構(gòu)造方法 執(zhí)行完畢
創(chuàng)建people1對象新實(shí)例: com.example.javademo.reflect.People@2a139a55
**************第二種:通過反射調(diào)用public有參構(gòu)造方法并創(chuàng)建People類實(shí)例對象***************************
調(diào)用public有參構(gòu)造方法 String:name = 甲方
創(chuàng)建people2對象新實(shí)例: com.example.javademo.reflect.People@15db9742
**************第三種:通過反射調(diào)用private有參構(gòu)造方法并創(chuàng)建People類實(shí)例對象***************************
有參private私有構(gòu)造方法的對象:private com.example.javademo.reflect.People(int,java.lang.String)
調(diào)用private有參數(shù)構(gòu)造方法 age :20 sex:men
創(chuàng)建people3對象新實(shí)例: com.example.javademo.reflect.People@6d06d69c
**************第四種:通過反射調(diào)用default有參構(gòu)造方法并創(chuàng)建People類實(shí)例對象***************************
有參default構(gòu)造方法的對象:com.example.javademo.reflect.People(long)
調(diào)用default有參數(shù)構(gòu)造方法 height :185
創(chuàng)建people4對象新實(shí)例: com.example.javademo.reflect.People@7852e922
通過上面的例子,總結(jié):
1. 反射Class對象, 反射Constructor對象, 反射類實(shí)例對象 是3個不同的對象,如果剛開始不熟悉API,建議分開創(chuàng)建;
2.getConstructor() 和 getDeclaredConstructor(類<?>... parameterTypes)方法的區(qū)別 前者是獲取public類型構(gòu)造方法的Constructor對象,后者是獲取全類型(public protect default private)類型的構(gòu)造方法的Constructor對象
3.反射私有構(gòu)造方法的時候,一定要setAccessible(true),不然會報錯java.lang.IllegalAccessException錯誤 * true表示:禁止java語言使用時安全檢查,暴力訪問
4. 調(diào)用Constructor類中newInstance()這個方法時,注意newInstance沒有帶參數(shù)(不帶參數(shù)可以不寫,或?qū)懗蒼ull 都可以行) 此方法內(nèi)部實(shí)際上調(diào)用了無參數(shù)構(gòu)造方法,必須保證無參構(gòu)造存在才可以,否則會拋出java.lang.InstantiationException異常。
3.3 反射調(diào)用類中方法
| 方法名 | 說明 |
| public Method[] getMethods() | 獲取所有"公有方法";(包含了父類的方法也包含Object類) |
| public Method getMethod(String name,Class<?>... parameterTypes) | 獲取指定參數(shù)的公共成員方法 類對象。 |
| public Method[] getDeclaredMethods() | 獲取所有的成員方法,包括私有的(不包括繼承的) |
| public Method getDeclaredMethod(String name,Class<?>... parameterTypes) | 獲取指定參數(shù)的方法對象 |
| public Object invoke(Object obj,Object... args) | 在具有指定參數(shù)的 方法對象上調(diào)用此 方法對象表示的底層方法。 |
我們看看這個例子
package com.example.javademo.reflect;
public class MethodTest {
public MethodTest() {
System.out.println("調(diào)用 MethodTest默認(rèn)的共有構(gòu)造方法");
}
String fruit = "蘋果";
int milliliter = 0;
char game = 'X';
String mood = "開心";
public void eat(String fruit) {
this.fruit = fruit;
System.out.println("調(diào)用public成員方法 eat 吃的水果為 :" + fruit);
}
int drink(int milliliter) {
this.milliliter = milliliter;
System.out.println("調(diào)用default成員方法 drink 喝水毫升量 :" + milliliter);
return milliliter;
}
protected void play(char game) {
this.game = game;
System.out.println("調(diào)用protected成員方法 play 玩游戲 :" + game);
}
private String happy(String mood) {
this.mood = mood;
System.out.println("調(diào)用private成員方法 happy 今日心情:" + mood);
return mood;
}
}反射測試類
package com.example.javademo.reflect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectMethod {
public static void main(String[] args) {
try {
System.out.println("**********************獲取反射后類對象*********************************");
//獲取方法類的class對象
Class methodClass = Class.forName("com.example.javademo.reflect.MethodTest");
System.out.println("反射類對象 :" + methodClass);
System.out.println("**********************獲取反射類中所有的成員方法**************************");
//返回一個"方法數(shù)組對象":反射類或接口中所有聲明的方法,包括公共,保護(hù),默認(rèn)(包)訪問和私有方法,但不包括繼承的方法。
Method[] methods = methodClass.getDeclaredMethods();
for (Method m : methods) {
System.out.println(m);
}
System.out.println("***************第一種*******調(diào)用反射類中public成員方法**************************");
//返回一個方法對象,它表示此表示的類或接口的指定聲明的方法對象。
//第一個參數(shù)為方法名,第二個可變參數(shù) : 為該方法傳入的參數(shù)
Method methodPublic = methodClass.getDeclaredMethod("eat", String.class);
//PUBLIC :1 PRIVATE:2 PROTECTED:4 PACKAGE(default):8
System.out.println("打印該public方法的修飾符 :" + methodPublic.getModifiers());
System.out.println("打印該public方法對象 表示方法的名稱: "+methodPublic.getName());
System.out.println("打印該public方法對象 可執(zhí)行文件的形式參數(shù)的數(shù)量 :" + methodPublic.getParameterCount());
//實(shí)例化一個MethodTest類對象,每個類有一個默認(rèn)的無參構(gòu)造方法,可以寫或不寫
//但是如果類中有帶參數(shù)的構(gòu)造方法,那無參構(gòu)造方法是必須要寫出來的,不然new對象的時候,就會報錯
MethodTest objMethodTest = (MethodTest)methodClass.getDeclaredConstructor().newInstance();
System.out.println("MethodTest類對象 :" + objMethodTest);
//invoke方法 第一個參數(shù): 從底層方法被調(diào)用的對象 如果底層方法是靜態(tài)的,則第一個參數(shù)obj對象傳遞null。
//第二個參數(shù): 該方法調(diào)用的參數(shù)
//如果方法正常完成,則返回的值將返回給調(diào)用者.
Object objectreturn1 = methodPublic.invoke(objMethodTest, "菠蘿");
//eat方法返回值為 null
System.out.println("eat方法 返回值為 :" + objectreturn1);
System.out.println("***************第二種*******調(diào)用反射類中private成員方法**************************");
Method methodPrivate = methodClass.getDeclaredMethod("happy", String.class);
System.out.println("打印該private方法的修飾符 :" + methodPrivate.getModifiers());
System.out.println("打印該private方法對象 表示方法的名稱: "+methodPrivate.getName());
System.out.println("打印該public方法對象 可執(zhí)行文件的形式參數(shù)的數(shù)量 :" + methodPrivate.getParameterCount());
//如果是訪問私有方法,則要加上這句話,禁止java語言使用時安全檢查
methodPrivate.setAccessible(true);
Object objectreturn2 = methodPrivate.invoke(objMethodTest, "超級nice");
System.out.println("happy方法 返回值為:"+objectreturn2);
System.out.println("***************第三種*******調(diào)用反射類中protected成員方法**************************");
Method methodProtected = methodClass.getDeclaredMethod("play", char.class);
System.out.println("打印該protected方法的修飾符 :" + methodProtected.getModifiers());
System.out.println("打印該protected方法對象 表示方法的名稱: "+methodProtected.getName());
System.out.println("打印該protected方法對象 可執(zhí)行文件的形式參數(shù)的數(shù)量 :" + methodProtected.getParameterCount());
Object objectreturn3 = methodProtected.invoke(objMethodTest, 'Y');
System.out.println("play方法 返回值為:"+objectreturn3);
System.out.println("***************第四種*******調(diào)用反射類中default成員方法**************************");
Method methodDefault = methodClass.getDeclaredMethod("drink", int.class);
System.out.println("打印該protected方法的修飾符 :" + methodDefault.getModifiers());
System.out.println("打印該protected方法對象 表示方法的名稱: "+methodDefault.getName());
System.out.println("打印該protected方法對象 可執(zhí)行文件的形式參數(shù)的數(shù)量 :" + methodDefault.getParameterCount());
Object objectreturn4 = methodDefault.invoke(objMethodTest, 180);
System.out.println("drink方法 返回值為:"+objectreturn4);
} catch (ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}打印log:
**********************獲取反射后類對象*********************************
反射類對象 :class com.example.javademo.reflect.MethodTest
**********************獲取反射類中所有的成員方法**************************
public void com.example.javademo.reflect.MethodTest.eat(java.lang.String)
private java.lang.String com.example.javademo.reflect.MethodTest.happy(java.lang.String)
protected void com.example.javademo.reflect.MethodTest.play(char)
int com.example.javademo.reflect.MethodTest.drink(int)
***************第一種*******調(diào)用反射類中public成員方法**************************
打印該public方法的修飾符 :1
打印該public方法對象 表示方法的名稱: eat
打印該public方法對象 可執(zhí)行文件的形式參數(shù)的數(shù)量 :1
調(diào)用 MethodTest默認(rèn)的共有構(gòu)造方法
MethodTest類對象 :com.example.javademo.reflect.MethodTest@15db9742
調(diào)用public成員方法 eat 吃的水果為 :菠蘿
eat方法 返回值為 :null
***************第二種*******調(diào)用反射類中private成員方法**************************
打印該private方法的修飾符 :2
打印該private方法對象 表示方法的名稱: happy
打印該public方法對象 可執(zhí)行文件的形式參數(shù)的數(shù)量 :1
調(diào)用private成員方法 happy 今日心情:超級nice
happy方法 返回值為:超級nice
***************第三種*******調(diào)用反射類中protected成員方法**************************
打印該protected方法的修飾符 :4
打印該protected方法對象 表示方法的名稱: play
打印該protected方法對象 可執(zhí)行文件的形式參數(shù)的數(shù)量 :1
調(diào)用protected成員方法 play 玩游戲 :Y
play方法 返回值為:null
***************第四種*******調(diào)用反射類中default成員方法**************************
打印該protected方法的修飾符 :0
打印該protected方法對象 表示方法的名稱: drink
打印該protected方法對象 可執(zhí)行文件的形式參數(shù)的數(shù)量 :1
調(diào)用default成員方法 drink 喝水毫升量 :180
drink方法 返回值為:180
代碼中基本都做了注釋,通過上面的例子,總結(jié):
1. 反射class對象, 反射Method對象 反射類實(shí)例對象 是3個不同的對象,要區(qū)分開
2. 在反射調(diào)用private方法時,一定要禁止java語言使用時安全檢查,要setAccessible(true),其他修飾符方法可以不用加這句話
3. (反射Method對象).invoke(Object obj, Object ...args) 方法參數(shù)的理解 :
1. 第一個參數(shù)為 通過用反射方法構(gòu)造出來的 反射類實(shí)例對象
2. 第二個可變參數(shù)為: 該方法需要傳入的參數(shù)
3. 如果方法正常完成,則將返回值返回
3.4 反射類中的成員變量
| 方法 | 說明 |
| Field[] getFields() | 獲取所有的"公有字段" |
| public Field getField(String fieldName) | 獲取指定參數(shù)的共有字段 |
| .Field[] getDeclaredFields() | 獲取所有字段,包括:私有、受保護(hù)、默認(rèn)、公有; |
| public Field getDeclaredField(String fieldName) | 獲取某個字段(包括私有的) |
| set(Object obj, Object value) | 將指定Field對象 設(shè)置為指定的新值 |
我們通過Demo講解:
package com.example.javademo.reflect;
/**
* desc : 供反射調(diào)用的FieldTest類
* version: 1.0
*/
public class FieldTest {
public FieldTest() {
System.out.println("調(diào)用FieldTest 默認(rèn)無參構(gòu)造方法");
}
public int age = 18;
private String name = "張三";
String sex = "男";
protected String phoneNum = "123456789";
public int getAge() {
return age;
}
public String getName() {
return name;
}
public String getSex() {
return sex;
}
public String getPhoneNum() {
return phoneNum;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setSex(String sex) {
this.sex = sex;
}
public void setPhoneNum(String phoneNum) {
this.phoneNum = phoneNum;
}
@Override
public String toString() {
return "FieldTest{" +
"age=" + age +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
", phoneNum='" + phoneNum + '\'' +
'}';
}反射測試類:
package com.example.javademo.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* desc : 反射FiledTest類中各字段(Field)的實(shí)現(xiàn)類
* version: 1.0
*/
public class ReflectField {
public static void main(String[] args) {
//用正常的new對象的方式,給字段賦值,打印對象值, 測試用
/* FieldTest objcetFieldTest = new FieldTest();
objcetFieldTest.setAge(15);
objcetFieldTest.setName("xiaomao");
objcetFieldTest.setPhoneNum("12345678");
objcetFieldTest.setSex("男");
System.out.println(objcetFieldTest.toString());*/
//通過反射的方式,修改字段值
try {
//通過反射獲取Class對象
Class FieldTestClass = Class.forName("com.example.javademo.reflect.FieldTest");
//用反射的方式來修改字段(成員變量)的值
System.out.println("************獲取所有的字段(包括共有、私有、受保護(hù)、默認(rèn)的)********************");
Field[] fields = FieldTestClass.getDeclaredFields();
for (Field f : fields) {
System.out.println(f);
}
//用反射的方式來構(gòu)造FieldTestl類對象
FieldTest objFieldTest = (FieldTest) FieldTestClass.getDeclaredConstructor().newInstance();
System.out.println("*************反射獲取public字段并調(diào)用***********************************");
//返回一個 Field對象,它表示的類或接口的指定已聲明字段對象。
Field fieldPublic = FieldTestClass.getDeclaredField("age");
System.out.println("該字段的修飾符 :" + fieldPublic.getModifiers());
System.out.println("該字段的名稱 :" + fieldPublic.getName());
/*
* 獲取字段的值:有 getInt(Object obj) getLong(Object obj) get(Object obj)等方法
*
* obj :為通過反射方法 構(gòu)造出來的類對象
* */
//獲取int類型 實(shí)例字段的默認(rèn)值
System.out.println("通過反射方式獲取age默認(rèn)值 : " + fieldPublic.getInt(objFieldTest));
/* 設(shè)置字段的值:
Field public void set(Object obj,Object value):
參數(shù)說明:
1.obj: 通過反射方法 構(gòu)造出來的類對象
2.value:要為字段設(shè)置的值;
*/
fieldPublic.set(objFieldTest, 20);
//通過反射的方式調(diào)用 getAge() 方法
Method method = FieldTestClass.getDeclaredMethod("getAge");
Object objectReturn = method.invoke(objFieldTest);
System.out.println("通過反射方式set新值之后, age的值 :" + objectReturn);
System.out.println("*************反射獲取private字段并調(diào)用***********************************");
//在項(xiàng)目實(shí)際中,因?yàn)樗接凶兞亢头椒ǎ瑹o法在外部類去調(diào)用它們,所以反射在這點(diǎn)上就派上用場了
Field fieldPrivate = FieldTestClass.getDeclaredField("name");
//private字段一定要調(diào)用setAccessible(true) 禁止java語言使用時安全檢查
fieldPrivate.setAccessible(true);
System.out.println("通過反射方式獲取age默認(rèn)值 : " + fieldPrivate.get(objFieldTest));
//修改private變量name的值
fieldPrivate.set(objFieldTest, "李四");
//通過反射的方式調(diào)用 getName() 方法
Method objectMethod = FieldTestClass.getDeclaredMethod("getName");
Object objectReturn1 = objectMethod.invoke(objFieldTest);
System.out.println("通過反射方式set新值之后, name的值 :" + objectReturn1);
} catch (ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}打印log:
************獲取所有的字段(包括共有、私有、受保護(hù)、默認(rèn)的)********************
public int com.example.javademo.reflect.FieldTest.age
private java.lang.String com.example.javademo.reflect.FieldTest.name
java.lang.String com.example.javademo.reflect.FieldTest.sex
protected java.lang.String com.example.javademo.reflect.FieldTest.phoneNum
調(diào)用FieldTest 默認(rèn)無參構(gòu)造方法
*************反射獲取public字段并調(diào)用***********************************
該字段的修飾符 :1
該字段的名稱 :age
通過反射方式獲取age默認(rèn)值 : 18
通過反射方式set新值之后, age的值 :20
*************反射獲取private字段并調(diào)用***********************************
通過反射方式獲取age默認(rèn)值 : 張三
通過反射方式set新值之后, name的值 :李四
通過例子,總結(jié)如下:
1. 反射class對象, 反射Field對象 反射類實(shí)例對象 是3個不同的對象,要區(qū)分開
2.在反射調(diào)用private字段時,一定要禁止java語言使用時安全檢查,要setAccessible(true)
3. Field public void set(Object obj,Object value):
參數(shù)說明:
1.obj: 通過反射方法 構(gòu)造出來的類對象
2.value:要為字段設(shè)置的值;
3.5 反射類中靜態(tài)方法和變量
怎么修改static修飾的變量呢?靜態(tài)變量和方法是在類的實(shí)例化之前就進(jìn)行了初始化(類的初始化階段),靜態(tài)變量和方法是從屬于類本身的,跟new出來的具體對象無關(guān),所以我們獲取變量就不需要傳入對象,直接傳入null即可。
Demo如下:
package com.example.javademo.reflect;
public class StaticFieldTest {
public StaticFieldTest() {
System.out.println("調(diào)用 StaticFieldTest 無參構(gòu)造方法");
}
//靜態(tài)變量
public static int age = 15;
public static void setAge(int age) {
StaticFieldTest.age = age;
}
//靜態(tài)方法
public static int getAge() {
return age;
}
}反射測試類:
package com.example.javademo.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectStatic {
public static void main(String[] args) {
try {
//獲取 StaticFieldTest class對象
Class staticClazz = Class.forName("com.example.javademo.reflect.StaticFieldTest");
//獲取靜態(tài)Field對象
Field staticField = staticClazz.getDeclaredField("age");
//因?yàn)槭莗ulic修飾符,這句話也可以不用寫
staticField.setAccessible(true);
//通過set方法,修改值, 靜態(tài)變量 是從屬于類,可以不用傳入類實(shí)例對象,直接傳入null
staticField.set(null, 25);
//驗(yàn)證修改后的值
Method agetet = staticClazz.getDeclaredMethod("getAge");
// 靜態(tài)方法 是從屬于類, 可以不用傳入類實(shí)例對象,直接傳入null
int agetest = (int) agetet.invoke(null);
System.out.println("**********************打印修改后的age值**************************");
System.out.println(agetest);
} catch (ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}打印結(jié)果:
**********************打印修改后的age值**************************
25
總結(jié)如下:
1. 對于static 變量和方法,因?yàn)樗菑膶儆陬惐旧?,在類的?shí)例化之前就進(jìn)行了初始化,當(dāng)傳入obj對象參數(shù)的時候,直接傳入null即可。
4. 反射在Android中的應(yīng)用
4.1反射實(shí)現(xiàn)獲取頂層activity的名稱
如前言背景中的問題描述,有了上面的理論知識做為基礎(chǔ),實(shí)現(xiàn)代碼如下:
/**
* @description: 通過反射方式獲取 頂層activity名稱
* @param: null
* @return: boolean 如果是目標(biāo)activity 則返回true
*/
public boolean isTopTargetActivity() {
try {
//通過包名和類名反射獲取 class 對象
Class activityThreadClass = Class.forName("android.app.ActivityThread");
//第二種: 反射創(chuàng)建 activiyThread對象 反射調(diào)用靜態(tài)方法,第一個參數(shù)obj對象傳遞 null
Object activityThreadObj = activityThreadClass.getMethod("currentActivityThread").invoke(null);
Log.e("test", "====activityThreadObj: "+activityThreadObj);
//獲取 mActivities Field對象 final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
Field activitiesField = activityThreadClass.getDeclaredField("mActivities");
//禁止Java語言訪問使用時進(jìn)行檢查
activitiesField.setAccessible(true);
//返回該所表示的字段的值 Field
Map activities = (Map) activitiesField.get(activityThreadObj);
for (Object activityClientRecord : activities.values()) {
//獲取 ActivityClientRecord class對象
Class activityRecordClass = activityClientRecord.getClass();
//獲取 boolean paused 字段 對象
Field pausedField = activityRecordClass.getDeclaredField("paused");
//允許暴力訪問,禁止java語言運(yùn)行時安全檢查
pausedField.setAccessible(true);
//Activity onResume的判斷條件
if (!pausedField.getBoolean(activityClientRecord)) {
//獲取 Activity activity field對象
Field activityField = activityRecordClass.getDeclaredField("activity");
activityField.setAccessible(true);
//獲取當(dāng)前顯示的activity的對象
Activity activity = (Activity) activityField.get(activityClientRecord);
//獲取當(dāng)前activity的名稱
String className = activity.getClass().toString();
Log.e("test", "====通過反射獲取的className===="+className);
if ("cn.com.test.activity.MainActivity".equals(className)) {
return true;
}
}
}
} catch (ClassNotFoundException e) {
Log.e("test", "======ClassNotFoundException===="+e.getMessage());
} catch (InvocationTargetException e) {
Log.e("test", "======InvocationTargetException===="+e.getMessage());
} catch (NoSuchMethodException e) {
Log.e("test", "======NoSuchMethodException===="+e.getMessage());
} catch (NoSuchFieldException e) {
Log.e("test", "======NoSuchFieldException===="+e.getMessage());
} catch (IllegalAccessException e) {
Log.e("test", "======IllegalAccessException===="+e.getMessage());
}
return false;
}4.2 反射調(diào)用SystemProperties中set get方法
再者,比如第三方應(yīng)用是無法直接調(diào)用SystemProperties中 get set 方法,我們也可以通過反射來實(shí)現(xiàn), 這個工具類已經(jīng)實(shí)現(xiàn)好,可以拿去直接用:
public class SystemPropertiesUtils {
private static final String TAG = "SystemPropertiesUtils";
private static Class<?> mClassType = null;
private static Method mGetMethod = null;
private static Method mGetIntMethod = null;
private static Method mGetBooleanMethod = null;
private static Class<?> getSystemPropertiesClass() throws ClassNotFoundException {
if (mClassType == null) {
mClassType = Class.forName("android.os.SystemProperties");
}
return mClassType;
}
private static Method getMethod() throws Exception {
if (mGetMethod == null) {
Class clazz = getSystemPropertiesClass();
mGetMethod = clazz.getDeclaredMethod("get", String.class);
}
return mGetMethod;
}
private static Method getIntMethod() throws Exception {
if (mGetIntMethod == null) {
Class clazz = getSystemPropertiesClass();
mGetIntMethod = clazz.getDeclaredMethod("getInt", String.class, int.class);
}
return mGetIntMethod;
}
private static Method getBooleanMethod() throws Exception {
if (mGetBooleanMethod == null) {
Class clazz = getSystemPropertiesClass();
mGetBooleanMethod = clazz.getDeclaredMethod("getBoolean", String.class, boolean.class);
}
return mGetBooleanMethod;
}
public static String get(String key, String def) {
try {
String value = (String) getMethod().invoke(null, key);
if (!TextUtils.isEmpty(value)) {
return value;
}
} catch (Exception e) {
Log.d(TAG, "Unable to read system properties");
}
return def;
}
public static int getInt(String key, int def) {
int value = def;
try {
value = (int) getIntMethod().invoke(null, key, def);
} catch (Exception e) {
Log.d(TAG, "Unable to read system properties");
}
return value;
}
public static boolean getBoolean(String key, boolean def) {
boolean value = def;
try {
value = (Boolean) getBooleanMethod().invoke(null, key, def);
} catch (Exception e) {
Log.d(TAG, "Unable to read system properties");
}
return value;
}
}4.3 通過反射創(chuàng)建Bitmap縮略圖
public static Bitmap createVideoThumbnail(String filePath) {
// MediaMetadataRetriever is available on API Level 8
// but is hidden until API Level 10
Class<?> clazz = null;
Object instance = null;
try {
clazz = Class.forName("android.media.MediaMetadataRetriever");
instance = clazz.newInstance();
Method method = clazz.getMethod("setDataSource", String.class);
method.invoke(instance, filePath);
// The method name changes between API Level 9 and 10.
if (Build.VERSION.SDK_INT <= 9) {
return (Bitmap) clazz.getMethod("captureFrame").invoke(instance);
} else {
byte[] data = (byte[]) clazz.getMethod("getEmbeddedPicture").invoke(instance);
if (data != null) {
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
if (bitmap != null) return bitmap;
}
return (Bitmap) clazz.getMethod("getFrameAtTime").invoke(instance);
}
} catch (IllegalArgumentException ex) {
// Assume this is a corrupt video file
} catch (RuntimeException ex) {
// Assume this is a corrupt video file.
} catch (InstantiationException e) {
Log.e(TAG, "createVideoThumbnail", e);
} catch (InvocationTargetException e) {
Log.e(TAG, "createVideoThumbnail", e);
} catch (ClassNotFoundException e) {
Log.e(TAG, "createVideoThumbnail", e);
} catch (NoSuchMethodException e) {
Log.e(TAG, "createVideoThumbnail", e);
} catch (IllegalAccessException e) {
Log.e(TAG, "createVideoThumbnail", e);
} finally {
try {
if (instance != null) {
clazz.getMethod("release").invoke(instance);
}
} catch (Exception ignored) {
}
}
return null;
}MediaMetadataRetriever.java 類中有個public無參構(gòu)造方法, 可以通過反射方式clazz.getDeclaredConstructor().newInstance() 拿到這個 類對象
接下來,就是獲取Method Field 對象 再通過invoke set 方法去修改方法 變量的值。
5. 總結(jié)
反射在Andorid中開發(fā)應(yīng)用的比較多,Class對象, Constructor 對象,Method 對象 Field對象各自的常用API要理解并熟練運(yùn)用, 結(jié)合源碼多閱讀多仿寫,相信你也可以carry住反射。
到此這篇關(guān)于詳解Andorid開發(fā)中反射機(jī)制是怎么一回事的文章就介紹到這了,更多相關(guān)Andorid反射機(jī)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android中Fragment的基本用法示例總結(jié)
Fragment是activity的界面中的一部分或一種行為,下面這篇文章主要給大家介紹了關(guān)于Android中Fragment的基本用法的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2018-05-05
Android 無障礙全局懸浮窗實(shí)現(xiàn)示例
本文主要介紹了Android 無障礙全局懸浮窗實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
Android使用TypeFace設(shè)置TextView的文字字體
這篇文章主要介紹了Android使用TypeFace設(shè)置TextView的文字字體的方法,幫助大家更好的利用Android開發(fā),感興趣的朋友可以了解下2021-01-01
詳解Android_性能優(yōu)化之ViewPager加載成百上千高清大圖oom解決方案
這篇文章主要介紹了詳解Android_性能優(yōu)化之ViewPager加載成百上千高清大圖oom解決方案,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2016-12-12
Android應(yīng)用內(nèi)懸浮窗的實(shí)現(xiàn)方案示例
本篇文章主要介紹了Android應(yīng)用內(nèi)懸浮窗的實(shí)現(xiàn)方案示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08
Android view自定義實(shí)現(xiàn)動態(tài)進(jìn)度條
這篇文章主要介紹了Android view自定義實(shí)現(xiàn)動態(tài)進(jìn)度條的相關(guān)資料,這里提供實(shí)例代碼及實(shí)現(xiàn)效果圖,需要的朋友可以參考下2016-12-12
Android 中TextView中跑馬燈效果的實(shí)現(xiàn)方法
這篇文章主要介紹了Android 中TextView中跑馬燈效果的實(shí)現(xiàn)方法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-02-02

