美文网首页
Java反射修改static和final相关变量的思考

Java反射修改static和final相关变量的思考

作者: 丢底很远 | 来源:发表于2017-01-31 03:49 被阅读0次

问题

起初反射修改String的final变量,按理应该能够修改的,但是发现修改不了?但是如果把String赋值形式改为new String("xxx");,则可以,又是为什么?

  • 为什么反射修改 final String str = "" 变量修改不了,而 final String str = new String("xxx") 形式可以修改?

  • 为什么反射修改非static的final变量不消去final不会报错,而static&final变量不消去final修饰符会报错?

结论

  • 反射修改final变量和是否static无关
  • 反射修改字段值,需要获取Field,底层是FieldAccessor接口,不同类型Field不同FieldAccessor实现,所以会导致非static的final变量不消去final不会报错,而static&final变量不消去final修饰符会报错
  • 具体值修改是通过unsafe.putObjectVolatile(var1, this.fieldOffset, var2);,所以修改类型底层数据结构,存储结构有关。因为final String str = "xxx" 是存储在方法区中的常量池中,是常量,而final String str = new String("xxx");也是存储在方法区中,但是是引用类型,所以引用类型的话就可以直接修改引用,而常量则不可以。
  • 注意:数组是引用类型。

问题解决方式

问题很奇怪,刚开始想了很久没思路,觉得debug一下

public void set(Object obj, Object value)
  throws IllegalArgumentException, IllegalAccessException {
  // 检查
  if (!override) {
    if (!Reflection.quickCheckMemberAccess(clazz, modifiers)){
      Class<?> caller = Reflection.getCallerClass();
      checkAccess(caller, clazz, obj, modifiers);
    }
  }
  // 获取FieldAccessor,并修改值
  getFieldAccessor(obj).set(obj, value);
}
  • 修改Field接口:FieldAccessor很关键
public FieldAccessor newFieldAccessor(Field var1, boolean var2) {
      checkInitted();
      return UnsafeFieldAccessorFactory.newFieldAccessor(var1, var2);
}
static FieldAccessor newFieldAccessor(Field var0, boolean var1) {
    Class var2 = var0.getType();
    boolean var3 = Modifier.isStatic(var0.getModifiers());
    boolean var4 = Modifier.isFinal(var0.getModifiers());
    boolean var5 = Modifier.isVolatile(var0.getModifiers());
    boolean var6 = var4 || var5;
    boolean var7 = var4 && (var3 || !var1);
    if(var3) {
        UnsafeFieldAccessorImpl.unsafe.ensureClassInitialized(var0.getDeclaringClass());
       // return ...;
    } else {
       // return ...;
    }
}
  • 在这里可以知道,在获取FieldAccessor的时候已经确定了static和final,不取消final会报错的原因跟FieldAccessor的实现有关
  • 这里不考虑var5,所以static final 变量 var7为true,final非static变量var7为false。
    if(var3) {
        UnsafeFieldAccessorImpl.unsafe.ensureClassInitialized(var0.getDeclaringClass());
        
        return (FieldAccessor)(!var6?
        (var2 == Boolean.TYPE? new UnsafeStaticBooleanFieldAccessorImpl(var0):
        (var2 == Byte.TYPE?new UnsafeStaticByteFieldAccessorImpl(var0):
        (var2 == Short.TYPE?new UnsafeStaticShortFieldAccessorImpl(var0):
        (var2 == Character.TYPE?new UnsafeStaticCharacterFieldAccessorImpl(var0):
        (var2 == Integer.TYPE?new UnsafeStaticIntegerFieldAccessorImpl(var0):
        (var2 == Long.TYPE?new UnsafeStaticLongFieldAccessorImpl(var0):
        (var2 == Float.TYPE?new UnsafeStaticFloatFieldAccessorImpl(var0):
        (var2 == Double.TYPE?new UnsafeStaticDoubleFieldAccessorImpl(var0):
        new UnsafeStaticObjectFieldAccessorImpl(var0))))))))):
        (var2 == Boolean.TYPE?new UnsafeQualifiedStaticBooleanFieldAccessorImpl(var0, var7):
        (var2 == Byte.TYPE?new UnsafeQualifiedStaticByteFieldAccessorImpl(var0, var7):
        (var2 == Short.TYPE?new UnsafeQualifiedStaticShortFieldAccessorImpl(var0, var7):
        (var2 == Character.TYPE?new UnsafeQualifiedStaticCharacterFieldAccessorImpl(var0, var7):
        (var2 == Integer.TYPE?new UnsafeQualifiedStaticIntegerFieldAccessorImpl(var0, var7):
        (var2 == Long.TYPE?new UnsafeQualifiedStaticLongFieldAccessorImpl(var0, var7):
        (var2 == Float.TYPE?new UnsafeQualifiedStaticFloatFieldAccessorImpl(var0, var7):
        (var2 == Double.TYPE?new UnsafeQualifiedStaticDoubleFieldAccessorImpl(var0, var7):
        new UnsafeQualifiedStaticObjectFieldAccessorImpl(var0, var7))))))))));
    } else {
        return (FieldAccessor)(!var6?(
        var2 == Boolean.TYPE?new UnsafeBooleanFieldAccessorImpl(var0):
        (var2 == Byte.TYPE?new UnsafeByteFieldAccessorImpl(var0):
        (var2 == Short.TYPE?new UnsafeShortFieldAccessorImpl(var0):
        (var2 == Character.TYPE?new UnsafeCharacterFieldAccessorImpl(var0):
        (var2 == Integer.TYPE?new UnsafeIntegerFieldAccessorImpl(var0):
        (var2 == Long.TYPE?new UnsafeLongFieldAccessorImpl(var0):
        (var2 == Float.TYPE?new UnsafeFloatFieldAccessorImpl(var0):
        (var2 == Double.TYPE?new UnsafeDoubleFieldAccessorImpl(var0):
        new UnsafeObjectFieldAccessorImpl(var0))))))))):
        (var2 == Boolean.TYPE?new UnsafeQualifiedBooleanFieldAccessorImpl(var0, var7):
        (var2 == Byte.TYPE?new UnsafeQualifiedByteFieldAccessorImpl(var0, var7):
        (var2 == Short.TYPE?new UnsafeQualifiedShortFieldAccessorImpl(var0, var7):
        (var2 == Character.TYPE?new UnsafeQualifiedCharacterFieldAccessorImpl(var0, var7):
        (var2 == Integer.TYPE?new UnsafeQualifiedIntegerFieldAccessorImpl(var0, var7):
        (var2 == Long.TYPE?new UnsafeQualifiedLongFieldAccessorImpl(var0, var7):
        (var2 == Float.TYPE?new UnsafeQualifiedFloatFieldAccessorImpl(var0, var7):
        (var2 == Double.TYPE?new UnsafeQualifiedDoubleFieldAccessorImpl(var0, var7):
        new UnsafeQualifiedObjectFieldAccessorImpl(var0, var7))))))))));
    }
  • 对于static、final变量,var3为true,var7为true,会创建UnsafeQualifiedStaticObjectFieldAccessorImpl类

  • 对于非static变量,var3为false,var7为true,会创建UnsafeQualifiedObjectFieldAccessorImpl类

static final的FieldAccessor

UnsafeQualifiedStaticObjectFieldAccessorImpl
// UnsafeQualifiedStaticObjectFieldAccessorImpl 父类
abstract class UnsafeQualifiedStaticFieldAccessorImpl extends UnsafeStaticFieldAccessorImpl {
    protected final boolean isReadOnly;

    UnsafeQualifiedStaticFieldAccessorImpl(Field var1, boolean var2) {
        super(var1);
        this.isReadOnly = var2;
    }
}
  • 可以看到这里的 isReadOnly 为true
设置Field值方法
public void set(Object var1, Object var2) throws IllegalArgumentException, IllegalAccessException {
    if(this.isReadOnly) {
        // 错误所在
        this.throwFinalFieldIllegalAccessException(var2);
    }

    if(var2 != null && !this.field.getType().isAssignableFrom(var2.getClass())) {
        this.throwSetIllegalArgumentException(var2);
    }

    unsafe.putObjectVolatile(this.base, this.fieldOffset, var2);
}
  • 可以看到,会先判断 isReadOnly变量(是否只读),如果没有取消final修饰符则会在这里抛出异常
  • 具体值的修改是通过 unsafe.putObjectVolatile(var1, this.fieldOffset, var2);进行。

final非static的FieldAccessor

UnsafeQualifiedObjectFieldAccessorImpl
// UnsafeQualifiedObjectFieldAccessorImpl 父类,同UnsafeQualifiedStaticObjectFieldAccessorImpl
abstract class UnsafeQualifiedFieldAccessorImpl extends UnsafeFieldAccessorImpl {
    protected final boolean isReadOnly;

    UnsafeQualifiedFieldAccessorImpl(Field var1, boolean var2) {
        super(var1);
        this.isReadOnly = var2;
    }
}
  • 这里的 isReadOnly 上面讲为false,所以不会判断是否可读

参考:

  • Why does reflection fail to update a static field?

如果上述有哪些错误或者不足,或者其他方面的思考,希望您能够指出,Thx!

相关文章

  • Java反射修改static和final相关变量的思考

    问题 起初反射修改String的final变量,按理应该能够修改的,但是发现修改不了?但是如果把String赋值形...

  • JAVA 反射修改static,final修饰的变量

    调用对应Class的getDeclaredField或getField方法,获取要修改的Filed;2个方法的差别...

  • Java 面向对象2

    Java 面向对象 1. static 和 final static关键字---修饰成员变量 用static修饰的...

  • final关键字

    1.final的具体使用场景 final能够修改变量,方法和类。 1.1修饰变量 在java中变量可以分为成员变量...

  • mmkv源码解析(java)

    一、成员变量 //错误恢复策略相关 private static final EnumMap

  • Java中的static和final

    java中的static和final 此篇文章是记录我在学习Java中static和final关键字的笔记。 st...

  • Java枚举

    背景 Java枚举类型是java1.5后提出的,解决了使用public static final定义的变量的类型安...

  • 如何对Java代码进行基本优化

    1、尽量不要使用static、final修饰符和局部变量 final修饰符修饰的变量值不可以修改,修饰的方法不可以...

  • N5.静态变量、常量和方法

    由static关键字修饰的变量和方法被称做静态变量和方法,用final static修饰一个成员变量,这个成员...

  • 工作细节

    1 类中变量声明规范,mHandler 2 static final 的变量,全大写 public static ...

网友评论

      本文标题:Java反射修改static和final相关变量的思考

      本文链接:https://www.haomeiwen.com/subject/gkohittx.html