Android Java反射與Proxy動(dòng)態(tài)代理詳解與使用基礎(chǔ)篇(一)
一、介紹
什么是反射?
反射是java語(yǔ)言的一個(gè)特性,它允程序在運(yùn)行時(shí)(注意不是編譯的時(shí)候)來進(jìn)行自我檢查并且對(duì)內(nèi)部的成員進(jìn)行操作。
反射是在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法,對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意方法和屬性,這種動(dòng)態(tài)獲取信息以及動(dòng)態(tài)調(diào)用對(duì)象方法的功能稱為java語(yǔ)言的反射機(jī)制。
為什么要用反射?
Java Reflection API簡(jiǎn)介
Java Reflection功能非常強(qiáng)大,并且非常有用,比如:
- 獲取任意類的名稱、package信息、所有屬性、方法、注解、類型、類加載器等
- 獲取任意對(duì)象的屬性,并且能改變對(duì)象的屬性
- 調(diào)用任意對(duì)象的方法
- 判斷任意一個(gè)對(duì)象所屬的類
- 實(shí)例化任意一個(gè)類的對(duì)象
- 通過反射我們可以實(shí)現(xiàn)動(dòng)態(tài)裝配,降低代碼的耦合度,動(dòng)態(tài)代理等。
在JDK中,主要由以下類來實(shí)現(xiàn)Java反射機(jī)制,這些類(除了第一個(gè))都位于java.lang.reflect包中
Class類:代表一個(gè)類,位于java.lang包下。
Field類:代表類的成員變量(成員變量也稱為類的屬性)。
Method類:代表類的方法。
Constructor類:代表類的構(gòu)造方法。
Array類:提供了動(dòng)態(tài)創(chuàng)建數(shù)組,以及訪問數(shù)組的元素的靜態(tài)方法。
二、反射的使用
1、創(chuàng)建類對(duì)象
正常我們創(chuàng)建類對(duì)象都是通過new關(guān)鍵字來創(chuàng)建,但是我們還有其他兩種方式
通過 Class 對(duì)象的 newInstance() 方法
通過 Constructor 對(duì)象的 newInstance() 方法
基礎(chǔ)數(shù)據(jù):
package com.example.mylibrary.ref;
public class MyPerson {
private String name = "default";
public int age = 2;
protected boolean sex;
private MyPerson(String name) {
this.name = name;
}
protected MyPerson(int age) {
this.age = age;
}
public MyPerson() {
}
public MyPerson(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
private String getPrivateName() {
return name;
}
protected int getProtectAge() {
return age;
}
}
1.1通過 Class 對(duì)象的 newInstance() 方法
Class cls=Class.forName("com.example.mylibrary.ref.MyPerson"); MyPerson person=(MyPerson) cls.newInstance();
1.2通過 Constructor 對(duì)象的 newInstance() 方法
Constructor constructor=cls.getConstructor(); MyPerson myPerson=(MyPerson) constructor.newInstance();
以上都是構(gòu)造器無(wú)默認(rèn)參數(shù),如果有的話1.1已無(wú)法滿足,且1.1的方法在java9的時(shí)候已過期,接下我們主講1.2通過Constructor 創(chuàng)作對(duì)象
有參構(gòu)造:
1、public Constructor
要指定構(gòu)造器的參數(shù)類型,必須一一對(duì)其
2、public T newInstance(Object... initargs)
在獲取對(duì)象時(shí),需要傳入默認(rèn)參數(shù)
Constructor constructor=cls.getConstructor(String.class,Integer.TYPE); MyPerson myPerson=(MyPerson) constructor.newInstance("你好",12);
1.3.獲得成員變量Field
獲取成員變量有四種:
1、Field[] fields = cls.getFields(); 2、Field field = cls.getField("age"); 3、Field[] dcfis = cls.getDeclaredFields(); 4、Field dfied = cls.getDeclaredField("name");
getFields:獲取所有public的屬性變量
getField:獲取指定為public的屬性變量對(duì)象
getDeclaredFields:獲取所有變量,包括public、protect、private
getDeclaredField:獲取任性一個(gè)屬性對(duì)象,包括public、protect、private
看如下debug日志:

Field對(duì)象API介紹:
Field是獲取變量,變量就有修改值和獲取變量對(duì)象。
1.修改參數(shù)
field.set(cls,value)
2.獲取變量
Object value=field.get(cls);
value:就是對(duì)象本身
3.獲取對(duì)象類型
field.getType()
注意:
1.通過getDeclared獲取的是全部,如果方法前面沒有Declared基本獲取的就是public
2.如果field設(shè)置value異常不生效,需要設(shè)置field.setAccessible(true)
1.4構(gòu)造器的獲取
構(gòu)造器獲取區(qū)分public和全部,通過如下方式獲取
Constructor[] construPublic= cls.getConstructors();//public Constructor[] consrAll=cls.getDeclaredConstructors();//all Log.log(construPublic.length+""); Log.log(consrAll.length+"");

1.5.類的方法Method
Method是class中的方法,方法的獲取如下
1.Method[] methods = cls.getMethods();
這種獲取到的方法是當(dāng)前類下可用的所有public方法,包括class自身的一些方法
如
getMethods=wait
getMethods=equals
getMethods=toString
getMethods=hashCode
getMethods=getClass
getMethods=notify
getMethods=notifyAll
2.Method[] methodDecs= cls.getDeclaredMethods();
這種獲取的是當(dāng)前類下的所有方法,不包括class自身提供的,只有用戶定義的所有類型
getDeclaredMethods=getName
getDeclaredMethods=setName
getDeclaredMethods=getAge
getDeclaredMethods=setAge
getDeclaredMethods=getProtectAge
getDeclaredMethods=getPrivateName
1.6. 如何調(diào)用Method
先準(zhǔn)備一個(gè)對(duì)象:MyPerson person=new MyPerson();
1.無(wú)參數(shù)調(diào)用
Method method_getAge = cls.getMethod("getAge"); method_getAge.setAccessible(true); Object o = method_getAge.invoke(new MyPerson());
2.有參調(diào)用
Method method_setAge = cls.getMethod("setAge", Integer.TYPE); method_setAge.setAccessible(true);
method_setAge.invoke(person,122);
通過Method設(shè)置或者獲取都一樣,方法的調(diào)用通過invoke(),必須傳入當(dāng)前對(duì)象,后面有多少個(gè)參數(shù)就一一對(duì)應(yīng),沒有就不傳。
這樣基本就完成了反射的作用
注意:
Method提供了getDefaultValue(),這個(gè)方法是獲取注解提供的默認(rèn)值,不是獲取目標(biāo)類的值。如果不是通過注解設(shè)置默認(rèn)值,返回的是null。
1.7.內(nèi)部類的獲取
我們上面都是介紹單獨(dú)類,有些人說如果我的類是類部類怎么獲???其實(shí)很簡(jiǎn)單
Class clsParent = Class.forName("com.example.mylibrary.ref.Parent"); Class clsChild = Class.forName(clsParent.getName() + "$Child");
path=父類+$+內(nèi)部類名稱
二、動(dòng)態(tài)代理Proxy
掌握了基本的反射使用,那我們繼續(xù)了解動(dòng)態(tài)代理。我們經(jīng)常聽到Hook,鉤子說法很懸乎,也很科幻,一些公司面試喜歡聞,你會(huì)Hook嘛?其實(shí)就這些。沒有那么懸乎,剩下的就是Hook涉及到的復(fù)雜場(chǎng)景,任何復(fù)雜的代碼都是從基礎(chǔ)做起。
動(dòng)態(tài)代理通過Proxy類完成
1、獲取一個(gè)代理對(duì)象:newProxyInstance
public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h) { Objects.requireNonNull(h); Class> caller = System.getSecurityManager() == null ? null : Reflection.getCallerClass(); Constructor> cons = getProxyConstructor(caller, loader, interfaces); return newProxyInstance(caller, cons, h); }
參數(shù)介紹
1.1、loader:加載器
1.2、interfaces:接口類接口,代理是通過代理類的接口進(jìn)行攔截,而不是直接修改方法體
1.3、InvocationHandler:攔截器
攔截器介紹:
public Object invoke(Object proxy, Method method, Object[] objects)
1、proxy:代理對(duì)象
2、method:代理對(duì)象調(diào)用的方法
3、objects:參數(shù)
小試牛刀:
public static void main(String[] args) throws Exception {
final PersonInterface person = new Child("sun");
Object proxy = Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
String name = method.getName();
Log.log("Object o, Method method, Object[] objects----------");
if (method.getName().equals("setName")) {
String item=(String) objects[0];
objects[0]="modify="+item;
}
return method.invoke(person, objects);
}
});
if (proxy instanceof PersonInterface) {
PersonInterface testPerson = (PersonInterface) proxy;
testPerson.setName("proxy");
}
PersonInterface testPerson = (PersonInterface) proxy;
testPerson.setName("proxy");
Log.log(testPerson.getName());
}
package com.example.mylibrary.ref;
import com.example.mylibrary.Log;
public class Child implements PersonInterface {
String name = "";
public Child(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
public void show() {
Log.log("name=" + name);
}
}
package com.example.mylibrary.ref;
public interface PersonInterface {
public String getName();
public void setName(String name);
}

DEBUG日志
這樣,我們就完成了Hook了。記住,動(dòng)態(tài)代理是攔截接口,而不是修改方法體
本文僅代表作者觀點(diǎn),版權(quán)歸原創(chuàng)者所有,如需轉(zhuǎn)載請(qǐng)?jiān)谖闹凶⒚鱽碓醇白髡呙帧?/p>
免責(zé)聲明:本文系轉(zhuǎn)載編輯文章,僅作分享之用。如分享內(nèi)容、圖片侵犯到您的版權(quán)或非授權(quán)發(fā)布,請(qǐng)及時(shí)與我們聯(lián)系進(jìn)行審核處理或刪除,您可以發(fā)送材料至郵箱:service@tojoy.com






