在學(xué)習(xí)Java的過程中坛吁,很多框架的原理都會(huì)涉及到反射知識(shí)摩幔,如果你對(duì)反射不太了解甚至都沒看過彤委,那么你的提升會(huì)有點(diǎn)慢。在面試當(dāng)中热鞍,你說你會(huì)用一個(gè)框架葫慎,并不能體現(xiàn)出你的什么能力,但是你說你會(huì)用而且還能分析框架的原理薇宠,還有能力舉一反三自己造個(gè)輪子偷办。那么面試官就會(huì)對(duì)你刮目想看了。當(dāng)然學(xué)習(xí)的過程是漫長(zhǎng)的澄港,一口也吃不成一個(gè)胖子椒涯,帶著自己心里的那個(gè)夢(mèng)想堅(jiān)持下去。
概念
定義
Java反射機(jī)制是在運(yùn)行狀態(tài)中回梧,對(duì)于任意一個(gè)類废岂,都能夠知道這個(gè)類中的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象狱意,都能夠調(diào)用它的任意一個(gè)方法和屬性湖苞;這種動(dòng)態(tài)獲取的信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱為java語言的反射機(jī)制。
作用
在運(yùn)行時(shí)判斷任意一個(gè)對(duì)象所屬的類详囤。
在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象财骨。
在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法。
在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法藏姐。
生成動(dòng)態(tài)代理隆箩。
應(yīng)用場(chǎng)景
逆向代碼 ,例如反編譯
與注解相結(jié)合的框架 例如Retrofit
單純的反射機(jī)制應(yīng)用框架 例如EventBus
動(dòng)態(tài)生成類框架 例如Gson
反射的簡(jiǎn)單使用
我這先寫一個(gè)User類方便后面的講解
public class User {
private String name;
private int age;
public String sex;
public User() {
}
public User(String name) {
this.name = name;
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public User(String name, Integer age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
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;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
/**
* 測(cè)試方法
* @param msg
* @return
*/
public String textMethed(String msg) {
return msg;
}
}
拿到Class對(duì)象
每個(gè)類被加載之后羔杨,系統(tǒng)就會(huì)為該類生成一個(gè)對(duì)應(yīng)的Class對(duì)象捌臊。通過該Class對(duì)象就可以訪問到JVM中的這個(gè)類。
獲取Class對(duì)象有如下三種方式:
使用Class類的forName(String clazzName)靜態(tài)方法兜材。該方法需要傳入字符串參數(shù)理澎,該字符串參數(shù)的值是某個(gè)類的全限定名(必須添加完整包名)逞力。
調(diào)用某個(gè)類的class屬性來獲取該類對(duì)應(yīng)的Class對(duì)象。
調(diào)用某個(gè)對(duì)象的getClass()方法糠爬。該方法是java.lang.Object類中的一個(gè)方法掏击。
public static void getClas(){
Class uClass;
//三種獲取class對(duì)象的方式
try {
uClass = Class.forName("com.zhong.reflect.User");//1
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
uClass = User.class;//2
uClass = new User().getClass();//3
}
我們拿到了這個(gè)類的class對(duì)象那么我們能對(duì)這個(gè)類做些什么操作呢?
反射查看信息
獲取對(duì)象中的成員屬性
public void getField(Class uClass) {
Field[] allFields = uClass.getDeclaredFields();//獲取class對(duì)象的所有屬性
Field[] publicFields = uClass.getFields();//獲取class對(duì)象的public屬性
try {
Field ageField = uClass.getDeclaredField("age");//獲取class指定屬性
Field desField = uClass.getField("sex");//獲取class指定的public屬性
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
獲取對(duì)象中的成員方法
public static void getMethed(Class uClass) {
Method[] methods = uClass.getDeclaredMethods();//獲取class對(duì)象的所有聲明方法
Method[] allMethods = uClass.getMethods();//獲取class對(duì)象的所有public方法 包括父類的方法
try {
Method method = uClass.getMethod("textMethed", String.class);//返回次Class對(duì)象對(duì)應(yīng)類的秩铆、帶指定形參列表的public方法
Method declaredMethod = uClass.getDeclaredMethod("textMethed", String.class);//返回次Class對(duì)象對(duì)應(yīng)類的砚亭、帶指定形參列表的方法
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
獲取對(duì)象中的構(gòu)造方法
public static void getConstructor(Class uClass) {
Constructor<?>[] allConstructors = uClass.getDeclaredConstructors();//獲取class對(duì)象的所有聲明構(gòu)造函數(shù)
Constructor<?>[] publicConstructors = uClass.getConstructors();//獲取class對(duì)象public構(gòu)造函數(shù)
try {
Constructor<?> constructor = uClass.getDeclaredConstructor(String.class);//獲取指定聲明構(gòu)造函數(shù)
Constructor publicConstructor = uClass.getConstructor(String.class);//獲取指定聲明的public構(gòu)造函數(shù)
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
獲取對(duì)象的class對(duì)象的信息
boolean isPrimitive = uClass.isPrimitive();//判斷是否是基礎(chǔ)類型
boolean isArray = uClass.isArray();//判斷是否是集合類
boolean isAnnotation = uClass.isAnnotation();//判斷是否是注解類
boolean isInterface = uClass.isInterface();//判斷是否是接口類
boolean isEnum = uClass.isEnum();//判斷是否是枚舉類
boolean isAnonymousClass=uClass.isAnonymousClass();//判斷是否是匿名內(nèi)部類
boolean isAnnotationPresent=uClass.isAnnotationPresent(Deprecated.class);//判斷是否被某個(gè)注解類修飾
String className = uClass.getName();//獲取class名字 包含包名路徑
Package aPackage = uClass.getPackage();//獲取class的包信息
String simpleName = uClass.getSimpleName();//獲取class類名
int modifiers = uClass.getModifiers();//獲取class訪問權(quán)限
Class<?>[] declaredClasses = uClass.getDeclaredClasses();//內(nèi)部類
Class<?> declaringClass = uClass.getDeclaringClass();//外部類
反射操作對(duì)象
創(chuàng)建對(duì)象
使用Class對(duì)象的newInstance()方法來創(chuàng)建該Class對(duì)象對(duì)應(yīng)類的實(shí)例。這種方式要求該Class對(duì)象的對(duì)應(yīng)類有默認(rèn)構(gòu)造器殴玛,而執(zhí)行newInstance()方法時(shí)實(shí)際上是利用默認(rèn)構(gòu)造器來創(chuàng)建該類的實(shí)例捅膘。
先使用Class對(duì)象獲取指定的Constructor對(duì)象,再調(diào)用Constructor對(duì)象的newInstance()方法來創(chuàng)建該Class對(duì)象對(duì)應(yīng)類的實(shí)例滚粟。通過這種方式可以選擇使用指定的構(gòu)造器來創(chuàng)建實(shí)例寻仗。
public static void newIns() throws Exception {
Class uClass = Class.forName("com.zhong.reflect.User");
//第一種方式 Class對(duì)象調(diào)用newInstance()方法生成
User user = (User) uClass.newInstance();
//第二種方式 對(duì)象獲得對(duì)應(yīng)的Constructor對(duì)象,再通過該Constructor對(duì)象的newInstance()方法生成
Constructor<?> constructor =uClass.getDeclaredConstructor(String.class, Integer.class,String.class);//獲取指定聲明構(gòu)造函數(shù)
user = (User) constructor.newInstance("小米", 20, "男");
}
調(diào)用方法
上面我們演示了如何獲取對(duì)象中方法凡壤,現(xiàn)在我們需要調(diào)用這個(gè)方法
public static void invokeMethed() throws Exception {
Class uClass = Class.forName("com.zhong.reflect.User");
//創(chuàng)建一個(gè)空對(duì)象
Object obj = uClass.newInstance();
//獲取指定參數(shù)的指定方法
Method textMethed = uClass.getMethod("textMethed", String.class);
//調(diào)用方法
Object str = textMethed.invoke(obj, "小米");
System.out.println(str);
}
注意:
當(dāng)通過Method的invoke()方法來調(diào)用對(duì)應(yīng)的方法時(shí)署尤,Java會(huì)要求程序必須有調(diào)用該方法的權(quán)限。如果程序確實(shí)需要調(diào)用某個(gè)對(duì)象的private方法亚侠,則可以先調(diào)用Method對(duì)象的setAccessible(boolean flag)方法曹体。
setAccessible(boolean flag):
將Method對(duì)象的acessible設(shè)置為指定的布爾值。值為true硝烂,指示該Method在使用時(shí)應(yīng)該取消Java語言的訪問權(quán)限檢查箕别;值為false,則知識(shí)該Method在使用時(shí)要實(shí)施Java語言的訪問權(quán)限檢查滞谢。
訪問成員變量值
public static void changeField() throws Exception{
Class uClass = Class.forName("com.zhong.reflect.User");
Object obj = uClass.newInstance();
//獲取一個(gè)私有屬性
Field field = uClass.getDeclaredField("age");
//設(shè)置權(quán)限
field.setAccessible(true);
field.setInt(obj,10);
int age = field.getInt(obj);
System.out.println(age);
}