【小家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关键字的了解


相关文章
|
6天前
|
Java 程序员 容器
Java中的变量和常量:数据的‘小盒子’和‘铁盒子’有啥不一样?
在Java中,变量是一个可以随时改变的数据容器,类似于一个可以反复打开的小盒子。定义变量时需指定数据类型和名称。例如:`int age = 25;` 表示定义一个整数类型的变量 `age`,初始值为25。 常量则是不可改变的数据容器,类似于一个锁死的铁盒子,定义时使用 `final` 关键字。例如:`final int MAX_SPEED = 120;` 表示定义一个名为 `MAX_SPEED` 的常量,值为120,且不能修改。 变量和常量的主要区别在于变量的数据可以随时修改,而常量的数据一旦确定就不能改变。常量主要用于防止意外修改、提高代码可读性和便于维护。
|
1月前
|
Java 编译器
java“变量 x 可能未被初始化”解决
在Java中,如果编译器检测到变量可能在使用前未被初始化,会报“变量 x 可能未被初始化”的错误。解决方法包括:1. 在声明变量时直接初始化;2. 确保所有可能的执行路径都能对变量进行初始化。
|
14天前
|
Java 编译器
Java重复定义变量详解
这段对话讨论了Java中变量作用域和重复定义的问题。学生提问为何不能重复定义变量导致编译错误,老师通过多个示例解释了编译器如何区分不同作用域内的变量,包括局部变量、成员变量和静态变量,并说明了使用`this`关键字和类名来区分变量的方法。最终,学生理解了编译器在逻辑层面检查变量定义的问题。
Java重复定义变量详解
|
22天前
|
Java
通过Java代码解释成员变量(实例变量)和局部变量的区别
本文通过一个Java示例,详细解释了成员变量(实例变量)和局部变量的区别。成员变量属于类的一部分,每个对象有独立的副本;局部变量则在方法或代码块内部声明,作用范围仅限于此。示例代码展示了如何在类中声明和使用这两种变量。
|
1月前
|
Java 程序员
Java 面试高频考点:static 和 final 深度剖析
本文介绍了 Java 中的 `static` 和 `final` 关键字。`static` 修饰的属性和方法属于类而非对象,所有实例共享;`final` 用于变量、方法和类,确保其不可修改或继承。两者结合可用于定义常量。文章通过具体示例详细解析了它们的用法和应用场景。
28 3
|
28天前
|
存储 安全 Java
了解final关键字在Java并发编程领域的作用吗?
在Java并发编程中,`final`关键字不仅用于修饰变量、方法和类,还在多线程环境中确保对象状态的可见性和不变性。本文深入探讨了`final`关键字的作用,特别是其在final域重排序规则中的应用,以及如何防止对象的“部分创建”问题,确保线程安全。通过具体示例,文章详细解析了final域的写入和读取操作的重排序规则,以及这些规则在不同处理器上的实现差异。
了解final关键字在Java并发编程领域的作用吗?
|
1月前
|
Java 编译器
在Java中,关于final、static关键字与方法的重写和继承【易错点】
在Java中,关于final、static关键字与方法的重写和继承【易错点】
21 5
|
1月前
|
分布式计算 资源调度 Hadoop
大数据-01-基础环境搭建 超详细 Hadoop Java 环境变量 3节点云服务器 2C4G XML 集群配置 HDFS Yarn MapRedece
大数据-01-基础环境搭建 超详细 Hadoop Java 环境变量 3节点云服务器 2C4G XML 集群配置 HDFS Yarn MapRedece
76 4
|
1月前
|
Java
java中父类方法return this.对象还是变量,子类去调用this.这个方法的问题
本文探讨了在Java中,当父类的方法返回`this`对象或变量时,子类调用该方法的行为,以及`this`关键字在不同类中调用方法时的指向问题。
15 0
java中父类方法return this.对象还是变量,子类去调用this.这个方法的问题
|
1月前
|
缓存 安全 Java
Java中 final、finally、finalize 有什么区别?
本文详细阐述了Java中`final`、`finally`和`finalize`的区别:`final`用于修饰类、方法和变量以表示不可变性;`finally`是用于确保在`try-catch`结构中无论是否发生异常都能执行的代码块;而`finalize`是`Object`类的方法,用于在对象被垃圾回收前执行清理工作,但在JDK 9中已被标记为弃用。
29 0
Java中 final、finally、finalize 有什么区别?