final修飾的變量是否能夠通過反射更改


首先給出結論,當定義基本數據類型的變量並且同時賦值的時候,該變量是無法通過反射更改.

此時由於JVM編譯優化機制,任何引用該變量的地方得到都是常量,上簡單代碼:

定義三個final變量,其中兩個為基本數據類型(int和string)


public class TestReflection {
final int primitiveInt = 42;
final Integer wrappedInt = 42;
final String stringValue = "42";
public int getPrimitiveInt() {
return this.primitiveInt;
}
public int getWrappedInt() {
return this.wrappedInt;
}
public String getStringValue() {
return this.stringValue;
}
public void changeField(String name, Object value) throws IllegalAccessException, NoSuchFieldException {
Field field = TestReflection.class.getDeclaredField(name);
field.setAccessible(true);
// 去除final修飾符,final,public等限定符在class文件中以16進制數存儲,詳見《深入理解java虛擬機》-6.3.5
Field modifiers_field = Field.class.getDeclaredField("modifiers");
modifiers_field.setAccessible(true);
modifiers_field.set(field, field.getModifiers() & ~Modifier.FINAL);

field.set(this, value);
System.out.println("reflection: " + name + " = " + field.get(this) );
}

}
下面是測試類
public class AppforReflection {
public static void main(String[] args) {
try {
TestReflection test = new TestReflection();

test.changeField("primitiveInt", 84);
System.out.println("direct: primitiveInt = " + test.getPrimitiveInt());

test.changeField("wrappedInt", 84);
System.out.println("direct: wrappedInt = " + test.getWrappedInt());

test.changeField("stringValue", "84");
System.out.println("direct: stringValue = " + test.getStringValue());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
結果如下:

reflection: primitiveInt = 84
direct: primitiveInt = 42
reflection: wrappedInt = 84
direct: wrappedInt = 84
reflection: stringValue = 84
direct: stringValue = 42
可以看到integer類型的變量被更改為84而int和string類型的變量依然為42
但是有兩種方法可以使其發生改變:

方法1.改變賦值方式,取消編譯時的自動優化,比如string如下賦值,int不變


final String stringValue = (null!=null?"":"42");
結果為:
reflection: primitiveInt = 84
direct: primitiveInt = 42
reflection: wrappedInt = 84
direct: wrappedInt = 84
reflection: stringValue = 84
direct: stringValue = 84//這里改變了
方法2.先定義后賦值:

static final int primitiveInt;
static final Integer wrappedInt;
static String stringValue;

static {//這里改為用靜態代碼塊賦值
primitiveInt = 42;
wrappedInt = 42;
stringValue = "42";
}
結果如下:

reflection: primitiveInt = 84
direct: primitiveInt = 84
reflection: wrappedInt = 84
direct: wrappedInt = 84
reflection: stringValue = 84
direct: stringValue = 84
有了以上的認知,我們可以嘗試改變源碼中的變量,如Integer內部類IntegerCache里的字段(low無法更改,但可以改變high和cache)

private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[]; }
代碼如下:
public class App {
static {
try {

// Class<?> clazz = Class.forName("java.lang.Integer$IntegerCache");
Class<?> clazz = Integer.class.getDeclaredClasses()[0];
// 三個屬性都是25 public static final 1+8+16
Field cache = clazz.getDeclaredField("cache");// [Ljava.lang.Integer;
Field low = clazz.getDeclaredField("low");// int
Field high = clazz.getDeclaredField("high");

// static final
// System.out.println(Modifier.toString(cache.getModifiers()));

cache.setAccessible(true);
low.setAccessible(true);
high.setAccessible(true);

/* 去除final修飾符的影響,將字段設為可修改的 */
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);

modifiersField.set(high, high.getModifiers() & ~Modifier.FINAL);
modifiersField.set(low, low.getModifiers() & ~Modifier.FINAL);
modifiersField.set(cache, cache.getModifiers() & ~Modifier.FINAL);

high.set(null, 1000);
low.set(low, -1001);//不起作用

/* 修改cache數組的信息,將數組的大小和內容都修改 */
Integer[] ca = new Integer[3000];
int j = -1001;
for (int k = 0; k < ca.length; k++)
ca[k] = new Integer(j++);
cache.set(null, ca);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Integer a = 160;
System.out.println(a);
}

}
結果你會發現
before modifying :static final
after modifying :static
-713
至於為什么a是-713,可以看一下integer這段源碼:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
這里我的IntegerCache.high已經更改為了1000,cache一並進行了更改,cache[0]為-1001,
而a的值是160在-128和1000之內所以走if語句

返回的就是cache[160+(-(-128))],即cache[288]
————————————————
版權聲明:本文為CSDN博主「a469357594」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/a469357594/article/details/79166621


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM