詳解Andorid開發(fā)中反射機(jī)制是怎么一回事
1. 背景
在andorid開發(fā)中,經(jīng)常遇見在某些工具類中沒有Context上下文對(duì)象時(shí),一些系統(tǒng)服務(wù)的代理對(duì)象無法創(chuàng)建出來,舉個(gè)例子:比如在源碼(framework/base/graphics/java/android/graphics)路徑下的Canvas.java Bitmap.javaPicture.java Paint.java 類就沒有上下文對(duì)象。當(dāng)時(shí)有需要調(diào)用AMS的API獲取當(dāng)前界面activity的名稱作為判斷條件去修改這些工具類中的參數(shù),但是這幾個(gè)工具類中又沒有上下文對(duì)象,怎么辦呢?
一般情況下,獲取頂層activity名稱的方法如下:
//1. 獲取 AMS 的代理對(duì)象 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();上述代碼是在有上下文對(duì)象context的情況下,直接獲取。
而在上面提及到的Picture.java等類中, 根本就沒有上下文,怎么搞? 這個(gè)時(shí)候我們通過java反射來實(shí)現(xiàn),接下來,本篇文章就來講解一下java反射機(jī)制,并利用反射實(shí)現(xiàn)獲取頂層activity。
2. java反射
2.1 什么是反射
反射(Reflection)是程序的自我分析能力,通過反射可以確定類中有哪些方法、有哪些構(gòu)造方法以及有哪些成員變量,在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意方法和屬性;這種動(dòng)態(tài)獲取信息以及動(dòng)態(tài)調(diào)用對(duì)象方法的功能稱為java語言的反射機(jī)制。
2.2 什么情況下要用反射
首先java是面向?qū)ο缶幊痰?,萬物皆對(duì)象, 當(dāng)我們拿到這個(gè)類對(duì)象后,就可以訪問類中的成員變量,調(diào)用類中的方法了,但是呢?對(duì)于private變量和方法,它的訪問權(quán)限作用域只在本類中,在外部類或者繼承類中,就算你創(chuàng)建了該類對(duì)象,也是無法直接調(diào)用的。來看下這個(gè)例子:
普通的一個(gè)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)建了對(duì)象,則所有的方法和變量都可以直接訪問
User user = new User();
System.out.println(user.name); //訪問私有變量
System.out.println(user.test());//調(diào)用私有方法
}
}打印結(jié)果如下:
墨子
私有成員方法
1
如果在外部?jī)?nèi)中創(chuàng)建該類對(duì)象,還能直接訪問私有方法和變量嗎? 測(cè)試一下:
public class PrivateTest {
public static void main(String[] args) {
User user = new User();
//外部?jī)?nèi)中創(chuàng)建的對(duì)象直接私有變量 報(bào)錯(cuò)
//System.out.println(user.name);
//外部?jī)?nèi)中創(chuàng)建的對(duì)象,直接調(diào)用private方法,也報(bào)錯(cuò)
//System.out.println(user.test());
//但是可以直接訪問 protected public的方法和變量
System.out.println(user.age);
System.out.println(user.test2());
}
}報(bào)錯(cuò)的兩行代碼注釋掉了,可以把代碼復(fù)制過去驗(yàn)證一下。
當(dāng)然了,在andorid開發(fā)中,還有其他場(chǎng)景會(huì)使用到反射, 很多類或方法中經(jīng)常加上了“@hide”注釋標(biāo)記,這些API是不允許在第三方應(yīng)用直接調(diào)用的比如:SystemProperties類在android.os下,但這個(gè)類是隱藏的,沒有系統(tǒng)權(quán)限(android:sharedUserId="android.uid.system")的app無法直接使用,只能通過java反射來調(diào)用。
還有 Andorid中動(dòng)態(tài)代理模式 , Android插件化(Hook)這些都會(huì)用到反射,這里不一一說明,因?yàn)槊總€(gè)技術(shù)點(diǎn)都需要去深挖才能理解透徹。
2.3 反射的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
1.可以調(diào)用私有方法、變量
2.可以調(diào)用隱藏api接口
3.能夠運(yùn)行時(shí)動(dòng)態(tài)獲取類的實(shí)例,提高代碼靈活性
缺點(diǎn):
1.安全問題:反射可以獲取到類對(duì)應(yīng)的所有方法和屬性,如果存在外部調(diào)用的情況,可能出現(xiàn)超出預(yù)期的調(diào)用,從而導(dǎo)致安全風(fēng)險(xiǎn), 而且也破壞java面向?qū)ο缶幊痰姆庋b這一特性。
2.性能問題:無論是通過字符串獲取Class、Method還是Field,都需要JVM的動(dòng)態(tài)鏈接機(jī)制動(dòng)態(tài)的進(jìn)行解析和匹配,勢(shì)必造成性能開銷。每一次的反射調(diào)用都會(huì)造成Java安全機(jī)制進(jìn)行額外的安全性驗(yàn)證,造成性能開銷。反射代碼使得許多JVM的運(yùn)行時(shí)優(yōu)化無法進(jìn)行。
3. Java反射機(jī)制API
Java反射機(jī)制API主要是 java.lang.Class類 和 java.lang.reflect包。
| 類 | 說明 |
| java.lang.Class | 代表整個(gè)字節(jié)碼。代表一個(gè)類型,代表整個(gè)類。 |
| 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對(duì)象
| 方式 | 說明 |
| 對(duì)象.getClass(); | |
| 類名.class | |
| Class.forName(“類的全路徑”); | 推薦此種寫法 |
package com.example.javademo.reflect;
public class ClassObject {
public static void main(String[] args) throws ClassNotFoundException {
//第一種: 對(duì)象.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出對(duì)象,然后通過對(duì)象獲取class對(duì)象, 但是你想想類對(duì)象都可以直接new出來,在外部?jī)?nèi)中除了private 變量和方法不能直接訪問外,都可以直接調(diào)用,我還反射干嘛?寫那么多代碼豈不是為難自己,沒事找事,不推薦!
第二種:通過類名.class 獲取字節(jié)碼class對(duì)象,需要導(dǎo)入包,依賴性比較強(qiáng),不然會(huì)編譯報(bào)錯(cuò)
第三種: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
三個(gè)都是同一個(gè)Class對(duì)象,在運(yùn)行期間,一個(gè)類,只有一個(gè)Class對(duì)象產(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 對(duì)象表示的構(gòu)造方法來創(chuàng)建該構(gòu)造方法的聲明類的新實(shí)例,并用指定的初始化參數(shù)初始化該實(shí)例。 它的返回值是T類型,所以newInstance是創(chuàng)建了一個(gè)構(gòu)造方法的聲名類的新實(shí)例對(duì)象,相當(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 );
}
}反射測(cè)試類:
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對(duì)象
Class peopleclass = Class.forName("com.example.javademo.reflect.People");
System.out.println("**********************獲取反射后類對(duì)象*********************************");
System.out.println("反射類對(duì)象: " + 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í)例對(duì)象***************************");
//返回一個(gè) Constructor對(duì)象, 該對(duì)象反映Constructor對(duì)象表示的類的指定的公共類函數(shù)。
Object constructorPublic = peopleclass.getConstructor();
System.out.println("無參構(gòu)造方法的對(duì)象:" + peopleclass.getConstructor());
//使用此 Constructor對(duì)象表示的構(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對(duì)象新實(shí)例: " + (People)peopleobject1);
System.out.println("**************第二種:通過反射調(diào)用public有參構(gòu)造方法并創(chuàng)建People類實(shí)例對(duì)象***************************");
Object peopleobject2 = peopleclass.getConstructor(String.class).newInstance("甲方");
System.out.println("創(chuàng)建people2對(duì)象新實(shí)例: " + (People)peopleobject2);
System.out.println("**************第三種:通過反射調(diào)用private有參構(gòu)造方法并創(chuàng)建People類實(shí)例對(duì)象***************************");
//注意,如果訪問protect private相關(guān)的構(gòu)造方法,需要用getDeclaredConstructor這個(gè)API
//返回一個(gè) Constructor對(duì)象,該對(duì)象反映 Constructor對(duì)象表示的類或接口的指定類函數(shù)。
Constructor constructorPrivate = peopleclass.getDeclaredConstructor(int.class, String.class);
//訪問私有構(gòu)造方法,這句話一定要寫,不然就會(huì)報(bào)錯(cuò)java.lang.IllegalAccessException
constructorPrivate.setAccessible(true);
System.out.println("有參private私有構(gòu)造方法的對(duì)象:" + peopleclass.getDeclaredConstructor(int.class, String.class));
Object peopleobject3 = constructorPrivate.newInstance(20, "men");
System.out.println("創(chuàng)建people3對(duì)象新實(shí)例: " + peopleobject3);
System.out.println("**************第四種:通過反射調(diào)用default有參構(gòu)造方法并創(chuàng)建People類實(shí)例對(duì)象***************************");
Constructor constructorDefault = peopleclass.getDeclaredConstructor(long.class);
//訪問public protect default構(gòu)造方法,這句話可以不用寫
//constructorDefault.setAccessible(true);
System.out.println("有參default構(gòu)造方法的對(duì)象:" + peopleclass.getDeclaredConstructor(long.class));
Object peopleobject4 = constructorDefault.newInstance(185);
System.out.println("創(chuàng)建people4對(duì)象新實(shí)例: " + peopleobject4);
} catch (ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}打印log:
**********************獲取反射后類對(duì)象*********************************
反射類對(duì)象: 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í)例對(duì)象***************************
無參構(gòu)造方法的對(duì)象:public com.example.javademo.reflect.People()
調(diào)用了public無參構(gòu)造方法 執(zhí)行完畢
創(chuàng)建people1對(duì)象新實(shí)例: com.example.javademo.reflect.People@2a139a55
**************第二種:通過反射調(diào)用public有參構(gòu)造方法并創(chuàng)建People類實(shí)例對(duì)象***************************
調(diào)用public有參構(gòu)造方法 String:name = 甲方
創(chuàng)建people2對(duì)象新實(shí)例: com.example.javademo.reflect.People@15db9742
**************第三種:通過反射調(diào)用private有參構(gòu)造方法并創(chuàng)建People類實(shí)例對(duì)象***************************
有參private私有構(gòu)造方法的對(duì)象:private com.example.javademo.reflect.People(int,java.lang.String)
調(diào)用private有參數(shù)構(gòu)造方法 age :20 sex:men
創(chuàng)建people3對(duì)象新實(shí)例: com.example.javademo.reflect.People@6d06d69c
**************第四種:通過反射調(diào)用default有參構(gòu)造方法并創(chuàng)建People類實(shí)例對(duì)象***************************
有參default構(gòu)造方法的對(duì)象:com.example.javademo.reflect.People(long)
調(diào)用default有參數(shù)構(gòu)造方法 height :185
創(chuàng)建people4對(duì)象新實(shí)例: com.example.javademo.reflect.People@7852e922
通過上面的例子,總結(jié):
1. 反射Class對(duì)象, 反射Constructor對(duì)象, 反射類實(shí)例對(duì)象 是3個(gè)不同的對(duì)象,如果剛開始不熟悉API,建議分開創(chuàng)建;
2.getConstructor() 和 getDeclaredConstructor(類<?>... parameterTypes)方法的區(qū)別 前者是獲取public類型構(gòu)造方法的Constructor對(duì)象,后者是獲取全類型(public protect default private)類型的構(gòu)造方法的Constructor對(duì)象
3.反射私有構(gòu)造方法的時(shí)候,一定要setAccessible(true),不然會(huì)報(bào)錯(cuò)java.lang.IllegalAccessException錯(cuò)誤 * true表示:禁止java語言使用時(shí)安全檢查,暴力訪問
4. 調(diào)用Constructor類中newInstance()這個(gè)方法時(shí),注意newInstance沒有帶參數(shù)(不帶參數(shù)可以不寫,或?qū)懗蒼ull 都可以行) 此方法內(nèi)部實(shí)際上調(diào)用了無參數(shù)構(gòu)造方法,必須保證無參構(gòu)造存在才可以,否則會(huì)拋出java.lang.InstantiationException異常。
3.3 反射調(diào)用類中方法
| 方法名 | 說明 |
| public Method[] getMethods() | 獲取所有"公有方法";(包含了父類的方法也包含Object類) |
| public Method getMethod(String name,Class<?>... parameterTypes) | 獲取指定參數(shù)的公共成員方法 類對(duì)象。 |
| public Method[] getDeclaredMethods() | 獲取所有的成員方法,包括私有的(不包括繼承的) |
| public Method getDeclaredMethod(String name,Class<?>... parameterTypes) | 獲取指定參數(shù)的方法對(duì)象 |
| public Object invoke(Object obj,Object... args) | 在具有指定參數(shù)的 方法對(duì)象上調(diào)用此 方法對(duì)象表示的底層方法。 |
我們看看這個(gè)例子
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;
}
}反射測(cè)試類
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("**********************獲取反射后類對(duì)象*********************************");
//獲取方法類的class對(duì)象
Class methodClass = Class.forName("com.example.javademo.reflect.MethodTest");
System.out.println("反射類對(duì)象 :" + methodClass);
System.out.println("**********************獲取反射類中所有的成員方法**************************");
//返回一個(gè)"方法數(shù)組對(duì)象":反射類或接口中所有聲明的方法,包括公共,保護(hù),默認(rèn)(包)訪問和私有方法,但不包括繼承的方法。
Method[] methods = methodClass.getDeclaredMethods();
for (Method m : methods) {
System.out.println(m);
}
System.out.println("***************第一種*******調(diào)用反射類中public成員方法**************************");
//返回一個(gè)方法對(duì)象,它表示此表示的類或接口的指定聲明的方法對(duì)象。
//第一個(gè)參數(shù)為方法名,第二個(gè)可變參數(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方法對(duì)象 表示方法的名稱: "+methodPublic.getName());
System.out.println("打印該public方法對(duì)象 可執(zhí)行文件的形式參數(shù)的數(shù)量 :" + methodPublic.getParameterCount());
//實(shí)例化一個(gè)MethodTest類對(duì)象,每個(gè)類有一個(gè)默認(rèn)的無參構(gòu)造方法,可以寫或不寫
//但是如果類中有帶參數(shù)的構(gòu)造方法,那無參構(gòu)造方法是必須要寫出來的,不然new對(duì)象的時(shí)候,就會(huì)報(bào)錯(cuò)
MethodTest objMethodTest = (MethodTest)methodClass.getDeclaredConstructor().newInstance();
System.out.println("MethodTest類對(duì)象 :" + objMethodTest);
//invoke方法 第一個(gè)參數(shù): 從底層方法被調(diào)用的對(duì)象 如果底層方法是靜態(tài)的,則第一個(gè)參數(shù)obj對(duì)象傳遞null。
//第二個(gè)參數(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方法對(duì)象 表示方法的名稱: "+methodPrivate.getName());
System.out.println("打印該public方法對(duì)象 可執(zhí)行文件的形式參數(shù)的數(shù)量 :" + methodPrivate.getParameterCount());
//如果是訪問私有方法,則要加上這句話,禁止java語言使用時(shí)安全檢查
methodPrivate.setAccessible(true);
Object objectreturn2 = methodPrivate.invoke(objMethodTest, "超級(jí)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方法對(duì)象 表示方法的名稱: "+methodProtected.getName());
System.out.println("打印該protected方法對(duì)象 可執(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方法對(duì)象 表示方法的名稱: "+methodDefault.getName());
System.out.println("打印該protected方法對(duì)象 可執(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:
**********************獲取反射后類對(duì)象*********************************
反射類對(duì)象 :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方法對(duì)象 表示方法的名稱: eat
打印該public方法對(duì)象 可執(zhí)行文件的形式參數(shù)的數(shù)量 :1
調(diào)用 MethodTest默認(rèn)的共有構(gòu)造方法
MethodTest類對(duì)象 :com.example.javademo.reflect.MethodTest@15db9742
調(diào)用public成員方法 eat 吃的水果為 :菠蘿
eat方法 返回值為 :null
***************第二種*******調(diào)用反射類中private成員方法**************************
打印該private方法的修飾符 :2
打印該private方法對(duì)象 表示方法的名稱: happy
打印該public方法對(duì)象 可執(zhí)行文件的形式參數(shù)的數(shù)量 :1
調(diào)用private成員方法 happy 今日心情:超級(jí)nice
happy方法 返回值為:超級(jí)nice
***************第三種*******調(diào)用反射類中protected成員方法**************************
打印該protected方法的修飾符 :4
打印該protected方法對(duì)象 表示方法的名稱: play
打印該protected方法對(duì)象 可執(zhí)行文件的形式參數(shù)的數(shù)量 :1
調(diào)用protected成員方法 play 玩游戲 :Y
play方法 返回值為:null
***************第四種*******調(diào)用反射類中default成員方法**************************
打印該protected方法的修飾符 :0
打印該protected方法對(duì)象 表示方法的名稱: drink
打印該protected方法對(duì)象 可執(zhí)行文件的形式參數(shù)的數(shù)量 :1
調(diào)用default成員方法 drink 喝水毫升量 :180
drink方法 返回值為:180
代碼中基本都做了注釋,通過上面的例子,總結(jié):
1. 反射class對(duì)象, 反射Method對(duì)象 反射類實(shí)例對(duì)象 是3個(gè)不同的對(duì)象,要區(qū)分開
2. 在反射調(diào)用private方法時(shí),一定要禁止java語言使用時(shí)安全檢查,要setAccessible(true),其他修飾符方法可以不用加這句話
3. (反射Method對(duì)象).invoke(Object obj, Object ...args) 方法參數(shù)的理解 :
1. 第一個(gè)參數(shù)為 通過用反射方法構(gòu)造出來的 反射類實(shí)例對(duì)象
2. 第二個(gè)可變參數(shù)為: 該方法需要傳入的參數(shù)
3. 如果方法正常完成,則將返回值返回
3.4 反射類中的成員變量
| 方法 | 說明 |
| Field[] getFields() | 獲取所有的"公有字段" |
| public Field getField(String fieldName) | 獲取指定參數(shù)的共有字段 |
| .Field[] getDeclaredFields() | 獲取所有字段,包括:私有、受保護(hù)、默認(rèn)、公有; |
| public Field getDeclaredField(String fieldName) | 獲取某個(gè)字段(包括私有的) |
| set(Object obj, Object value) | 將指定Field對(duì)象 設(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 + '\'' +
'}';
}反射測(cè)試類:
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對(duì)象的方式,給字段賦值,打印對(duì)象值, 測(cè)試用
/* FieldTest objcetFieldTest = new FieldTest();
objcetFieldTest.setAge(15);
objcetFieldTest.setName("xiaomao");
objcetFieldTest.setPhoneNum("12345678");
objcetFieldTest.setSex("男");
System.out.println(objcetFieldTest.toString());*/
//通過反射的方式,修改字段值
try {
//通過反射獲取Class對(duì)象
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類對(duì)象
FieldTest objFieldTest = (FieldTest) FieldTestClass.getDeclaredConstructor().newInstance();
System.out.println("*************反射獲取public字段并調(diào)用***********************************");
//返回一個(gè) Field對(duì)象,它表示的類或接口的指定已聲明字段對(duì)象。
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)造出來的類對(duì)象
* */
//獲取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)造出來的類對(duì)象
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)上就派上用場(chǎng)了
Field fieldPrivate = FieldTestClass.getDeclaredField("name");
//private字段一定要調(diào)用setAccessible(true) 禁止java語言使用時(shí)安全檢查
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對(duì)象, 反射Field對(duì)象 反射類實(shí)例對(duì)象 是3個(gè)不同的對(duì)象,要區(qū)分開
2.在反射調(diào)用private字段時(shí),一定要禁止java語言使用時(shí)安全檢查,要setAccessible(true)
3. Field public void set(Object obj,Object value):
參數(shù)說明:
1.obj: 通過反射方法 構(gòu)造出來的類對(duì)象
2.value:要為字段設(shè)置的值;
3.5 反射類中靜態(tài)方法和變量
怎么修改static修飾的變量呢?靜態(tài)變量和方法是在類的實(shí)例化之前就進(jìn)行了初始化(類的初始化階段),靜態(tài)變量和方法是從屬于類本身的,跟new出來的具體對(duì)象無關(guān),所以我們獲取變量就不需要傳入對(duì)象,直接傳入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;
}
}反射測(cè)試類:
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對(duì)象
Class staticClazz = Class.forName("com.example.javademo.reflect.StaticFieldTest");
//獲取靜態(tài)Field對(duì)象
Field staticField = staticClazz.getDeclaredField("age");
//因?yàn)槭莗ulic修飾符,這句話也可以不用寫
staticField.setAccessible(true);
//通過set方法,修改值, 靜態(tài)變量 是從屬于類,可以不用傳入類實(shí)例對(duì)象,直接傳入null
staticField.set(null, 25);
//驗(yàn)證修改后的值
Method agetet = staticClazz.getDeclaredMethod("getAge");
// 靜態(tài)方法 是從屬于類, 可以不用傳入類實(shí)例對(duì)象,直接傳入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. 對(duì)于static 變量和方法,因?yàn)樗菑膶儆陬惐旧?,在類的?shí)例化之前就進(jìn)行了初始化,當(dāng)傳入obj對(duì)象參數(shù)的時(shí)候,直接傳入null即可。
4. 反射在Android中的應(yīng)用
4.1反射實(shí)現(xiàn)獲取頂層activity的名稱
如前言背景中的問題描述,有了上面的理論知識(shí)做為基礎(chǔ),實(shí)現(xiàn)代碼如下:
/**
* @description: 通過反射方式獲取 頂層activity名稱
* @param: null
* @return: boolean 如果是目標(biāo)activity 則返回true
*/
public boolean isTopTargetActivity() {
try {
//通過包名和類名反射獲取 class 對(duì)象
Class activityThreadClass = Class.forName("android.app.ActivityThread");
//第二種: 反射創(chuàng)建 activiyThread對(duì)象 反射調(diào)用靜態(tài)方法,第一個(gè)參數(shù)obj對(duì)象傳遞 null
Object activityThreadObj = activityThreadClass.getMethod("currentActivityThread").invoke(null);
Log.e("test", "====activityThreadObj: "+activityThreadObj);
//獲取 mActivities Field對(duì)象 final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
Field activitiesField = activityThreadClass.getDeclaredField("mActivities");
//禁止Java語言訪問使用時(shí)進(jìn)行檢查
activitiesField.setAccessible(true);
//返回該所表示的字段的值 Field
Map activities = (Map) activitiesField.get(activityThreadObj);
for (Object activityClientRecord : activities.values()) {
//獲取 ActivityClientRecord class對(duì)象
Class activityRecordClass = activityClientRecord.getClass();
//獲取 boolean paused 字段 對(duì)象
Field pausedField = activityRecordClass.getDeclaredField("paused");
//允許暴力訪問,禁止java語言運(yùn)行時(shí)安全檢查
pausedField.setAccessible(true);
//Activity onResume的判斷條件
if (!pausedField.getBoolean(activityClientRecord)) {
//獲取 Activity activity field對(duì)象
Field activityField = activityRecordClass.getDeclaredField("activity");
activityField.setAccessible(true);
//獲取當(dāng)前顯示的activity的對(duì)象
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), 這個(gè)工具類已經(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 類中有個(gè)public無參構(gòu)造方法, 可以通過反射方式clazz.getDeclaredConstructor().newInstance() 拿到這個(gè) 類對(duì)象
接下來,就是獲取Method Field 對(duì)象 再通過invoke set 方法去修改方法 變量的值。
5. 總結(jié)
反射在Andorid中開發(fā)應(yīng)用的比較多,Class對(duì)象, Constructor 對(duì)象,Method 對(duì)象 Field對(duì)象各自的常用API要理解并熟練運(yùn)用, 結(jié)合源碼多閱讀多仿寫,相信你也可以carry住反射。
到此這篇關(guān)于詳解Andorid開發(fā)中反射機(jī)制是怎么一回事的文章就介紹到這了,更多相關(guān)Andorid反射機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android在自定義類中實(shí)現(xiàn)自定義監(jiān)聽器方式
這篇文章主要介紹了Android在自定義類中實(shí)現(xiàn)自定義監(jiān)聽器方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-03-03
Android實(shí)現(xiàn)生成二維碼并保存到相冊(cè)
這篇文章主要介紹了如何利用Android實(shí)現(xiàn)二維碼的生成,并且保存到本地相冊(cè)。文中的示例代碼講解詳細(xì),感興趣的小伙伴快跟隨小編學(xué)習(xí)一下2022-04-04
ExpandableListView實(shí)現(xiàn)手風(fēng)琴效果
這篇文章主要為大家詳細(xì)介紹了ExpandableListView實(shí)現(xiàn)手風(fēng)琴效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
Android BottomNavigationBar底部導(dǎo)航的使用方法
這篇文章主要為大家詳細(xì)介紹了Android BottomNavigationBar底部導(dǎo)航的使用方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11
Android開發(fā)中的Surface庫及用其制作播放器UI的例子
這篇文章主要介紹了Android開發(fā)中的Surface庫及用其制作播放器界面的例子,利用SurfaceView和SurfaceHolder可以高效地繪制和控制圖形界面,需要的朋友可以參考下2016-04-04
Android獲取、更改包名的小技巧分享(超實(shí)用)
這篇文章主要給大家分享介紹了關(guān)于利用Android更改包名的一個(gè)小技巧,通過這個(gè)方法大家可以很方便的修改包名,再也不用為頻繁修改包名而感到頭疼,文中還給大家分享利一個(gè)android獲取手機(jī)所有應(yīng)用包名的方法,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-12-12
Android?NDK開發(fā)(C語言--聯(lián)合體與枚舉)
這篇文章主要介紹了Android?NDK開發(fā)C語言聯(lián)合體與枚舉,共用體是一種特殊的數(shù)據(jù)類型,允許您在相同的內(nèi)存位置存儲(chǔ)不同的數(shù)據(jù)類型。您可以定義一個(gè)帶有多成員的共用體,但是任何時(shí)候只能有一個(gè)成員帶有值。下面詳細(xì)介紹該內(nèi)容,需要的朋友可以參考一下2021-12-12
Android應(yīng)用開發(fā)中Fragment間通信的實(shí)現(xiàn)教程
這篇文章主要介紹了Android應(yīng)用開發(fā)中Fragment間通信的實(shí)現(xiàn)教程,包括接口的定義實(shí)現(xiàn)與Fragment通信的動(dòng)靜態(tài)加載等,需要的朋友可以參考下2016-02-02

