通过反射更改私有最终字段
class WithPrivateFinalField {
private final String s = "I’m totally safe";
public String toString() {
return "s = " + s;
}
}
WithPrivateFinalField pf = new WithPrivateFinalField();
System.out.println(pf);
Field f = pf.getClass().getDeclaredField("s");
f.setAccessible(true);
System.out.println("f.get(pf): " + f.get(pf));
f.set(pf, "No, you’re not!");
System.out.println(pf);
System.out.println(f.get(pf));
输出:
s = I’m totally safe
f.get(pf): I’m totally safe
s = I’m totally safe
No, you’re not!
为什么会这样,你能解释一下吗?第一个打印告诉我们私有“s”字段没有改变,正如我所期望的那样。但是,如果我们通过反射获取该字段,则第二个打印显示它已更新。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这个答案对于这个主题来说非常详尽。
JLS 17.5.3 最终字段的后续修改
但是,如果您仔细阅读上面的段落,您可能会在这里找到一种方法(在构造函数中而不是在字段定义中设置
private final
字段):输出如下:
Hope这有点帮助。
This answer is more than exhaustive on the topic.
JLS 17.5.3 Subsequent Modification of Final Fields
But, if you read the paragraph above very carefully, you may find a way around here (set the
private final
field in the constructor instead of in the field definition):The output is then as follows:
Hope this helps a bit.
这
实际上是这样编译的:
也就是说,编译时常量被内联。请参阅这个问题。避免内联的最简单方法是像这样声明
String
:对于其他类型,一个简单的方法调用即可解决问题:
This
actually compiles like this:
That is, compile-time constants get inlined. See this question. The easiest way to avoid inlining is to declare the
String
like this:For other types, a trivial method call does the trick:
下面是
WithPrivateFinalField
类文件的反编译(为了简单起见,我将其放在一个单独的类中):注意在
toString()
方法中,地址 0 处使用的常量 [0 ldc; [23]
] 显示编译器已经提前将字符串文字"s = "
和私有最终字段" I'm Excellent safe"
连接在一起,存储它。无论实例变量s
如何变化,toString() 方法都将始终返回"s = I'm Excellent safe"
。Here's a decompile of
WithPrivateFinalField
class file (I put it in a separate class for simplicity):Note in the
toString()
method, the constant used at address 0 [0 ldc <String "s = I’m totally safe"> [23]
] shows the compiler already concatenated the string literal"s = "
and the private final field" I’m totally safe"
together in advance and stored it. The toString() method will always return"s = I’m totally safe"
regardless of how the instance variables
changes.作为
final
,编译器希望该值不会更改,因此它可能将字符串直接硬编码到toString
方法中。Being
final
, the compiler expected the value not to change, so it probably hardcoded the string directly into yourtoString
method.