溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java中反射的作用是什么

發(fā)布時間:2021-07-20 11:07:34 來源:億速云 閱讀:126 作者:chen 欄目:編程語言

本篇內(nèi)容主要講解“Java中反射的作用是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Java中反射的作用是什么”吧!

前言

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

反射介紹

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

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

但是有的場景,我們無法正常去操作:

  • 只知道類路徑,無法直接實例化的對象。

  • 無法直接操作某個對象的變量和方法,比如私有方法,私有變量。

  • 需要hook系統(tǒng)邏輯,比如修改某個實例的參數(shù)。

等等情況。

所以我們就需要一種機制能讓我們?nèi)ゲ僮魅我獾念惡蛯ο蟆?/p>

這種機制,就是反射。簡單的說,反射就是:

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

常用API舉例

先設定一個User類:

package com.example.testapplication.reflection; public class User {     private int age;     public String name;      public User() {         System.out.println("調(diào)用了User()");     }      private User(int age, String name) {         this.name = name;         this.age = age;         System.out.println("調(diào)用了User(age,name)"+"__age:"+age+"__name:"+name);     }      public User(String name) {         this.name = name;         System.out.println("調(diào)用了User(name)"+"__name:"+name);     }      private String getName() {         System.out.println("調(diào)用了getName()");         return this.name;     }      private String setName(String name) {      this.name = name;         System.out.println("調(diào)用了setName(name)__"+name);         return this.name;     }      public int getAge() {         System.out.println("調(diào)用了getAge()");         return this.age;     }     }

獲取Class對象

  • 主要有三種方法獲取Class對象:

  • 根據(jù)類路徑獲取類對象

  • 直接獲取

實例對象的getclass方法

//1、根據(jù)類路徑獲取類對象 try {     Class clz = Class.forName("com.example.testapplication.reflection.User"); } catch (ClassNotFoundException e) {     e.printStackTrace(); }  //2、直接獲取 Class clz = User.class;  //3、對象的getclass方法 Class clz = new User().getClass();

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

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

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

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

try {        //獲取無參構(gòu)造函數(shù)        Constructor constructor1 = clz.getConstructor();         //獲取參數(shù)為String的構(gòu)造函數(shù)        Constructor constructor2 =clz.getConstructor(String.class);         //獲取參數(shù)為int,String的構(gòu)造函數(shù)        Class[] params = {int.class,String.class};        Constructor constructor3 =clz.getDeclaredConstructor(params);    } catch (NoSuchMethodException e) {        e.printStackTrace();    }

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

調(diào)用類的構(gòu)造方法生成實例對象

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

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

Object user = clz.newInstance();

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

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

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

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

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

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

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

所以一般直接用getDeclaredField即可。

修改實例的屬性

接上例,獲取類的屬性后,可以去修改類實例的對應屬性,比如我們有個user的實例對象,我們來修改它的name和age。

//修改name,name為public屬性 Class clz = User.class; Field field1 = clz.getField("name"); field1.set(user,"xixi");  //修改age,age為private屬性 Class clz = User.class; Field field2 = clz.getDeclaredField("age"); field2.setAccessible(true); field2.set(user,123);

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

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

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

method1.setAccessible(true); Object name = method1.invoke(user);   method2.setAccessible(true); method2.invoke(user, "xixi");  Object age = method3.invoke(user);

反射優(yōu)缺點

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

  • 性能問題。由于用到動態(tài)類型(運行時才檢查類型),所以反射的效率比較低。但是對程序的影響比較小,除非對性能要求比較高。所以需要在兩者之間平衡。

  • 不夠安全。由于可以執(zhí)行一些私有的屬性和方法,所以可能會帶來安全問題。

  • 不易讀寫。當然這一點也有解決方案,比如jOOR庫,但是不適用于Android定義為final的字段。

Android中的應用

插件化(Hook)

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

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

// 獲取到當前的ActivityThread對象 Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Field currentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread"); currentActivityThreadField.setAccessible(true); Object currentActivityThread = currentActivityThreadField.get(null);  //獲取這個對象的mH Field mHField = activityThreadClass.getDeclaredField("mH"); mHField.setAccessible(true); Handler mH = (Handler) mHField.get(currentActivityThread);   //替換mh為我們自己的HandlerCallback Field mCallBackField = Handler.class.getDeclaredField("mCallback"); mCallBackField.setAccessible(true); mCallBackField.set(mH, new MyActivityThreadHandlerCallback(mH));

動態(tài)代理

動態(tài)代理的特點是不需要提前創(chuàng)建代理對象,而是利用反射機制在運行時創(chuàng)建代理類,從而動態(tài)實現(xiàn)代理功能。

public class InvocationTest implements InvocationHandler {     // 代理對象(代理接口)     private Object subject;      public InvocationTest(Object subject) {         this.subject = subject;     }     @Override     public Object invoke(Object object, Method method, Object[] args)             throws Throwable {         //代理真實對象之前         Object obj = method.invoke(subject, args);         //代理真實對象之后         return obj;     } }

三方庫(注解)

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

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

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

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

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

到此,相信大家對“Java中反射的作用是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學習!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI