Final修饰的字段是否可以通过反射设置值

案发现场

经常听说final修饰的字段是常量不能改变的他的值,但是以外发现 Integer.java源码中的字段“value”是final,但是可以通过反射改变他的值。

public final class Integer extends Number implements Comparable<Integer> {
  /**
     * The value of the {@code Integer}.
     *
     * @serial
     */
   private final int value;
  public Integer(String s) throws NumberFormatException {
        this.value = parseInt(s, 10);
    }
  public Integer(int value) {
        this.value = value;
    }
}

 

验证final修饰的字段是否可以修改

对final修饰的成员非静态变量做强制修改

package test;

import java.lang.reflect.Field;

/**
 * @author kancy
 * @version 1.0
 * @date 2019/3/7 12:28
 */
public class FinalDemo {
    /**常量:默认值null*/
    private final String v1 = null;
    /**常量:默认值v4*/
    private final String v2 = "v2";

    public static void main(String[] args) throws Exception {
        FinalDemo finalDemo = new FinalDemo();
        Field f1 = finalDemo.getClass().getDeclaredField("v1");
        Field f2 = finalDemo.getClass().getDeclaredField("v2");
        f1.setAccessible(true);
        f2.setAccessible(true);

        // 反射改变v1的值
        f1.set(finalDemo, "new_v1");
        System.out.println("v1 改变后的值(对象取值):" + finalDemo.getV1());
        System.out.println("v1 改变后的值(反射取值):" + f1.get(finalDemo));
        // 反射改变v2的值
        f2.set(finalDemo, "new_v2");
        System.out.println("v2 改变后的值(对象取值):" + finalDemo.getV2());
        System.out.println("v2 改变后的值(反射取值):" + f2.get(finalDemo));
        System.out.println("--------------------------------------------------------");
        // 反射改变v1的值
        f1.set(finalDemo, "new_new_v1");
        System.out.println("v1 再次改变后的值(对象取值):" + finalDemo.getV1());
        System.out.println("v1 再次改变后的值(反射取值):" + f1.get(finalDemo));
        // 反射改变v2的值
        f2.set(finalDemo, "new_new_v2");
        System.out.println("v2 再次改变后的值(对象取值):" + finalDemo.getV2());
        System.out.println("v2 再次改变后的值(反射取值):" + f2.get(finalDemo));
    }

    public String getV1() {
        return v1;
    }

    public String getV2() {
        return v2;
    }
}

结果打印:

v1 改变后的值(对象取值):new_v1
v1 改变后的值(反射取值):new_v1
v2 改变后的值(对象取值):v2
v2 改变后的值(反射取值):new_v2
--------------------------------------------------------
v1 再次改变后的值(对象取值):new_new_v1
v1 再次改变后的值(反射取值):new_new_v1
v2 再次改变后的值(对象取值):v2
v2 再次改变后的值(反射取值):new_new_v2

结论:对于非静态的final成员变量,在没有赋值的情况下是可以使用反射对其进行赋值的;对于已经初始化赋值的变量,反射不能真正该变变量的值,但是使用反射get是可以获取到改变后的值,用实例是无法获取到的。

 

对final修饰的成员非静态变量做强制修改

 

import java.lang.reflect.Field;

/**
 * @author kancy
 * @version 1.0
 * @date 2019/3/7 12:35
 */
public class StaticFinalDemo {
    /**
     * 静态常量 默认值null
     */
    private static final String v1 = null;
    /**
     * 静态常量 默认值v3
     */
    private static final String v2 = "v2";

    public static void main(String[] args) throws Exception {
        Field f1 = StaticFinalDemo.class.getDeclaredField("v1");
        Field f2 = StaticFinalDemo.class.getDeclaredField("v2");
        f1.setAccessible(true);
        f2.setAccessible(true);

        // 反射改变v1的值
        try {
            f1.set(StaticFinalDemo.class, "new_v1");
            System.out.println("v1 改变后的值(对象取值):" + StaticFinalDemo.getV2());
            System.out.println("v1 改变后的值(反射取值):" + f1.get(StaticFinalDemo.class));
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        // 反射改变v2的值
        try {
            f2.set(StaticFinalDemo.class, "new_v2");
            System.out.println("v2 改变后的值(对象取值):" + StaticFinalDemo.getV2());
            System.out.println("v2 改变后的值(反射取值):" + f2.get(StaticFinalDemo.class));
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }

    public static String getV1() {
        return v1;
    }

    public static String getV2() {
        return v2;
    }
}

 

结果:

java.lang.IllegalAccessException: Can not set static final java.lang.String field StaticFinalDemo.v1 to java.lang.String
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:76)
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:80)
at sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl.set(UnsafeQualifiedStaticObjectFieldAccessorImpl.java:77)
at java.lang.reflect.Field.set(Field.java:758)
at StaticFinalDemo.main(StaticFinalDemo.java:26)
java.lang.IllegalAccessException: Can not set static final java.lang.String field StaticFinalDemo.v2 to java.lang.String
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:76)
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:80)
at sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl.set(UnsafeQualifiedStaticObjectFieldAccessorImpl.java:77)
at java.lang.reflect.Field.set(Field.java:758)
at StaticFinalDemo.main(StaticFinalDemo.java:36)

 

结论:对静态的final修饰的字段(赋值或者不赋值)进行修改,会报错:java.lang.IllegalAccessException错误。

 

 反射的set的过程

https://www.edrawsoft.cn/viewer/public/s/13c34261013087

 

 

如果final修饰的类型为Object引用类型

package test;

import java.lang.reflect.Field;

/**
 * @author kancy
 * @version 1.0
 * @date 2019/3/7 12:28
 */
public class FinalDemo {
    /**常量:默认值null*/
    private final String v1 = null;
    /**常量:默认值v4*/
    private final String v2 = "v2";
    private final ChildA v3 = new ChildA("kancy");
    private final ChildA v4 = null;

    public FinalDemo() {
    }

    public static void main(String[] args) throws Exception {
        FinalDemo finalDemo = new FinalDemo();
        Field f3 = finalDemo.getClass().getDeclaredField("v3");
        f3.setAccessible(true);
        ChildA oldValue = finalDemo.getV3();
        System.out.println("v3 改变前的值:" + finalDemo.getV3()+", " );
        f3.set(finalDemo, new ChildA("pmm"));
        System.out.println("v3 改变后的值(对象取值):" + finalDemo.getV3()+", ");
        System.out.println("v3 改变后的值(反射取值):" + f3.get(finalDemo)+", ");
        // 地址已经发生变化
        System.out.println(oldValue == finalDemo.getV3());

    }

    public ChildA getV3() {
        return v3;
    }

    public static class ChildA {
        private String name;

        public ChildA(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }
}

 

结果:

v3 改变前的值:test.FinalDemo$ChildA@66d3c617, 
v3 改变后的值(对象取值):test.FinalDemo$ChildA@63947c6b, 
v3 改变后的值(反射取值):test.FinalDemo$ChildA@63947c6b, 
false

进程已结束,退出代码 0

 

结论:可以看出实例和反射取出的对象都是同一个,而使用字符类型就不是同一种类型,这是因为字符存在于常量池中,而对象存在与堆区。field.set是直接通过unsafe操作内存的,一但fianl修饰的字段被初始化了,引用的地址就不能发生变化,但是堆中的对象是可以被修改的。而字符串常量是不能被修改的,所以出现了,反射和实例去取出的数据不一致情况。

 

转载于:https://www.cnblogs.com/kancy/p/10488923.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java 中,可以使用反射来删除一个字段。下面是一个示例代码: ```java import java.lang.reflect.Field; public class Main { public static void main(String[] args) throws Exception { MyClass obj = new MyClass(); Field field = obj.getClass().getDeclaredField("myField"); field.setAccessible(true); field.set(obj, "Hello World!"); System.out.println(obj.getMyField()); Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); modifiersField.setInt(field, field.getModifiers() & ~java.lang.reflect.Modifier.FINAL); field.setAccessible(true); field.set(obj, null); System.out.println(obj.getMyField()); } } class MyClass { private String myField; public String getMyField() { return myField; } public void setMyField(String myField) { this.myField = myField; } } ``` 在这个示例中,我们首先通过 `getDeclaredField` 方法获取了 `MyClass` 中的 `myField` 字段。然后,我们设置了该字段的可访问性,并将其设置为字符串 "Hello World!"。接下来,我们通过反射获取 `modifiers` 字段,并设置其可访问性。然后,我们使用位运算符将 `FINAL` 修饰符从 `modifiers` 字段中移除,从而允许我们修改该字段。最后,我们再次设置字段的可访问性,并将其设置为 `null`,从而删除了该字段。 需要注意的是,这种做法可能会破坏代码的封装性,并且可能会导致意想不到的行为。因此,应该谨慎使用反射来删除字段

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值