final在java中的语义就是常量的意思,这个我们大家都知道。

但是这个常量其实按照C语言的思想来说的话其实是相当于一个指针指向一个地址,然后指针的值不能被改变,也就是是说这个指针变量一直指向这块内存空间,但是这这块内存空间的值是可以被改变的。我们今天就来尝试一下去改变一下这个内存空间的值

 

final Integer num = 321;

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    T t = new T();
    System.out.println(t.num);
    Field field = T.class.getDeclaredField("num");
    field.set(t,32132);
    System.out.println(t.num);
}

结果

修改了JAVA_HOME java还是原来的版本_内存空间

说是不能设置final域的字段,加上 field.setAccessible(true)试试看

final Integer num = 321;

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    T t = new T();
    System.out.println(t.num);
    Field field = T.class.getDeclaredField("num");
    field.setAccessible(true);
    field.set(t,32132);
    System.out.println(t.num);
}

修改了JAVA_HOME java还是原来的版本_System_02

这样看来,final域也是可以被反射修改的嘛。

 

刚才尝试是引用类型,现在试试基本类型

final int num = 321;

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    T t = new T();
    System.out.println(t.num);
    Field field = T.class.getDeclaredField("num");
    field.setAccessible(true);
    field.set(t,32132);
    System.out.println(t.num);
}

运行成功了,但是值没有被改变 

 

修改了JAVA_HOME java还是原来的版本_内存空间_03

 

尝试反射去获取一下值

final int num = 321;

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    T t = new T();
    System.out.println(t.num);
    Field field = T.class.getDeclaredField("num");
    field.setAccessible(true);
    field.set(t,32132);
    System.out.println(t.num);
    System.out.println(field.get(t));
}

反射的值是被修改了的 

 

修改了JAVA_HOME java还是原来的版本_内存空间_04

为什么出现了这种情况,猜想应该是Java对基本类型的final域进行了什么优化处理,导致了不能被修改。

我们来看看其对java做了什么处理:

int num = 321;

public static void main(String[] args) {
    T t = new T();
    int c = t.num;
}

对应的字节码指令 

修改了JAVA_HOME java还是原来的版本_字节码_05

final int num = 321;

public static void main(String[] args) {
    T t = new T();
    int c = t.num;
}

对应的字节码 

 

修改了JAVA_HOME java还是原来的版本_字节码_06

可以看出区别还是非常明显的,没有加final的,获取对应的属性值的时候是通过getfield去获取对应的值,但是加上final的,没有通过该getfield获取属性值,而是直接在操作数中带上了321,猜想这可能就是编译器进行的优化,他发现该字段是基本类型且被final修饰,反正是只读的,就直接将321写入到了字节码指令中,而不用每次去读取。所以,都写入字节码指令了,你修改他对象上的值,有什么用?这个对象中的num域还是存在的,所以我们反射还是能进行修改,反射修改之后也能获取到被修改之后的值,但是问题在于,我们直接t.num这样生成的字节码指令,根本就不会去实例域中取值!