問題
起初反射修改String的final變量盗舰,按理應(yīng)該能夠修改的蕊程,但是發(fā)現(xiàn)修改不了局装?但是如果把String賦值形式改為new String("xxx");晤锹,則可以摩幔,又是為什么?
為什么反射修改 final String str = "" 變量修改不了鞭铆,而 final String str = new String("xxx") 形式可以修改幽崩?
為什么反射修改非static的final變量不消去final不會報錯严蓖,而static&final變量不消去final修飾符會報錯诲祸?
結(jié)論
- 反射修改final變量和是否static無關(guān)
- 反射修改字段值矛缨,需要獲取Field,底層是FieldAccessor接口舶担,不同類型Field不同F(xiàn)ieldAccessor實現(xiàn)坡疼,所以會導(dǎo)致非static的final變量不消去final不會報錯,而static&final變量不消去final修飾符會報錯
- 具體值修改是通過unsafe.putObjectVolatile(var1, this.fieldOffset, var2);衣陶,所以修改類型底層數(shù)據(jù)結(jié)構(gòu)柄瑰,存儲結(jié)構(gòu)有關(guān)。因為final String str = "xxx" 是存儲在方法區(qū)中的常量池中剪况,是常量教沾,而final String str = new String("xxx");也是存儲在方法區(qū)中,但是是引用類型译断,所以引用類型的話就可以直接修改引用授翻,而常量則不可以。
- 注意:數(shù)組是引用類型孙咪。
問題解決方式
問題很奇怪堪唐,剛開始想了很久沒思路,覺得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很關(guān)鍵
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的時候已經(jīng)確定了static和final,不取消final會報錯的原因跟FieldAccessor的實現(xiàn)有關(guān)
- 這里不考慮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寇荧,會創(chuàng)建UnsafeQualifiedStaticObjectFieldAccessorImpl類
對于非static變量举庶,var3為false,var7為true揩抡,會創(chuàng)建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
設(shè)置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);進(jìn)行峦嗤。
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装黑!