Java 反射:修改 final 字段值


#Java 反射、泛型与注解#


结论

  • 对于 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)

( 本文完 )