【小家java】final修饰的变量真的不可变吗?

简介: 【小家java】final修饰的变量真的不可变吗?

相关阅读


【小家java】java5新特性(简述十大新特性) 重要一跃

【小家java】java6新特性(简述十大新特性) 鸡肋升级

【小家java】java7新特性(简述八大新特性) 不温不火

【小家java】java8新特性(简述十大新特性) 饱受赞誉

【小家java】java9新特性(简述十大新特性) 褒贬不一

【小家java】java10新特性(简述十大新特性) 小步迭代

【小家java】java11新特性(简述八大新特性) 首个重磅LTS版本


每篇一句


穷不练酒,富不占赌

1、概述


这可能是大家的一个共识:如果我们希望这个变量不可变,我们可以用final进行修饰。但本篇将带你深入了解不变的含义,我相信可以让你更深的了解final的原理,也能记得更牢靠

2、栗子


被final修饰过的变量,只是说栈存储的地址不能再改变,但是却没有说地址指向的内容不能改变。所以用final修饰,但内容是个对象啥的,然后改变对象属性值,这个不在本文讨论的范围以内。本文想讨论的是,直接就概念final的栈的地址,让它去指向另外一块内存地址。比如下面直接暴力反射处理,显然是不好使的:


private static final String str = "abc";
 private final String str2 = "efg";
 @Test
 public void fun1() throws Exception {
     System.out.println(str); //abc
     System.out.println(str2); //efg
     ///
     Field field = Tests.class.getDeclaredField("str");
     field.setAccessible(true);
     field.set(this, "cba"); //java.lang.IllegalAccessException: Can not set static final
     System.out.println(str);
 }


接下来,就好好看看我是怎么改变这个值的:

private static final String str = "abc";
 private final String str2 = "efg";
 @Test
 public void fun1() throws Exception {
     System.out.println(str); //abc
     System.out.println(str2); //efg
     ///
     Field field = Tests.class.getDeclaredField("str2");
     field.setAccessible(true);
     Field modifiersField = Field.class.getDeclaredField("modifiers");
     modifiersField.setAccessible(true);
     modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
     field.set(this, "gfe");
     System.out.println(str2); //efg
 }

诧异吧,我们会发现最后输出的还是efg,啪啪打脸啊,但是我debug一下,看到是如下情况:


image.png


这里面我解释两个东西:


1、为什么能够撼动final的值?


field.getModifiers()&~Modifier.FINAL 这句话就是去掉final。其实java的访问权限信息啥的都是以2的N次幂来作为表示的,具体都是在java.lang.reflect.Modifier这个类里。so,咱们都把它的修饰符干掉,当然可以对Field set值了


所以,java的反射机制直接打破了封装有木有,哈哈哈


2、为什么最终打印的和我们调试的值不一样?


System.out.println(str2); //efg
System.out.println(field.get(this)); //gfe  通过反射拿到的值是对的


我们通过反射拿到的值是正确的,而直接输出变量的值却是不对的。究其原因:这其实是Java编译器对 final 属型的内联优化(java的内联机制和jvm底层有关,对程序调优有非常重要的作用。后续JVM相关博文,我会重点讨论),即编译时把该 final 的值直接放到了引用它的地方。即使是反射修改了该属性,但这种事后处理于事无补。


所以,咱们确实是可以通过反射来修改final的值,但是我们在后续代码中却不能用,尴尬。为了解决这个问题,设计的面实在是有点多,所以此处不适合展开来说。等后面讲述了JVM相关的东西后,会回到这里补充,请持续关注。。。


但是,请大家可以记住一个结论:


只要不会被编译器内联优化的 final 属性就可以通过反射有效的进行修改 – 修改后代码中可使用到新的值


3、使用场景


几乎没啥使用场景,除非一些极限情况:比如强制修改第三方源码。。。

4、最后


整理出来的内容,希望能加深大家对final关键字的了解


相关文章
|
15天前
|
Java API
【JAVA】final、finally、finalize 有什么区别?
【JAVA】final、finally、finalize 有什么区别?
|
2天前
|
小程序 Java 容器
03|Java基础语法:讲解标识符、关键字、变量、数据类型、运算符、控制语句(条件分支、循环)
03|Java基础语法:讲解标识符、关键字、变量、数据类型、运算符、控制语句(条件分支、循环)
7 0
|
2天前
|
Java
深入浅出Java基础语法:标识符、关键字、变量、数据类型、运算符与控制语句
深入浅出Java基础语法:标识符、关键字、变量、数据类型、运算符与控制语句
5 0
|
2天前
|
存储 Java 定位技术
轻松理解Java中的数据类型和变量
轻松理解Java中的数据类型和变量
5 0
|
6天前
|
存储 Java
一文搞懂Java中所有变量概念!
一文搞懂Java中所有变量概念!
17 2
一文搞懂Java中所有变量概念!
|
12天前
|
Java
final 在 java 中有什么作用?
final 在 java 中有什么作用?
|
12天前
|
Java
【Java探索之旅】数据类型与变量 字面常量 整型变量
【Java探索之旅】数据类型与变量 字面常量 整型变量
23 0
|
12天前
|
存储 Java C语言
【Java探索之旅】数据类型与变量 浮点型,字符型,布尔型,字符串型
【Java探索之旅】数据类型与变量 浮点型,字符型,布尔型,字符串型
20 0
|
13天前
|
Java 开发者
Java变量命名规则
Java变量命名规则
19 0
|
18天前
|
Java
面试官:小伙子来说一说Java中final关键字,以及它和finally、finalize()有什么区别?
面试官:“小伙子,用过final关键字吗?” 我:“必须用过呀” 面试官:“好,那来说一说你对这个关键字的理解吧,再说一说它与finally、finalize()的区别” 我:“好嘞!
19 1