反射
1. 什么是反射沟突?
? 主要是指程序可以訪問汉矿、檢測(cè)和修改它本身狀態(tài)或行為的一種能力
2. Java反射提供了什么能力拜隧?
? 在Java運(yùn)行時(shí)環(huán)境中呻此,對(duì)于任意一個(gè)類轮纫,都知道這個(gè)類有哪些屬性和方法,對(duì)于任意一個(gè)對(duì)象都能調(diào)用它的任意一個(gè)方法焚鲜。具體的能力如下:
- 1.在運(yùn)行時(shí)判斷任意一個(gè)對(duì)象所屬的類掌唾。
- 2.在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象。
- 3.在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法忿磅。
- 4.在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法糯彬。
3.反射的作用是什么?
? 反射可以在程序運(yùn)行的時(shí)候葱她,動(dòng)態(tài)修改程序的某些屬性撩扒,使得程序按照我們?cè)O(shè)計(jì)的流程運(yùn)行。大量開源框架都會(huì)用到反射機(jī)制吨些,例如:
- 騰訊的Tinker熱修復(fù)框架會(huì)反射DexPathList類拿到dexElements變量搓谆,將補(bǔ)丁dex文件放入到dexElements數(shù)組的第一個(gè),讓ClassLoader首先加載已修復(fù)的類豪墅。
- 360的DroidPlugin等插件化框架會(huì)反射AMS偷梁換柱intent泉手,來繞過AMS對(duì)待啟動(dòng)的Activity是否注冊(cè)Mainifest的驗(yàn)證。
- ARouter偶器、ButterKnife斩萌、Retrofit、Dagger2等等開源框架都會(huì)通過反射拿到用戶使用注解的類屏轰,然后完成參數(shù)注入或者獲取颊郎。
下面用一張圖來解釋一下反射來實(shí)現(xiàn)Hook的原理:
? 橫軸是程序正常運(yùn)行的時(shí)間軸,我們通過反射機(jī)制在編譯期或者運(yùn)行時(shí)拿到程序中的某個(gè)類霎苗,動(dòng)態(tài)修改該類中的一些屬性姆吭,使程序按照我們期望的點(diǎn)運(yùn)行。該機(jī)制主要還是用于我們無法修改到別人的代碼唁盏,又要借助別人的代碼實(shí)現(xiàn)自己期望的邏輯的場(chǎng)景猾编。
4. 反射有什么缺點(diǎn)瘤睹?
? 大量運(yùn)用反射會(huì)導(dǎo)致程序變慢升敲,但是經(jīng)過測(cè)試答倡,一般使用反射的數(shù)量級(jí)在1000以下,幾乎可以忽略影響驴党。那么為什么反射會(huì)導(dǎo)致程序性能變差呢:
- 在使用反射的過程中會(huì)產(chǎn)生大量的臨時(shí)對(duì)象
- 虛擬機(jī)在檢查對(duì)象可見性的時(shí)候是會(huì)消耗CPU資源
- 反射會(huì)生成沒有優(yōu)化的字節(jié)碼
- 進(jìn)行拆箱瘪撇、裝箱、類型轉(zhuǎn)換的時(shí)候會(huì)消耗資源
5. 如何使用反射港庄?
? 在JDK中主要有以下類來實(shí)現(xiàn)反射機(jī)制倔既,這些(除了第一個(gè))都位于rt.jar的java.lang.reflect包中
- Class類:代表一個(gè)類,位于java.lang包下鹏氧。
- Field類:代表類的成員變量(成員變量也稱為類的屬性)渤涌。
- Method類:代表類的方法。
- Constructor類:代表類的構(gòu)造方法把还。
- Array類:提供了動(dòng)態(tài)創(chuàng)建數(shù)組实蓬,以及訪問數(shù)組的元素的靜態(tài)方法。
詳細(xì)API我先用一張圖展示出來吊履,然后一個(gè)一個(gè)闡述用法安皱。
5.1 Class類的6種獲取方式
類名.class。 例如: MainActivity.class;
對(duì)象.getClass()艇炎。 例如: View view; view.getClass();
Class.forName("全限定名")酌伊。 例如: Class.forName("java.lang.String");
-
類.class.getClassLoader().loadClass("全限定名")。
例如: MainActivity.class.getClassLoader().loadClass("java.lang.String");
子類.class.getSuperClass()缀踪。 例如: MainActivity.class.getSuperclass();
包裝類.class居砖。 例如: Integer.class、ContextThemeWrapper.class
5.2 根據(jù)類獲取類名驴娃、全限定名和包名
- getName() 獲取全限定名奏候。 例如: MainActivity.class.getName()
- getSimpleName() 獲得類名。例如: MainActivity.class.getSimpleName()
- getPackage().getName()包名托慨。 例如: MainActivity.class.getPackage().getName()
5.3 獲取變量鼻由、屬性
- getField("屬性名") 獲取public公共屬性,包括可以獲取父類的
- getName() 屬性名
- getModifiers() 修飾符
- getType() 數(shù)據(jù)類型
- set(對(duì)象名厚棵,屬性值) 給屬性賦值蕉世。相當(dāng)于 對(duì)象名.set屬性名
- get(對(duì)象名) 獲取屬性。相當(dāng)于 對(duì)象名.get屬性名
- getDeclearedField("屬性名") 獲取指定屬性
- setAccessible(true) 放開private屬性訪問權(quán)
- getDeclearedFields() 獲取類的全部屬性
5.4 獲取類中的方法
- getMethod(方法名婆硬,參數(shù)數(shù)據(jù)類型(沒有參數(shù)傳null)) 獲取public方法
- getDeclearedMethod(方法名狠轻,參數(shù)數(shù)據(jù)類型(沒有參數(shù)傳null)) 獲取類中所有方法
- invoke(對(duì)象名,參數(shù)列表) 執(zhí)行方法彬犯。相當(dāng)于 對(duì)象名.方法名 如果是靜態(tài)方法對(duì)象名傳入null
- getParameterTypes() 得到返回參數(shù)列表
- getDeclearedMethods() 得到類的所有的方法
- getReturnType() 獲取返回值的數(shù)據(jù)類型
5.5 獲取和調(diào)用構(gòu)造方法
- Class對(duì)象.getConstructor() 得到構(gòu)造方法
- Class對(duì)象.getConstructors() 得到所有構(gòu)造方法
- Class對(duì)象.getDeclaredConstructor 獲取Class類中的構(gòu)造方法
- newInstance(參數(shù)) 調(diào)用構(gòu)造方法