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

posted on 2021-05-11 11:24  德邦总管  阅读(1274)  评论(0编辑  收藏  举报

导航