日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
聊聊Java知識(shí)點(diǎn)之反射

前言

創(chuàng)新互聯(lián)公司-成都網(wǎng)站建設(shè)公司,專(zhuān)注成都做網(wǎng)站、成都網(wǎng)站建設(shè)、網(wǎng)站營(yíng)銷(xiāo)推廣,域名與空間,網(wǎng)頁(yè)空間,綿陽(yáng)服務(wù)器托管有關(guān)企業(yè)網(wǎng)站制作方案、改版、費(fèi)用等問(wèn)題,請(qǐng)聯(lián)系創(chuàng)新互聯(lián)公司。

今天說(shuō)Java模塊內(nèi)容:反射。

反射介紹

正常情況下,我們知曉我們要操作的類(lèi)和對(duì)象是什么,可以直接操作這些對(duì)象中的變量和方法,比如一個(gè)User類(lèi):

 
 
 
 
  1. User user=new User(); 
  2. user.setName("Bob"); 

但是有的場(chǎng)景,我們無(wú)法正常去操作:

  • 只知道類(lèi)路徑,無(wú)法直接實(shí)例化的對(duì)象。
  • 無(wú)法直接操作某個(gè)對(duì)象的變量和方法,比如私有方法,私有變量。
  • 需要hook系統(tǒng)邏輯,比如修改某個(gè)實(shí)例的參數(shù)。

等等情況。

所以我們就需要一種機(jī)制能讓我們?nèi)ゲ僮魅我獾念?lèi)和對(duì)象。

這種機(jī)制,就是反射。簡(jiǎn)單的說(shuō),反射就是:

對(duì)于任意一個(gè)類(lèi),都能夠知道這個(gè)類(lèi)的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意方法和屬性。

常用API舉例

先設(shè)定一個(gè)User類(lèi):

 
 
 
 
  1. package com.example.testapplication.reflection; 
  2. public class User { 
  3.     private int age; 
  4.     public String name; 
  5.  
  6.     public User() { 
  7.         System.out.println("調(diào)用了User()"); 
  8.     } 
  9.  
  10.     private User(int age, String name) { 
  11.         this.name = name; 
  12.         this.age = age; 
  13.         System.out.println("調(diào)用了User(age,name)"+"__age:"+age+"__name:"+name); 
  14.     } 
  15.  
  16.     public User(String name) { 
  17.         this.name = name; 
  18.         System.out.println("調(diào)用了User(name)"+"__name:"+name); 
  19.     } 
  20.  
  21.     private String getName() { 
  22.         System.out.println("調(diào)用了getName()"); 
  23.         return this.name; 
  24.     } 
  25.  
  26.     private String setName(String name) { 
  27.      this.name = name; 
  28.         System.out.println("調(diào)用了setName(name)__"+name); 
  29.         return this.name; 
  30.     } 
  31.  
  32.     public int getAge() { 
  33.         System.out.println("調(diào)用了getAge()"); 
  34.         return this.age; 
  35.     }     

獲取Class對(duì)象

  • 主要有三種方法獲取Class對(duì)象:
  • 根據(jù)類(lèi)路徑獲取類(lèi)對(duì)象
  • 直接獲取

實(shí)例對(duì)象的getclass方法

 
 
 
 
  1. //1、根據(jù)類(lèi)路徑獲取類(lèi)對(duì)象 
  2. try { 
  3.     Class clz = Class.forName("com.example.testapplication.reflection.User"); 
  4. } catch (ClassNotFoundException e) { 
  5.     e.printStackTrace(); 
  6.  
  7. //2、直接獲取 
  8. Class clz = User.class; 
  9.  
  10. //3、對(duì)象的getclass方法 
  11. Class clz = new User().getClass(); 

獲取類(lèi)的構(gòu)造方法

1、獲取類(lèi)所有構(gòu)造方法

 
 
 
 
  1. Class clz = User.class; 
  2. //獲取所有構(gòu)造函數(shù)(不包括私有構(gòu)造方法) 
  3. Constructor[] constructors1 = clz.getConstructors(); 
  4. //獲取所有構(gòu)造函數(shù)(包括私有構(gòu)造方法) 
  5. Constructor[] constructors2 = clz.getDeclaredConstructors(); 

2、獲取類(lèi)的單個(gè)構(gòu)造方法

 
 
 
 
  1. try { 
  2.        //獲取無(wú)參構(gòu)造函數(shù) 
  3.        Constructor constructor1 = clz.getConstructor(); 
  4.  
  5.        //獲取參數(shù)為String的構(gòu)造函數(shù) 
  6.        Constructor constructor2 =clz.getConstructor(String.class); 
  7.  
  8.        //獲取參數(shù)為int,String的構(gòu)造函數(shù) 
  9.        Class[] params = {int.class,String.class}; 
  10.        Constructor constructor3 =clz.getDeclaredConstructor(params); 
  11.    } catch (NoSuchMethodException e) { 
  12.        e.printStackTrace(); 
  13.    } 

需要注意的是,User(int age, String name)為私有構(gòu)造方法,所以需要使用getDeclaredConstructor獲取。

調(diào)用類(lèi)的構(gòu)造方法生成實(shí)例對(duì)象

1、調(diào)用Class對(duì)象的newInstance方法

這個(gè)方法只能調(diào)用無(wú)參構(gòu)造函數(shù),也就是Class對(duì)象的newInstance方法不能傳入?yún)?shù)。

 
 
 
 
  1. Object user = clz.newInstance(); 

2、調(diào)用Constructor對(duì)象的newInstance方法

 
 
 
 
  1. Class[] params = {int.class,String.class}; 
  2. Constructor constructor3 =clz.getDeclaredConstructor(params); 
  3. constructor3.setAccessible(true); 
  4. constructor3.newInstance(22,"Bob"); 

這里要注意下,雖然getDeclaredConstructor能獲取私有構(gòu)造方法,但是如果要調(diào)用這個(gè)私有方法,需要設(shè)置setAccessible(true)方法,否則會(huì)報(bào)錯(cuò):

 
 
 
 
  1. can not access a member of class com.example.testapplication.reflection.User with modifiers "private" 

獲取類(lèi)的屬性(包括私有屬性)

 
 
 
 
  1. Class clz = User.class; 
  2. Field field1 = clz.getField("name"); 
  3. Field field2 = clz.getDeclaredField("age"); 

同樣的,getField獲取public類(lèi)變量,getDeclaredField可以獲取所有變量(包括私有變量屬性)。

所以一般直接用getDeclaredField即可。

修改實(shí)例的屬性

接上例,獲取類(lèi)的屬性后,可以去修改類(lèi)實(shí)例的對(duì)應(yīng)屬性,比如我們有個(gè)user的實(shí)例對(duì)象,我們來(lái)修改它的name和age。

 
 
 
 
  1. //修改name,name為public屬性 
  2. Class clz = User.class; 
  3. Field field1 = clz.getField("name"); 
  4. field1.set(user,"xixi"); 
  5.  
  6. //修改age,age為private屬性 
  7. Class clz = User.class; 
  8. Field field2 = clz.getDeclaredField("age"); 
  9. field2.setAccessible(true); 
  10. field2.set(user,123); 

獲取類(lèi)的方法(包括私有方法)

 
 
 
 
  1.   //獲取getName方法 
  2.    Method method1 = clz.getDeclaredMethod("getName"); 
  3. //獲取setName方法,帶參數(shù) 
  4.    Method method2 = clz.getDeclaredMethod("setName", String.class); 
  5.    //獲取getage方法 
  6.    Method method3 = clz.getMethod("getAge"); 

調(diào)用實(shí)例的方法

 
 
 
 
  1. method1.setAccessible(true); 
  2. Object name = method1.invoke(user); 
  3.  
  4.  
  5. method2.setAccessible(true); 
  6. method2.invoke(user, "xixi"); 
  7.  
  8. Object age = method3.invoke(user); 

反射優(yōu)缺點(diǎn)

雖然反射很好用,增加了程序的靈活性,但是也有他的缺點(diǎn):

  • 性能問(wèn)題。由于用到動(dòng)態(tài)類(lèi)型(運(yùn)行時(shí)才檢查類(lèi)型),所以反射的效率比較低。但是對(duì)程序的影響比較小,除非對(duì)性能要求比較高。所以需要在兩者之間平衡。
  • 不夠安全。由于可以執(zhí)行一些私有的屬性和方法,所以可能會(huì)帶來(lái)安全問(wèn)題。
  • 不易讀寫(xiě)。當(dāng)然這一點(diǎn)也有解決方案,比如jOOR庫(kù),但是不適用于Android定義為final的字段。

Android中的應(yīng)用

插件化(Hook)

Hook 技術(shù)又叫做鉤子函數(shù),在系統(tǒng)沒(méi)有調(diào)用該函數(shù)之前,鉤子程序就先捕獲該消息,鉤子函數(shù)先得到控制權(quán),這時(shí)鉤子函數(shù)既可以加工處理(改變)該函數(shù)的執(zhí)行行為,還可以強(qiáng)制結(jié)束消息的傳遞。

在插件化中,我們需要找到可以hook的點(diǎn),然后進(jìn)行一些插件的工作,比如替換Activity,替換mH等等。這其中就用到大量反射的知識(shí),這里以替換mH為例:

 
 
 
 
  1. // 獲取到當(dāng)前的ActivityThread對(duì)象 
  2. Class activityThreadClass = Class.forName("android.app.ActivityThread"); 
  3. Field currentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread"); 
  4. currentActivityThreadField.setAccessible(true); 
  5. Object currentActivityThread = currentActivityThreadField.get(null); 
  6.  
  7. //獲取這個(gè)對(duì)象的mH 
  8. Field mHField = activityThreadClass.getDeclaredField("mH"); 
  9. mHField.setAccessible(true); 
  10. Handler mH = (Handler) mHField.get(currentActivityThread); 
  11.  
  12.  
  13. //替換mh為我們自己的HandlerCallback 
  14. Field mCallBackField = Handler.class.getDeclaredField("mCallback"); 
  15. mCallBackField.setAccessible(true); 
  16. mCallBackField.set(mH, new MyActivityThreadHandlerCallback(mH)); 

動(dòng)態(tài)代理

動(dòng)態(tài)代理的特點(diǎn)是不需要提前創(chuàng)建代理對(duì)象,而是利用反射機(jī)制在運(yùn)行時(shí)創(chuàng)建代理類(lèi),從而動(dòng)態(tài)實(shí)現(xiàn)代理功能。

 
 
 
 
  1. public class InvocationTest implements InvocationHandler { 
  2.     // 代理對(duì)象(代理接口) 
  3.     private Object subject; 
  4.  
  5.     public InvocationTest(Object subject) { 
  6.         this.subject = subject; 
  7.     } 
  8.     @Override 
  9.     public Object invoke(Object object, Method method, Object[] args) 
  10.             throws Throwable { 
  11.         //代理真實(shí)對(duì)象之前 
  12.         Object obj = method.invoke(subject, args); 
  13.         //代理真實(shí)對(duì)象之后 
  14.         return obj; 
  15.     } 

三方庫(kù)(注解)

我們可以發(fā)現(xiàn)很多庫(kù)都會(huì)用到注解,而獲取注解的過(guò)程也會(huì)有反射的過(guò)程,比如獲取Activity中所有變量的注解:

 
 
 
 
  1. public void getAnnotation(Activity activity){ 
  2.     Class clazz = activity.getClass(); 
  3.     //獲得activity中的所有變量 
  4.     Field[] fields = clazz.getDeclaredFields(); 
  5.     for (Field field : fields) { 
  6.         field.setAccessible(true); 
  7.         //獲取變量上加的注解 
  8.         MyAnnotation test = field.getAnnotation(MyAnnotation.class); 
  9.         //... 
  10.     } 

這種通過(guò)反射處理注解的方式稱(chēng)作運(yùn)行時(shí)注解,也就是程序運(yùn)行狀態(tài)的時(shí)候才會(huì)去處理注解。但是上文說(shuō)過(guò)了,反射會(huì)在一定程度上影響到程序的性能,所以還有一種處理注解的方式:編譯時(shí)注解。

所用到的注解處理工具是APT。

APT是一種注解處理器,可以在編譯時(shí)進(jìn)行掃描和處理注解,然后生成java代碼文件,這種方法對(duì)比反射就能比較小的影響到程序的運(yùn)行性能。

這里就不說(shuō)APT的使用了,下次會(huì)專(zhuān)門(mén)有章節(jié)提到~

Android體系架構(gòu)

Android體系架構(gòu):https://github.com/JiMuzz/Android-Architecture

參考

https://www.jianshu.com/p/3382cc765b39

https://segmentfault.com/a/1190000015860183

本文轉(zhuǎn)載自微信公眾號(hào)「碼上積木」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系碼上積木公眾號(hào)。


新聞名稱(chēng):聊聊Java知識(shí)點(diǎn)之反射
地址分享:http://www.dlmjj.cn/article/dhheiic.html