反射无法修改 final String
文章目录
- 1. Demo
- 2. String 为什么不能被修改?
- 3. 参考资料
1. Demo
### Demo
public class Demo {
public static void main(String[] args) {
User user = new User();
Class<User> clz = User.class;
Field field1 = null;
try {
// 修改 String 对象失败
field1 = clz.getDeclaredField("name");
field1.setAccessible(true);
field1.set(user, "xixi");
// 修改 Student 对象成功
field1 = clz.getDeclaredField("student");
field1.setAccessible(true);
field1.set(user, new Student());
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
### User
class User {
private final String name = "Bob";
private final Student student = new Student();
public String getName() {
return name;
}
public Student getStudent() {
return student;
}
}
- 经过测试,Student 变量即使被 final 修饰也是可以通过反射进行修改的,但是 String 变量没有修改成功
2. String 为什么不能被修改?
- 对应的 User.class 文件
class User {
private final String name = "Bob";
private final Student student = new Student();
User() {
}
public String getName() {
return "Bob";
}
public Student getStudent() {
return this.student;
}
}
- 这就涉及到 JVM 的内联优化了
内联函数,编译器将指定的函数体插入并取代每一处调用该函数的地方(上下文),从而节省了每次调用函数带来的额外时间开支
- 简单的说,就是JVM在处理代码的时候会帮我们优化代码逻辑,比如上述的 final 变量,已知 final 修饰后不会被修改,所以获取这个变量的时候就直接帮你在编译阶段就给赋值了
- 所以上述的 getName 方法经过 JVM 编译内联优化后会变成:
public String getName() {
return "Bob";
}
- 所以无论怎么修改,都获取不到修改后的值
- 总结:反射是可以修改final变量的,但是如果是基本数据类型或者String类型的时候,无法通过对象获取修改后的值,因为JVM对其进行了内联优化
- 那有没有办法获取修改后的值呢?有,可以通过反射中的
Field.get(Object obj)
方法获取:
// 获取 field 对应的变量在 user 对象中的值
System.out.println("修改后" + field.get(user));
3. 参考资料
- https://blog.csdn.net/afdafvdaa/article/details/115190755
- https://www.51cto.com/article/641671.html