分析String的结构
通过反射可以很轻松地获取所有属性
// 获取所有属性
for (Field field : String.class.getDeclaredFields()) {
System.out.println(field);
}

方框框起来的 private final byte[] java.lang.String.value 即为需要的对象。
设置可见性
接下来就是常见的反射修改可见性。
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
然而这一步会报错: java.base does not “opens java.lang“ to unnamed module ,即非法访问警告。
这是因为 JDK 9 开始,除非模块标识为opens去允许反射访问,否则模块不能使用反射去访问非公有的成员/成员方法以及构造方法。解决方案为,设置VM启动参数 --add-opens=java.base/java.lang.invoke=ALL-UNNAMED
参照 非法访问异常 以及 IDEA设置VMoptions
编写显示函数
希望显示比较充分的信息,但这样反复调格式就太麻烦了,所以封装到函数里。由于是采用的 main 入口函数,所以需要写成静态方法。
private static void show(String s, String name, Field field) {
StringBuilder sb = new StringBuilder();
try {
sb.append("String ").append(name).append("@").append(s.hashCode()).append("{")
.append("value@").append(Integer.toHexString(field.get(s).hashCode())).append(" = ").append(s)
.append("}");
System.out.println(sb.toString());
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
编写主函数
public static void main(String[] args) {
String a = "a";
String b = "b";
String c = "a";
// 获取所有属性
for (Field field : String.class.getDeclaredFields()) {
System.out.println(field);
}
try {
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
show(a, "a", field);
show(b, "b", field);
show(c, "c", field);
field.set(a, field.get(b));
show(a, "a", field);
show(b, "b", field);
show(c, "c", field);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
执行效果
String a@97{value@568db2f2 = a}
String b@98{value@378bf509 = b}
String c@97{value@568db2f2 = a}
String b@97{value@378bf509 = b}
String b@98{value@378bf509 = b}
String c@97{value@378bf509 = b}
其中前三行是执行前,后三行是执行后。
值得注意的是,第四行原本是希望显示为:
String a@97{value@378bf509 = b}
而实际结果为:

这说明我们成功地修改了常量池中字符串 "a" 的值,使其值为 private final byte[] value = {'b'}
这也就有了题目,在main函数的最后补充以下代码:
System.out.println("\"a\"现在的值为:");
System.out.println("a");
field.set(a, new byte[] {65, 66, 67});
System.out.println("\"a\"现在的值为:");
System.out.println("a");
结果为:

可见 private final byte[] value 是可以修改的,不仅可以指向常量池,也可以指向堆。
本文介绍如何使用Java反射技术获取并修改String类的private final byte[] value字段,演示了非法访问警告的解决方法及字符串值的修改过程。

9639

被折叠的 条评论
为什么被折叠?



