一、反射是否可以修改final類型變量?
先看一段代碼
public class ReflectTest {
//被final修飾的變量
public final int number = 1 ;
public int getNumber() {
return number;
}
public static void main(String[] args) throws Exception {
ReflectTest reflectTest = new ReflectTest();
Field numberField = ReflectTest.class.getDeclaredField("number");
numberField.setAccessible(true);
//通過反射將number的值由1改為2
numberField.set(reflectTest, 2);
System.out.println(reflectTest.number);
}
}
運(yùn)行結(jié)果:
> Task :reflect:ReflectTest.main()
1
我明明通過反射將number的值由1 變成了 2 ,但是我們打印的結(jié)果還是1。
我們接下來看一下它的class文件

ReflectTest.class.jpg
最終:我們獲取的值還是1。
問題來了:反射難道就真的不能修改final的值嗎?
答案是可以的。
- 修改代碼1如下:
public class ReflectTest {
//編譯期間final類型的數(shù)據(jù)自動被優(yōu)化
public final int number = 1 ;
public static void main(String[] args) throws Exception {
ReflectTest reflectTest = new ReflectTest();
Field numberField = ReflectTest.class.getDeclaredField("number");
numberField .setAccessible(true);
numberField .set(reflectTest, 2);
// 通過反射方法,動態(tài)的去拿
System.out.println(numberField .get(reflectTest));
}
}
打印結(jié)果:
> Task :reflect:ReflectTest.main()
2
- 修改代碼2如下:
public class ReflectTest {
//編譯期間final類型的數(shù)據(jù)自動被優(yōu)化
public final int number ;
//todo 在構(gòu)造函數(shù)中初始化
public ReflectTest() {
number = 1;
}
public static void main(String[] args) throws Exception {
ReflectTest reflectTest = new ReflectTest();
Field numberField = ReflectTest.class.getDeclaredField("number");
numberField .setAccessible(true);
numberField .set(reflectTest, 2);
System.out.println(reflectTest.number);
}
}
打印結(jié)果:
> Task :reflect:ReflectTest.main()
2
二、反射為什么慢?
1.Method#invoke 需要進(jìn)行自動拆裝箱
1、反射的invoke方法的參數(shù)是 Object[] 類型,如果是基本數(shù)據(jù)類型會轉(zhuǎn)化為Integer裝箱,同時再包裝成Object數(shù)組。在執(zhí)行時候又會把數(shù)組拆解開,并拆箱為基本數(shù)據(jù)類型。拆箱和裝箱需要時間。
2、反射的Class.forName屬于native方法,native方法就要經(jīng)過語言執(zhí)行層面轉(zhuǎn)換。也就是java到C層再切換到Java層耗時。
2、需要檢查方法
反射時需要檢查方法可見性以及每個實際參數(shù)與形式參數(shù)的類型匹配性
2、編譯器無法對動態(tài)調(diào)用的代碼做優(yōu)化,比如內(nèi)聯(lián)
反射涉及到動態(tài)解析的類型,影響內(nèi)聯(lián)判斷并且無法進(jìn)行JIT