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);
}
结果
说是不能设置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);
}
这样看来,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);
}
运行成功了,但是值没有被改变
尝试反射去获取一下值
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对基本类型的final域进行了什么优化处理,导致了不能被修改。
我们来看看其对java做了什么处理:
int num = 321;
public static void main(String[] args) {
T t = new T();
int c = t.num;
}
对应的字节码指令
final int num = 321;
public static void main(String[] args) {
T t = new T();
int c = t.num;
}
对应的字节码
可以看出区别还是非常明显的,没有加final的,获取对应的属性值的时候是通过getfield去获取对应的值,但是加上final的,没有通过该getfield获取属性值,而是直接在操作数中带上了321,猜想这可能就是编译器进行的优化,他发现该字段是基本类型且被final修饰,反正是只读的,就直接将321写入到了字节码指令中,而不用每次去读取。所以,都写入字节码指令了,你修改他对象上的值,有什么用?这个对象中的num域还是存在的,所以我们反射还是能进行修改,反射修改之后也能获取到被修改之后的值,但是问题在于,我们直接t.num这样生成的字节码指令,根本就不会去实例域中取值!