詳解Andorid開(kāi)發(fā)中反射機(jī)制是怎么一回事
1. 背景
在andorid開(kāi)發(fā)中,經(jīng)常遇見(jiàn)在某些工具類(lèi)中沒(méi)有Context上下文對(duì)象時(shí),一些系統(tǒng)服務(wù)的代理對(duì)象無(wú)法創(chuàng)建出來(lái),舉個(gè)例子:比如在源碼(framework/base/graphics/java/android/graphics)路徑下的Canvas.java Bitmap.javaPicture.java Paint.java 類(lèi)就沒(méi)有上下文對(duì)象。當(dāng)時(shí)有需要調(diào)用AMS的API獲取當(dāng)前界面activity的名稱作為判斷條件去修改這些工具類(lèi)中的參數(shù),但是這幾個(gè)工具類(lèi)中又沒(méi)有上下文對(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等類(lèi)中, 根本就沒(méi)有上下文,怎么搞? 這個(gè)時(shí)候我們通過(guò)java反射來(lái)實(shí)現(xiàn),接下來(lái),本篇文章就來(lái)講解一下java反射機(jī)制,并利用反射實(shí)現(xiàn)獲取頂層activity。
2. java反射
2.1 什么是反射
反射(Reflection)是程序的自我分析能力,通過(guò)反射可以確定類(lèi)中有哪些方法、有哪些構(gòu)造方法以及有哪些成員變量,在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類(lèi),都能夠知道這個(gè)類(lèi)的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意方法和屬性;這種動(dòng)態(tài)獲取信息以及動(dòng)態(tài)調(diào)用對(duì)象方法的功能稱為java語(yǔ)言的反射機(jī)制。
2.2 什么情況下要用反射
首先java是面向?qū)ο缶幊痰模f(wàn)物皆對(duì)象, 當(dāng)我們拿到這個(gè)類(lèi)對(duì)象后,就可以訪問(wèn)類(lèi)中的成員變量,調(diào)用類(lèi)中的方法了,但是呢?對(duì)于private變量和方法,它的訪問(wèn)權(quán)限作用域只在本類(lèi)中,在外部類(lèi)或者繼承類(lèi)中,就算你創(chuàng)建了該類(lèi)對(duì)象,也是無(wú)法直接調(diào)用的。來(lái)看下這個(gè)例子:
普通的一個(gè)User 類(lèi)
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) { //如果在本類(lèi)中創(chuàng)建了對(duì)象,則所有的方法和變量都可以直接訪問(wèn) User user = new User(); System.out.println(user.name); //訪問(wèn)私有變量 System.out.println(user.test());//調(diào)用私有方法 } }
打印結(jié)果如下:
墨子
私有成員方法
1
如果在外部?jī)?nèi)中創(chuàng)建該類(lèi)對(duì)象,還能直接訪問(wèn)私有方法和變量嗎? 測(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()); //但是可以直接訪問(wèn) protected public的方法和變量 System.out.println(user.age); System.out.println(user.test2()); } }
報(bào)錯(cuò)的兩行代碼注釋掉了,可以把代碼復(fù)制過(guò)去驗(yàn)證一下。
當(dāng)然了,在andorid開(kāi)發(fā)中,還有其他場(chǎng)景會(huì)使用到反射, 很多類(lèi)或方法中經(jīng)常加上了“@hide”注釋標(biāo)記,這些API是不允許在第三方應(yīng)用直接調(diào)用的比如:SystemProperties類(lèi)在android.os下,但這個(gè)類(lèi)是隱藏的,沒(méi)有系統(tǒng)權(quán)限(android:sharedUserId="android.uid.system")的app無(wú)法直接使用,只能通過(guò)java反射來(lái)調(diào)用。
還有 Andorid中動(dòng)態(tài)代理模式 , Android插件化(Hook)這些都會(huì)用到反射,這里不一一說(shuō)明,因?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)獲取類(lèi)的實(shí)例,提高代碼靈活性
缺點(diǎn):
1.安全問(wèn)題:反射可以獲取到類(lèi)對(duì)應(yīng)的所有方法和屬性,如果存在外部調(diào)用的情況,可能出現(xiàn)超出預(yù)期的調(diào)用,從而導(dǎo)致安全風(fēng)險(xiǎn), 而且也破壞java面向?qū)ο缶幊痰姆庋b這一特性。
2.性能問(wèn)題:無(wú)論是通過(guò)字符串獲取Class、Method還是Field,都需要JVM的動(dòng)態(tài)鏈接機(jī)制動(dòng)態(tài)的進(jìn)行解析和匹配,勢(shì)必造成性能開(kāi)銷(xiāo)。每一次的反射調(diào)用都會(huì)造成Java安全機(jī)制進(jìn)行額外的安全性驗(yàn)證,造成性能開(kāi)銷(xiāo)。反射代碼使得許多JVM的運(yùn)行時(shí)優(yōu)化無(wú)法進(jìn)行。
3. Java反射機(jī)制API
Java反射機(jī)制API主要是 java.lang.Class類(lèi) 和 java.lang.reflect包。
類(lèi) | 說(shuō)明 |
java.lang.Class | 代表整個(gè)字節(jié)碼。代表一個(gè)類(lèi)型,代表整個(gè)類(lèi)。 |
java.lang.reflect.Constructor | 代表字節(jié)碼中的構(gòu)造方法字節(jié)碼。代表類(lèi)中的構(gòu)造方法。 |
java.lang.reflect.Method | 代表字節(jié)碼中的方法字節(jié)碼。代表類(lèi)中的方法。 |
java.lang.reflect.Field | 代表字節(jié)碼中的屬性字節(jié)碼。代表類(lèi)中的成員變量(靜態(tài)變量+實(shí)例變量) |
3.1 獲取Class對(duì)象
方式 | 說(shuō)明 |
對(duì)象.getClass(); | |
類(lèi)名.class | |
Class.forName(“類(lèi)的全路徑”); | 推薦此種寫(xiě)法 |
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); //第二種: 類(lèi)名.class Class clazz2 = People.class; System.out.println(clazz2); //第三種: Class.forName(包名.類(lèi)名) Class clazz3 = Class.forName("com.example.javademo.reflect.People"); System.out.println(clazz3); } }
第一種:直接new出對(duì)象,然后通過(guò)對(duì)象獲取class對(duì)象, 但是你想想類(lèi)對(duì)象都可以直接new出來(lái),在外部?jī)?nèi)中除了private 變量和方法不能直接訪問(wèn)外,都可以直接調(diào)用,我還反射干嘛?寫(xiě)那么多代碼豈不是為難自己,沒(méi)事找事,不推薦!
第二種:通過(guò)類(lèi)名.class 獲取字節(jié)碼class對(duì)象,需要導(dǎo)入包,依賴性比較強(qiáng),不然會(huì)編譯報(bào)錯(cuò)
第三種:Class的靜態(tài)方法,安全性強(qiáng), 參數(shù)為:類(lèi)包名+類(lèi)名
運(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è)類(lèi),只有一個(gè)Class對(duì)象產(chǎn)生
3.2 反射調(diào)用類(lèi)構(gòu)造方法
方法名解釋說(shuō)明
方法名 | 解釋說(shuō)明 |
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)造方法來(lái)創(chuàng)建該構(gòu)造方法的聲明類(lèi)的新實(shí)例,并用指定的初始化參數(shù)初始化該實(shí)例。 它的返回值是T類(lèi)型,所以newInstance是創(chuàng)建了一個(gè)構(gòu)造方法的聲名類(lèi)的新實(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無(wú)參構(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è)試類(lèi):
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("**********************獲取反射后類(lèi)對(duì)象*********************************"); System.out.println("反射類(lèi)對(duì)象: " + peopleclass); //獲取所有構(gòu)造方法 System.out.println("**********************打印所有構(gòu)造方法*********************************"); Constructor[] conArray = peopleclass.getDeclaredConstructors(); for(Constructor c : conArray){ System.out.println(c); } System.out.println("**************第一種:通過(guò)反射調(diào)用默認(rèn)無(wú)參構(gòu)造方法并創(chuàng)建people類(lèi)實(shí)例對(duì)象***************************"); //返回一個(gè) Constructor對(duì)象, 該對(duì)象反映Constructor對(duì)象表示的類(lèi)的指定的公共類(lèi)函數(shù)。 Object constructorPublic = peopleclass.getConstructor(); System.out.println("無(wú)參構(gòu)造方法的對(duì)象:" + peopleclass.getConstructor()); //使用此 Constructor對(duì)象表示的構(gòu)造函數(shù),使用指定的初始化參數(shù)來(lái)創(chuàng)建和初始化構(gòu)造函數(shù)的聲明類(lèi)的新實(shí)例。 //newInstance()方法內(nèi)部實(shí)際上調(diào)用了無(wú)參數(shù)構(gòu)造方法,必須保證無(wú)參構(gòu)造存在才可以 Object peopleobject1 = ((Constructor) constructorPublic).newInstance(); System.out.println("創(chuàng)建people1對(duì)象新實(shí)例: " + (People)peopleobject1); System.out.println("**************第二種:通過(guò)反射調(diào)用public有參構(gòu)造方法并創(chuàng)建People類(lèi)實(shí)例對(duì)象***************************"); Object peopleobject2 = peopleclass.getConstructor(String.class).newInstance("甲方"); System.out.println("創(chuàng)建people2對(duì)象新實(shí)例: " + (People)peopleobject2); System.out.println("**************第三種:通過(guò)反射調(diào)用private有參構(gòu)造方法并創(chuàng)建People類(lèi)實(shí)例對(duì)象***************************"); //注意,如果訪問(wèn)protect private相關(guān)的構(gòu)造方法,需要用getDeclaredConstructor這個(gè)API //返回一個(gè) Constructor對(duì)象,該對(duì)象反映 Constructor對(duì)象表示的類(lèi)或接口的指定類(lèi)函數(shù)。 Constructor constructorPrivate = peopleclass.getDeclaredConstructor(int.class, String.class); //訪問(wèn)私有構(gòu)造方法,這句話一定要寫(xiě),不然就會(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("**************第四種:通過(guò)反射調(diào)用default有參構(gòu)造方法并創(chuàng)建People類(lèi)實(shí)例對(duì)象***************************"); Constructor constructorDefault = peopleclass.getDeclaredConstructor(long.class); //訪問(wèn)public protect default構(gòu)造方法,這句話可以不用寫(xiě) //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:
**********************獲取反射后類(lèi)對(duì)象*********************************
反射類(lèi)對(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()
**************第一種:通過(guò)反射調(diào)用默認(rèn)無(wú)參構(gòu)造方法并創(chuàng)建people類(lèi)實(shí)例對(duì)象***************************
無(wú)參構(gòu)造方法的對(duì)象:public com.example.javademo.reflect.People()
調(diào)用了public無(wú)參構(gòu)造方法 執(zhí)行完畢
創(chuàng)建people1對(duì)象新實(shí)例: com.example.javademo.reflect.People@2a139a55
**************第二種:通過(guò)反射調(diào)用public有參構(gòu)造方法并創(chuàng)建People類(lèi)實(shí)例對(duì)象***************************
調(diào)用public有參構(gòu)造方法 String:name = 甲方
創(chuàng)建people2對(duì)象新實(shí)例: com.example.javademo.reflect.People@15db9742
**************第三種:通過(guò)反射調(diào)用private有參構(gòu)造方法并創(chuàng)建People類(lèi)實(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
**************第四種:通過(guò)反射調(diào)用default有參構(gòu)造方法并創(chuàng)建People類(lèi)實(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
通過(guò)上面的例子,總結(jié):
1. 反射Class對(duì)象, 反射Constructor對(duì)象, 反射類(lèi)實(shí)例對(duì)象 是3個(gè)不同的對(duì)象,如果剛開(kāi)始不熟悉API,建議分開(kāi)創(chuàng)建;
2.getConstructor() 和 getDeclaredConstructor(類(lèi)<?>... parameterTypes)方法的區(qū)別 前者是獲取public類(lèi)型構(gòu)造方法的Constructor對(duì)象,后者是獲取全類(lèi)型(public protect default private)類(lèi)型的構(gòu)造方法的Constructor對(duì)象
3.反射私有構(gòu)造方法的時(shí)候,一定要setAccessible(true),不然會(huì)報(bào)錯(cuò)java.lang.IllegalAccessException錯(cuò)誤 * true表示:禁止java語(yǔ)言使用時(shí)安全檢查,暴力訪問(wèn)
4. 調(diào)用Constructor類(lèi)中newInstance()這個(gè)方法時(shí),注意newInstance沒(méi)有帶參數(shù)(不帶參數(shù)可以不寫(xiě),或?qū)懗蒼ull 都可以行) 此方法內(nèi)部實(shí)際上調(diào)用了無(wú)參數(shù)構(gòu)造方法,必須保證無(wú)參構(gòu)造存在才可以,否則會(huì)拋出java.lang.InstantiationException異常。
3.3 反射調(diào)用類(lèi)中方法
方法名 | 說(shuō)明 |
public Method[] getMethods() | 獲取所有"公有方法";(包含了父類(lèi)的方法也包含Object類(lèi)) |
public Method getMethod(String name,Class<?>... parameterTypes) | 獲取指定參數(shù)的公共成員方法 類(lèi)對(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 = "蘋(píng)果"; int milliliter = 0; char game = 'X'; String mood = "開(kāi)心"; 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è)試類(lèi)
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("**********************獲取反射后類(lèi)對(duì)象*********************************"); //獲取方法類(lèi)的class對(duì)象 Class methodClass = Class.forName("com.example.javademo.reflect.MethodTest"); System.out.println("反射類(lèi)對(duì)象 :" + methodClass); System.out.println("**********************獲取反射類(lèi)中所有的成員方法**************************"); //返回一個(gè)"方法數(shù)組對(duì)象":反射類(lèi)或接口中所有聲明的方法,包括公共,保護(hù),默認(rèn)(包)訪問(wèn)和私有方法,但不包括繼承的方法。 Method[] methods = methodClass.getDeclaredMethods(); for (Method m : methods) { System.out.println(m); } System.out.println("***************第一種*******調(diào)用反射類(lèi)中public成員方法**************************"); //返回一個(gè)方法對(duì)象,它表示此表示的類(lèi)或接口的指定聲明的方法對(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類(lèi)對(duì)象,每個(gè)類(lèi)有一個(gè)默認(rèn)的無(wú)參構(gòu)造方法,可以寫(xiě)或不寫(xiě) //但是如果類(lèi)中有帶參數(shù)的構(gòu)造方法,那無(wú)參構(gòu)造方法是必須要寫(xiě)出來(lái)的,不然new對(duì)象的時(shí)候,就會(huì)報(bào)錯(cuò) MethodTest objMethodTest = (MethodTest)methodClass.getDeclaredConstructor().newInstance(); System.out.println("MethodTest類(lèi)對(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)用反射類(lèi)中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()); //如果是訪問(wèn)私有方法,則要加上這句話,禁止java語(yǔ)言使用時(shí)安全檢查 methodPrivate.setAccessible(true); Object objectreturn2 = methodPrivate.invoke(objMethodTest, "超級(jí)nice"); System.out.println("happy方法 返回值為:"+objectreturn2); System.out.println("***************第三種*******調(diào)用反射類(lèi)中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)用反射類(lèi)中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:
**********************獲取反射后類(lèi)對(duì)象*********************************
反射類(lèi)對(duì)象 :class com.example.javademo.reflect.MethodTest
**********************獲取反射類(lèi)中所有的成員方法**************************
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)用反射類(lèi)中public成員方法**************************
打印該public方法的修飾符 :1
打印該public方法對(duì)象 表示方法的名稱: eat
打印該public方法對(duì)象 可執(zhí)行文件的形式參數(shù)的數(shù)量 :1
調(diào)用 MethodTest默認(rèn)的共有構(gòu)造方法
MethodTest類(lèi)對(duì)象 :com.example.javademo.reflect.MethodTest@15db9742
調(diào)用public成員方法 eat 吃的水果為 :菠蘿
eat方法 返回值為 :null
***************第二種*******調(diào)用反射類(lèi)中private成員方法**************************
打印該private方法的修飾符 :2
打印該private方法對(duì)象 表示方法的名稱: happy
打印該public方法對(duì)象 可執(zhí)行文件的形式參數(shù)的數(shù)量 :1
調(diào)用private成員方法 happy 今日心情:超級(jí)nice
happy方法 返回值為:超級(jí)nice
***************第三種*******調(diào)用反射類(lèi)中protected成員方法**************************
打印該protected方法的修飾符 :4
打印該protected方法對(duì)象 表示方法的名稱: play
打印該protected方法對(duì)象 可執(zhí)行文件的形式參數(shù)的數(shù)量 :1
調(diào)用protected成員方法 play 玩游戲 :Y
play方法 返回值為:null
***************第四種*******調(diào)用反射類(lèi)中default成員方法**************************
打印該protected方法的修飾符 :0
打印該protected方法對(duì)象 表示方法的名稱: drink
打印該protected方法對(duì)象 可執(zhí)行文件的形式參數(shù)的數(shù)量 :1
調(diào)用default成員方法 drink 喝水毫升量 :180
drink方法 返回值為:180
代碼中基本都做了注釋,通過(guò)上面的例子,總結(jié):
1. 反射class對(duì)象, 反射Method對(duì)象 反射類(lèi)實(shí)例對(duì)象 是3個(gè)不同的對(duì)象,要區(qū)分開(kāi)
2. 在反射調(diào)用private方法時(shí),一定要禁止java語(yǔ)言使用時(shí)安全檢查,要setAccessible(true),其他修飾符方法可以不用加這句話
3. (反射Method對(duì)象).invoke(Object obj, Object ...args) 方法參數(shù)的理解 :
1. 第一個(gè)參數(shù)為 通過(guò)用反射方法構(gòu)造出來(lái)的 反射類(lèi)實(shí)例對(duì)象
2. 第二個(gè)可變參數(shù)為: 該方法需要傳入的參數(shù)
3. 如果方法正常完成,則將返回值返回
3.4 反射類(lèi)中的成員變量
方法 | 說(shuō)明 |
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è)置為指定的新值 |
我們通過(guò)Demo講解:
package com.example.javademo.reflect; /** * desc : 供反射調(diào)用的FieldTest類(lèi) * version: 1.0 */ public class FieldTest { public FieldTest() { System.out.println("調(diào)用FieldTest 默認(rèn)無(wú)參構(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è)試類(lèi):
package com.example.javademo.reflect; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * desc : 反射FiledTest類(lèi)中各字段(Field)的實(shí)現(xiàn)類(lèi) * 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());*/ //通過(guò)反射的方式,修改字段值 try { //通過(guò)反射獲取Class對(duì)象 Class FieldTestClass = Class.forName("com.example.javademo.reflect.FieldTest"); //用反射的方式來(lái)修改字段(成員變量)的值 System.out.println("************獲取所有的字段(包括共有、私有、受保護(hù)、默認(rèn)的)********************"); Field[] fields = FieldTestClass.getDeclaredFields(); for (Field f : fields) { System.out.println(f); } //用反射的方式來(lái)構(gòu)造FieldTestl類(lèi)對(duì)象 FieldTest objFieldTest = (FieldTest) FieldTestClass.getDeclaredConstructor().newInstance(); System.out.println("*************反射獲取public字段并調(diào)用***********************************"); //返回一個(gè) Field對(duì)象,它表示的類(lèi)或接口的指定已聲明字段對(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 :為通過(guò)反射方法 構(gòu)造出來(lái)的類(lèi)對(duì)象 * */ //獲取int類(lèi)型 實(shí)例字段的默認(rèn)值 System.out.println("通過(guò)反射方式獲取age默認(rèn)值 : " + fieldPublic.getInt(objFieldTest)); /* 設(shè)置字段的值: Field public void set(Object obj,Object value): 參數(shù)說(shuō)明: 1.obj: 通過(guò)反射方法 構(gòu)造出來(lái)的類(lèi)對(duì)象 2.value:要為字段設(shè)置的值; */ fieldPublic.set(objFieldTest, 20); //通過(guò)反射的方式調(diào)用 getAge() 方法 Method method = FieldTestClass.getDeclaredMethod("getAge"); Object objectReturn = method.invoke(objFieldTest); System.out.println("通過(guò)反射方式set新值之后, age的值 :" + objectReturn); System.out.println("*************反射獲取private字段并調(diào)用***********************************"); //在項(xiàng)目實(shí)際中,因?yàn)樗接凶兞亢头椒?,無(wú)法在外部類(lèi)去調(diào)用它們,所以反射在這點(diǎn)上就派上用場(chǎng)了 Field fieldPrivate = FieldTestClass.getDeclaredField("name"); //private字段一定要調(diào)用setAccessible(true) 禁止java語(yǔ)言使用時(shí)安全檢查 fieldPrivate.setAccessible(true); System.out.println("通過(guò)反射方式獲取age默認(rèn)值 : " + fieldPrivate.get(objFieldTest)); //修改private變量name的值 fieldPrivate.set(objFieldTest, "李四"); //通過(guò)反射的方式調(diào)用 getName() 方法 Method objectMethod = FieldTestClass.getDeclaredMethod("getName"); Object objectReturn1 = objectMethod.invoke(objFieldTest); System.out.println("通過(guò)反射方式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)無(wú)參構(gòu)造方法
*************反射獲取public字段并調(diào)用***********************************
該字段的修飾符 :1
該字段的名稱 :age
通過(guò)反射方式獲取age默認(rèn)值 : 18
通過(guò)反射方式set新值之后, age的值 :20
*************反射獲取private字段并調(diào)用***********************************
通過(guò)反射方式獲取age默認(rèn)值 : 張三
通過(guò)反射方式set新值之后, name的值 :李四
通過(guò)例子,總結(jié)如下:
1. 反射class對(duì)象, 反射Field對(duì)象 反射類(lèi)實(shí)例對(duì)象 是3個(gè)不同的對(duì)象,要區(qū)分開(kāi)
2.在反射調(diào)用private字段時(shí),一定要禁止java語(yǔ)言使用時(shí)安全檢查,要setAccessible(true)
3. Field public void set(Object obj,Object value):
參數(shù)說(shuō)明:
1.obj: 通過(guò)反射方法 構(gòu)造出來(lái)的類(lèi)對(duì)象
2.value:要為字段設(shè)置的值;
3.5 反射類(lèi)中靜態(tài)方法和變量
怎么修改static修飾的變量呢?靜態(tài)變量和方法是在類(lèi)的實(shí)例化之前就進(jìn)行了初始化(類(lèi)的初始化階段),靜態(tài)變量和方法是從屬于類(lèi)本身的,跟new出來(lái)的具體對(duì)象無(wú)關(guān),所以我們獲取變量就不需要傳入對(duì)象,直接傳入null即可。
Demo如下:
package com.example.javademo.reflect; public class StaticFieldTest { public StaticFieldTest() { System.out.println("調(diào)用 StaticFieldTest 無(wú)參構(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è)試類(lèi):
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修飾符,這句話也可以不用寫(xiě) staticField.setAccessible(true); //通過(guò)set方法,修改值, 靜態(tài)變量 是從屬于類(lèi),可以不用傳入類(lèi)實(shí)例對(duì)象,直接傳入null staticField.set(null, 25); //驗(yàn)證修改后的值 Method agetet = staticClazz.getDeclaredMethod("getAge"); // 靜態(tài)方法 是從屬于類(lèi), 可以不用傳入類(lè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)樗菑膶儆陬?lèi)本身,在類(lèi)的實(shí)例化之前就進(jìn)行了初始化,當(dāng)傳入obj對(duì)象參數(shù)的時(shí)候,直接傳入null即可。
4. 反射在Android中的應(yīng)用
4.1反射實(shí)現(xiàn)獲取頂層activity的名稱
如前言背景中的問(wèn)題描述,有了上面的理論知識(shí)做為基礎(chǔ),實(shí)現(xiàn)代碼如下:
/** * @description: 通過(guò)反射方式獲取 頂層activity名稱 * @param: null * @return: boolean 如果是目標(biāo)activity 則返回true */ public boolean isTopTargetActivity() { try { //通過(guò)包名和類(lèi)名反射獲取 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語(yǔ)言訪問(wèn)使用時(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"); //允許暴力訪問(wèn),禁止java語(yǔ)言運(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", "====通過(guò)反射獲取的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)用是無(wú)法直接調(diào)用SystemProperties中 get set 方法,我們也可以通過(guò)反射來(lái)實(shí)現(xiàn), 這個(gè)工具類(lèi)已經(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 通過(guò)反射創(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 類(lèi)中有個(gè)public無(wú)參構(gòu)造方法, 可以通過(guò)反射方式clazz.getDeclaredConstructor().newInstance() 拿到這個(gè) 類(lèi)對(duì)象
接下來(lái),就是獲取Method Field 對(duì)象 再通過(guò)invoke set 方法去修改方法 變量的值。
5. 總結(jié)
反射在Andorid中開(kāi)發(fā)應(yīng)用的比較多,Class對(duì)象, Constructor 對(duì)象,Method 對(duì)象 Field對(duì)象各自的常用API要理解并熟練運(yùn)用, 結(jié)合源碼多閱讀多仿寫(xiě),相信你也可以carry住反射。
到此這篇關(guān)于詳解Andorid開(kāi)發(fā)中反射機(jī)制是怎么一回事的文章就介紹到這了,更多相關(guān)Andorid反射機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android中Fragment的基本用法示例總結(jié)
Fragment是activity的界面中的一部分或一種行為,下面這篇文章主要給大家介紹了關(guān)于Android中Fragment的基本用法的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2018-05-05Android 無(wú)障礙全局懸浮窗實(shí)現(xiàn)示例
本文主要介紹了Android 無(wú)障礙全局懸浮窗實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06Flutter 日期時(shí)間DatePicker控件及國(guó)際化
這篇文章主要介紹了Flutter 日期時(shí)間DatePicker控件及國(guó)際化,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03Android使用TypeFace設(shè)置TextView的文字字體
這篇文章主要介紹了Android使用TypeFace設(shè)置TextView的文字字體的方法,幫助大家更好的利用Android開(kāi)發(fā),感興趣的朋友可以了解下2021-01-01詳解Android_性能優(yōu)化之ViewPager加載成百上千高清大圖oom解決方案
這篇文章主要介紹了詳解Android_性能優(yōu)化之ViewPager加載成百上千高清大圖oom解決方案,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2016-12-12Android自定義簡(jiǎn)單的頂部標(biāo)題欄
這篇文章主要為大家詳細(xì)介紹了Android自定義簡(jiǎn)單的頂部標(biāo)題欄,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-11-11Android應(yīng)用內(nèi)懸浮窗的實(shí)現(xiàn)方案示例
本篇文章主要介紹了Android應(yīng)用內(nèi)懸浮窗的實(shí)現(xiàn)方案示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08Android view自定義實(shí)現(xiàn)動(dòng)態(tài)進(jìn)度條
這篇文章主要介紹了Android view自定義實(shí)現(xiàn)動(dòng)態(tài)進(jìn)度條的相關(guān)資料,這里提供實(shí)例代碼及實(shí)現(xiàn)效果圖,需要的朋友可以參考下2016-12-12Android 中TextView中跑馬燈效果的實(shí)現(xiàn)方法
這篇文章主要介紹了Android 中TextView中跑馬燈效果的實(shí)現(xiàn)方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-02-02