结论
- 对于 String 和基础类型(int、long 等)类型的 final 字段通过反射修改后,只有通过反射才能获取修改后的值。通过直接访问对象字段、对象方法等其他方式获取的结果仍然是旧值。
- 对于 String 和基础类型(int、long 等)之外的类型 的 final 字段通过反射修改后,通过反射、直接访问对象字段、对象方法等方式获取的值,都是修改后的值。
- 对于 static 或者 final 单独标识的字段,都可以通过反射修改,但是若用 static、final 共同标识字段,则无法修改。在Java 8 等版本中,可提通过反射去掉 字段的 final 属性来解决;但是在较新的版本,比如 Java 17 中,无法去除 final 属性。
示例1:修改 final 对象类型字段
package org.example;
import java.lang.reflect.Field;
public class TestChangeFinal {
public static class UserInfo {
private final Integer age = 18;
public Integer getAge() {
return age;
}
}
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
UserInfo userInfo = new UserInfo();
// 修改前
System.out.println("修改前: " + userInfo.getAge());
// 修改
Field ageField = UserInfo.class.getDeclaredField("age");
ageField.setAccessible(true);
ageField.set(userInfo, 20);
// 修改后
System.out.println("修改后: " + userInfo.getAge());
}
}
执行结果:
修改前: 18
修改后: 20
示例2:修改 final private 基础类型字段
package org.example;
import java.lang.reflect.Field;
public class TestChangeFinal {
public static class UserInfo {
private final int age = 18;
public int getAge() {
return age;
}
}
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
UserInfo userInfo = new UserInfo();
// 修改前
System.out.println("修改前: " + userInfo.getAge());
// 修改
Field ageField = UserInfo.class.getDeclaredField("age");
ageField.setAccessible(true);
ageField.set(userInfo, 20); // 设置为 20
// 修改后通过反射获取值
Object obj = ageField.get(userInfo);
System.out.println("通过反射获取修改后的值:" + obj); // 20
System.out.println("通过对象方法获取修改后的值: " + userInfo.getAge()); // 18
}
}
执行结果:
修改前: 18
通过反射获取修改后的值:20
通过对象方法获取修改后的值: 18
为什么通过反射获取的值是 20,通过对象获取的是 18 ?
这是因为 age 字段的确被改了,但是 userInfo.getAge() 被编译器优化了。我们将代码对应的 class 文件反编译结果如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.example;
import java.lang.reflect.Field;
public class TestChangeFinal {
public TestChangeFinal() {
}
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
TestChangeFinal.UserInfo userInfo = new TestChangeFinal.UserInfo();
System.out.println("修改前: " + userInfo.getAge());
Field ageField = TestChangeFinal.UserInfo.class.getDeclaredField("age");
ageField.setAccessible(true);
ageField.set(userInfo, 20);
Object obj = ageField.get(userInfo);
System.out.println("通过反射获取修改后的值:" + obj);
System.out.println("通过对象方法获取修改后的值: " + userInfo.getAge());
}
public static class UserInfo {
private final int age = 18;
public UserInfo() {
}
public int getAge() { // 注意,这里被优化了
return 18;
}
}
}
示例3:修改 final public 基础类型字段
package org.example;
import java.lang.reflect.Field;
public class TestChangeFinal {
public static class UserInfo {
public final int age = 18;
public int getAge() {
return age;
}
}
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
UserInfo userInfo = new UserInfo();
// 修改前
System.out.println("修改前: " + userInfo.getAge());
// 修改
Field ageField = UserInfo.class.getDeclaredField("age");
ageField.setAccessible(true);
ageField.setInt(userInfo, 20); // 设置为 20
// 修改后通过反射获取值
Object obj = ageField.get(userInfo);
System.out.println("通过反射获取修改后的值:" + obj); // 20
System.out.println("通过对象字段名获取修改后的值:" + userInfo.age); // 18
System.out.println("通过对象方法获取修改后的值: " + userInfo.getAge()); // 18
}
}
执行结果:
修改前: 18
通过反射获取修改后的值:20
通过对象字段名获取修改后的值:18
通过对象方法获取修改后的值: 18
同样的,userInfo.age 在编译时也被直接替换成常量了。
将代码对应的 class 文件反编译结果如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.example;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.util.Objects;
public class TestChangeFinal {
public TestChangeFinal() {
}
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
TestChangeFinal.UserInfo userInfo = new TestChangeFinal.UserInfo();
System.out.println("修改前: " + userInfo.getAge());
Field ageField = TestChangeFinal.UserInfo.class.getDeclaredField("age");
ageField.setAccessible(true);
ageField.setInt(userInfo, 20);
Object obj = ageField.get(userInfo);
System.out.println("通过反射获取修改后的值:" + obj);
PrintStream var10000 = System.out;
Objects.requireNonNull(userInfo);
var10000.println("通过对象字段名获取修改后的值:" + 18); // 被优化
System.out.println("通过对象方法获取修改后的值: " + userInfo.getAge());
}
public static class UserInfo {
public final int age = 18;
public UserInfo() {
}
public int getAge() {
return 18; // 被优化
}
}
}
示例4:修改 final public String 类型字段
package org.example;
import java.lang.reflect.Field;
public class TestChangeFinal {
public static class UserInfo {
public final String name = "123";
public String getName() {
return name;
}
}
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
UserInfo userInfo = new UserInfo();
// 修改前
System.out.println("修改前: " + userInfo.getName());
// 修改
Field nameField = UserInfo.class.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(userInfo, "456"); // 设置为 "456"
// 修改后通过反射获取值
Object obj = nameField.get(userInfo);
System.out.println("通过反射获取修改后的值:" + obj); // "456"
System.out.println("通过对象字段名获取修改后的值:" + userInfo.name); // "123"
System.out.println("通过对象方法获取修改后的值: " + userInfo.getName()); // "123"
}
}
执行结果:
修改前: 123
通过反射获取修改后的值:456
通过对象字段名获取修改后的值:123
通过对象方法获取修改后的值: 123
示例5:修改 final staic 类型字段
下面的实现会报错:
package org.example;
import java.lang.reflect.Field;
public class TestChangeFinal {
public static class UserInfo {
public static final String name = "123";
public String getName() {
return name;
}
}
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
UserInfo userInfo = new UserInfo();
// 修改前
System.out.println("修改前: " + userInfo.getName());
// 修改
Field nameField = UserInfo.class.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(userInfo, "456"); // 报错
}
}
执行结果:
修改前: 123
Exception in thread "main" java.lang.IllegalAccessException: Can not set static final java.lang.String field org.example.TestChangeFinal$UserInfo.name to java.lang.String
at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:76)
at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:80)
at java.base/jdk.internal.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl.set(UnsafeQualifiedStaticObjectFieldAccessorImpl.java:77)
at java.base/java.lang.reflect.Field.set(Field.java:799)
at org.example.TestChangeFinal.main(TestChangeFinal.java:23)
需要去除 final 属性。
package com.example;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class TestChangeFinal {
public static class UserInfo {
public static final String name = "123";
public String getName() {
return name;
}
}
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
UserInfo userInfo = new UserInfo();
// 修改前
System.out.println("修改前: " + userInfo.getName());
// 修改
Field nameField = UserInfo.class.getDeclaredField("name");
nameField.setAccessible(true);
// 去除字段的 final 属性
Field modifiers = Field.class.getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.setInt(nameField, nameField.getModifiers() & ~Modifier.FINAL);
// 修改 name 值
nameField.set(userInfo, "456"); // 设置为 "456"
// 修改后通过反射获取值
Object obj = nameField.get(userInfo);
System.out.println("通过反射获取修改后的值:" + obj); // "456"
System.out.println("通过对象字段名获取修改后的值:" + userInfo.name); // "123"
System.out.println("通过对象方法获取修改后的值: " + userInfo.getName()); // "123"
}
}
在 Java 8 中执行结果:
修改前: 123
通过反射获取修改后的值:456
通过对象字段名获取修改后的值:123
通过对象方法获取修改后的值: 123
在 Java 17 中执行结果:
修改前: 123
Exception in thread "main" java.lang.NoSuchFieldException: modifiers
at java.base/java.lang.Class.getDeclaredField(Class.java:2610)
at org.example.TestChangeFinal.main(TestChangeFinal.java:26)