zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    前端中不同页面之间传递参数的几种方式
    js中的闭包内存图
    【JavaScript基础】js中关于声明提前的几个误区
    【面试】前端面试题
    js继承的几种方式
    原生Ajax实现异步交互
    git基本使用
    一个简单的SSM框架实例(使用IDEA)
    高安全性的JavaScript
    高性能的JavaScript
  • 原文地址:https://www.cnblogs.com/zhncnblogs/p/14754584.html
Copyright © 2011-2022 走看看