目錄
一、什么是反射
二、反射的作用和應(yīng)用場(chǎng)景
三、反射的優(yōu)缺點(diǎn)
四、使用反射(獲取Class對(duì)象、獲取類的構(gòu)造方法及其參數(shù)類型和修飾類型、通過構(gòu)造函數(shù)的newInstance方法創(chuàng)建類的實(shí)例、獲取類的方法并調(diào)用它、獲取和修改類的屬性 等等)
五、反射問題探索
一、什么是反射
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ī)制。通俗點(diǎn)講,通過反射,該類對(duì)我們來說是完全透明的,想要獲取任何東西都可以。
二、反射的作用和應(yīng)用場(chǎng)景
作用
- 獲取任意類的名稱、package 信息、所有屬性、方法、注解、類型、類加載器、modifiers、父類、現(xiàn)實(shí)接口等
- 獲取任意對(duì)象的屬性,并且能改變對(duì)象的屬性
- 調(diào)用任意對(duì)象的方法
- 判斷任意一個(gè)對(duì)象所屬的類
- 實(shí)例化任意一個(gè)類的對(duì)象
- 生成動(dòng)態(tài)代理。
應(yīng)用場(chǎng)景
1.實(shí)例化系統(tǒng)隱藏的類
2.調(diào)用對(duì)象被隱藏起來的屬性和方法
3.動(dòng)態(tài)代理
4.與注解的結(jié)合使用
5.在編譯時(shí)無法知道該對(duì)象或類可能屬于哪些類,程序在運(yùn)行時(shí)獲取對(duì)象和類的信息
反射的作用和應(yīng)用場(chǎng)景有點(diǎn)類似了,我們只要記住反射的作用,就能在你遇到問題的時(shí)候想起它。反射一般是用來開發(fā)框架,在我們平常的開發(fā)中,反射用得并不多,但是我們必須搞懂它,它可以幫助我們理解一些框架的原理,有一句很經(jīng)典的話就是:反射是框架設(shè)計(jì)的靈魂
三、反射的優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
提高了 Java 程序的靈活性和擴(kuò)展性,降低耦合性,提高自適應(yīng)能力。
允許程序創(chuàng)建和控制任何類的對(duì)象,無需提前硬編碼目標(biāo)類
應(yīng)用很廣,測(cè)試工具、框架都用到了反射
缺點(diǎn)
性能問題:反射是一種解釋操作,遠(yuǎn)慢于直接代碼。因此反射機(jī)制主要用在對(duì)靈活性和擴(kuò)展性要求很高的系統(tǒng)框架上,普通程序不建議使用
模糊程序內(nèi)部邏輯:反射繞過了源代碼,無法再源代碼中看到程序的邏輯,會(huì)帶來維護(hù)問題
增大了復(fù)雜性:反射代碼比同等功能的直接代碼更復(fù)雜
四、使用反射
1、獲取Class類對(duì)象,三種方式
//通過Class.forName()方法獲取
try {
Class test = Class.forName("com.mumumxi.test");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//通過對(duì)象的getClass()方法獲取
String string = "test";
Class test1 = string.getClass();
//直接通過類字面常量獲取
Class test2 = String.class;
2、獲取類的構(gòu)造方法及其參數(shù)類型和修飾語
package com.mumuxi.testapplication;
import java.lang.reflect.Constructor;
/**
* @author mumuxi
* @date 2020/1/19
*/
public class Test {
public static final String TAG = Test.class.getSimpleName();
public Test() {
}
public Test(int i) {
}
public Test(int i, String string) {
}
public static void main(String[] args) {
Class test = null;
try {
test = Class.forName("com.mumuxi.testapplication.Test");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//getDeclaredConstructors 返回類的所有構(gòu)造函數(shù),包括私有的
Constructor[] constructors = test.getDeclaredConstructors();
for (Constructor constructor : constructors) {
//通過getParameterTypes 獲取構(gòu)造函數(shù)的參數(shù)Class列表
for (Class name : constructor.getParameterTypes()) {
System.out.println(name.getName());
}
//通過getModifiers獲取構(gòu)造函數(shù)的修飾類型,是private的還是public..
System.out.println(constructor.getModifiers());
System.out.println("--------------------------------");
}
//getConstructors 返回public類型的構(gòu)造函數(shù)
Constructor[] constructors1 = test.getConstructors();
//獲取無參的構(gòu)造函數(shù)
try {
Constructor constructor = test.getDeclaredConstructor();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
//獲取只有一個(gè)參數(shù)的構(gòu)造函數(shù),參數(shù)類型是int
Class[] classes = {int.class};
try {
Constructor constructor1 = test.getDeclaredConstructor(classes);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
//獲取只有兩個(gè)參數(shù)的構(gòu)造函數(shù),參數(shù)類型依次是int 、String
Class[] classes1 = {int.class, String.class};
try {
Constructor constructor1 = test.getDeclaredConstructor(classes1);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
3、通過構(gòu)造函數(shù)的newInstance方法創(chuàng)建類的實(shí)例
package com.mumuxi.testapplication;
import android.util.Log;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* @author mumuxi
* @date 2020/1/19
*/
public class Test {
public static final String TAG = Test.class.getSimpleName();
public Test() {
}
public Test(int i) {
}
public Test(int i, String string) {
}
public static void main(String[] args) {
Class test = null;
try {
test = Class.forName("com.mumuxi.testapplication.Test");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Constructor constructor = null;
//調(diào)用無參構(gòu)造函數(shù)新建類的實(shí)例
if (test != null) {
try {
constructor = test.getDeclaredConstructor();
constructor.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
if (constructor != null) {
try {
Object object = constructor.newInstance();
if (object instanceof Test) {
System.out.println("create object success");
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
//調(diào)用含有一個(gè)參數(shù)的構(gòu)造函數(shù)新建類的實(shí)例
Class[] param = {int.class};
if (test != null) {
try {
constructor = test.getDeclaredConstructor(param);
constructor.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
if (constructor != null) {
try {
Object object = constructor.newInstance(1);
if (object instanceof Test) {
System.out.println("create object success");
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
//調(diào)用含有兩個(gè)參數(shù)的構(gòu)造函數(shù)新建類的實(shí)例
Class[] param1 = {int.class, String.class};
if (test != null) {
try {
constructor = test.getDeclaredConstructor(param1);
constructor.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
if (constructor != null) {
try {
Object object = constructor.newInstance(1, "hehe");
if (object instanceof Test) {
System.out.println("create object success");
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
4、獲取類的方法并調(diào)用它(私有、公共、靜態(tài))
package com.mumuxi.testapplication;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author mumuxi
* @date 2020/1/19
*/
public class Test {
private static final String TAG = Test.class.getSimpleName();
public static void main(String[] args) {
Test test = new Test();
Method method = null;
//獲取一個(gè)對(duì)象的私有或公共方法并調(diào)用它
try {
/*通過Class對(duì)象的getDeclaredMethod方法獲取指定方法,
第一個(gè)參數(shù)是方法名,第二個(gè)參數(shù)是方法的參數(shù)Class對(duì)象列表*/
method = Test.class.getDeclaredMethod("test", null);
//method 是私有類型的還需要通過setAccessible給予權(quán)限
method.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
if (method != null) {
try {
//通過invoke來執(zhí)行該方法第,一個(gè)參數(shù)是類實(shí)例,第二個(gè)是方法的參數(shù)列表
method.invoke(test, null);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
//獲取指定類的私有方法并調(diào)用它
try {
method = Test.class.getDeclaredMethod("test1");
//method 是私有類型的還需要通過setAccessible給予權(quán)限
method.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
if (method != null) {
try {
//通過invoke來執(zhí)行該方法第,一個(gè)參數(shù)是類實(shí)例,第二個(gè)是方法的參數(shù)列表
method.invoke(null);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
private void test() {
System.out.println("hehe");
}
public static void test1(){
System.out.println("hehe1");
}
}
5、獲取和修改類的屬性 (私有、公共、靜態(tài))
package com.mumuxi.testapplication;
import java.lang.reflect.Field;
/**
* @author mumuxi
* @date 2020/1/19
*/
public class Test {
public static final String TAG = Test.class.getSimpleName();
private String test = "test";
private static String TEST = "TEST";
public static void main(String[] args) {
Class test = null;
try {
test = Class.forName("com.mumuxi.testapplication.Test");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//獲取實(shí)例的指定字段并修改它的值
Object object = null;
if (test != null) {
try {
object = test.newInstance();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
Field field = null;
if (test != null) {
try {
//通過Class對(duì)象的getDeclaredField獲取指定屬性,參數(shù)是屬性名
field = test.getDeclaredField("test");
field.setAccessible(true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
if (field != null && object != null) {
try {
//通過Field的get方法獲取指定實(shí)例的字段,參數(shù)是具體的實(shí)例
Object fieldObject = field.get(object);
System.out.println("非靜態(tài)字段------------");
if (fieldObject instanceof String) {
System.out.println("String 類型");
}
System.out.println(fieldObject);
/*通過Field的set方法修改指定實(shí)例的字段
第一個(gè)參數(shù)是具體的實(shí)例,第二個(gè)參數(shù)是需要修改的值*/
field.set(object, "改掉了");
System.out.println(field.get(object));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
//修改類的靜態(tài)字段
if (test != null) {
try {
//通過Class對(duì)象的getDeclaredField獲取指定屬性,參數(shù)是屬性名
field = test.getDeclaredField("TEST");
field.setAccessible(true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
try {
System.out.println("靜態(tài)字段------------");
Object staticObject = field.get(null);
System.out.println(staticObject);
field.set(staticObject, "ABCD");
System.out.println(field.get(null));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
6、通過Class對(duì)象getSuperclass方法的獲取類的父類
Class test = null;
try {
test = Class.forName("com.mumuxi.testapplication.Test");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
if (test != null) {
Class superClass = test.getSuperclass();
}
7、獲取類的接口
Class test = null;
try {
test = Class.forName("com.mumuxi.testapplication.Test");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
if (test != null) {
Class[] interfaces = test.getInterfaces();
}
推薦使用 Joor反射庫(kù) ,可以參考Java反射庫(kù)jOOR簡(jiǎn)介
五、反射問題探索
1.Java中的final字段真的不能修改么?(怎樣修改final字段)
通過反射是可以修改final字段的!但是一般情況下不建議這么做,如果出現(xiàn)問題也比較難排查。
具體可以參考:https://blog.csdn.net/adu003/article/details/104376399
2.反射可以破壞單例嗎?
反射可以破壞單例,但是我們也可以通過一些方法防御被反射破壞單例。
3.反射的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
1.能夠運(yùn)行時(shí)動(dòng)態(tài)地獲取類的實(shí)例,提高靈活性
2.與動(dòng)態(tài)編譯結(jié)合
缺點(diǎn):
1)使用反射性能較低,需要解析字節(jié)碼,將內(nèi)存中的對(duì)象進(jìn)行解析。
解決方案:
1、通過setAccessible(true)關(guān)閉JDK的安全檢查來提升反射速度;
2、多次創(chuàng)建一個(gè)類的實(shí)例時(shí),有緩存會(huì)快很多
3、ReflflectASM工具類,通過字節(jié)碼生成的方式加快反射速度
2)相對(duì)不安全,破壞了封裝性(因?yàn)橥ㄟ^反射可以獲得私有方法和屬性)