final 修饰的变量一定不能被修改?一定能被修改?

2024-08-22 15:36

本文主要是介绍final 修饰的变量一定不能被修改?一定能被修改?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1.final 修饰成员属性

2.final 修饰局部变量


1.final 修饰成员属性

1.1 final 修饰 String 类型的成员属性

public class FinalReflection {private static final String FINAL_STRING = "张三";public static void main(String[] args) throws Exception {System.out.println("反射前: " + FINAL_STRING);Class<FinalReflection> clazz = FinalReflection.class;Field field = clazz.getDeclaredField("FINAL_STRING");field.setAccessible(true);// 取消 final 修饰符的效果Field modifiersField = Field.class.getDeclaredField("modifiers");modifiersField.setAccessible(true);modifiersField.setInt(field, field.getModifiers() & ~java.lang.reflect.Modifier.FINAL);// 修改 final 的值field.set(null, "李四");// 使用反射再次获取字段的值System.out.println("通过反射获取修改后的值: " + field.get(null));System.out.println("直接访问 FINAL_STRING: " + FINAL_STRING);}
}

程序执行结果:

在 Java 中,final 关键字用于声明常量,它使得变量一旦赋值后就不能被修改。不过,通过反射机制,可以在特殊情况下修改 final 变量的值。

此处通过反射来获取值,确实看到修改了,只不过 FINAL_STRING 是一个 static final 变量,且它的值在编译时已经确定。对于这种情况,Java 编译器会进行优化,将使用 FINAL_STRING 的地方直接替换为它的值 "张三"(直接引用替换符号引用)。这意味着在编译后的字节码中,直接存储了 "张三" 这个值,而不是通过访问变量 FINAL_STRING 来获取它,所以直接访问 FINAL_STRING是看不到修改的。

1.2 final 修饰 Integer 类型的成员属性

public class Example {private static final Integer NUM = 128;public static void main(String[] args) throws Exception {// 这里的值已经被编译器内联为 5System.out.println(NUM);// 通过反射修改 x// 报错:java.lang.IllegalAccessException: Can not set final int fieldField field = Example.class.getDeclaredField("NUM");field.setAccessible(true);field.setInt(null, 10);System.out.println(NUM);}
}

程序执行结果: 

为啥前面修改 final String 还好好的,只不过编译器做了优化,导致它的指向并没有被改变,而到 final Integer 就直接报错了?

1.3 比较 “Integer” 和 “String”

Integer 的反射行为

  • 缓存机制Integer-128127 范围内使用缓存机制。final 修饰符的值不会被轻易修改,因为修改会影响到缓存和内部的优化机制。对于超出这个范围的值,创建新对象时可能会抛出异常或编译错误

  • 编译优化:对于 final 修饰的 Integer,编译器在编译时可能会对值进行优化,使得直接修改变得不现实。

String 的反射行为

  • 常量池String 在编译时会利用常量池进行优化。当 String 变量被声明为 final 时,编译器可能会将其内联到常量池中。在这种情况下,即使通过反射尝试修改 final 变量的引用,实际的编译器优化可能会防止这种修改生效。

  • 反射的影响:由于 String 对象的不可变性和常量池的优化,尝试通过反射修改 final 变量的引用不会直接导致编译错误,但修改后的结果可能不会被反映出。

2.final 修饰局部变量

修改 final 修饰 Integer 的局部变量:

public class Example {public static void main(String[] args) throws Exception {final Integer num = 128;// 通过反射修改 xField field = Example.class.getDeclaredField("num");field.setAccessible(true);field.setInt(null, 10);System.out.println(num);}
}

修改 final 修饰 String 的局部变量:

public class FinalReflection {public static void main(String[] args) throws Exception {final String FINAL_STRING = "张三";System.out.println("反射前: " + FINAL_STRING);Class<FinalReflection> clazz = FinalReflection.class;Field field = clazz.getDeclaredField("FINAL_STRING");field.setAccessible(true);// 取消 final 修饰符的效果Field modifiersField = Field.class.getDeclaredField("modifiers");modifiersField.setAccessible(true);modifiersField.setInt(field, field.getModifiers() & ~java.lang.reflect.Modifier.FINAL);// 修改 final 的值field.set(null, "李四");// 使用反射再次获取字段的值System.out.println("通过反射获取修改后的值: " + field.get(null));System.out.println("直接访问 FINAL_STRING: " + FINAL_STRING);}
}

以上两种情况,代码都会报错,所以 final 修饰的局部变量是不允许被修改的!

编译时内联

  • 局部变量:编译器在优化时可能将 final 局部变量内联到代码中,这意味着代码在运行时直接使用变量的值,而不是通过引用获取值。
  • 成员变量:即使是 final 成员变量,也可能在编译时进行优化,但这主要是指编译器将其常量值内联到字节码中。在运行时,成员变量仍然是对象的一部分,可以通过反射进行操作。

实际效果

即使你通过反射成功修改了 final 成员变量的值,这种修改在实际应用中可能不会如预期那样生效。原因包括:

  • JVM 缓存和优化:JVM 可能对 final 字段应用了各种优化措施,例如缓存字段值,这使得反射修改可能不会立即反映在应用程序中。
  • 编译器优化:编译器可能会对 final 字段做出优化,使得即使你通过反射修改了字段值,程序仍然会看到原始的常量值。

这篇关于final 修饰的变量一定不能被修改?一定能被修改?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1096683

相关文章

修改若依框架Token的过期时间问题

《修改若依框架Token的过期时间问题》本文介绍了如何修改若依框架中Token的过期时间,通过修改`application.yml`文件中的配置来实现,默认单位为分钟,希望此经验对大家有所帮助,也欢迎... 目录修改若依框架Token的过期时间修改Token的过期时间关闭Token的过期时js间总结修改若依

MySQL修改密码的四种实现方式

《MySQL修改密码的四种实现方式》文章主要介绍了如何使用命令行工具修改MySQL密码,包括使用`setpassword`命令和`mysqladmin`命令,此外,还详细描述了忘记密码时的处理方法,包... 目录mysql修改密码四种方式一、set password命令二、使用mysqladmin三、修改u

使用Python在Excel中插入、修改、提取和删除超链接

《使用Python在Excel中插入、修改、提取和删除超链接》超链接是Excel中的常用功能,通过点击超链接可以快速跳转到外部网站、本地文件或工作表中的特定单元格,有效提升数据访问的效率和用户体验,这... 目录引言使用工具python在Excel中插入超链接Python修改Excel中的超链接Python

浅析Rust多线程中如何安全的使用变量

《浅析Rust多线程中如何安全的使用变量》这篇文章主要为大家详细介绍了Rust如何在线程的闭包中安全的使用变量,包括共享变量和修改变量,文中的示例代码讲解详细,有需要的小伙伴可以参考下... 目录1. 向线程传递变量2. 多线程共享变量引用3. 多线程中修改变量4. 总结在Rust语言中,一个既引人入胜又可

python修改字符串值的三种方法

《python修改字符串值的三种方法》本文主要介绍了python修改字符串值的三种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学... 目录第一种方法:第二种方法:第三种方法:在python中,字符串对象是不可变类型,所以我们没办法直接

Mysql8.0修改配置文件my.ini的坑及解决

《Mysql8.0修改配置文件my.ini的坑及解决》使用记事本直接编辑my.ini文件保存后,可能会导致MySQL无法启动,因为MySQL会以ANSI编码读取该文件,解决方法是使用Notepad++... 目录Myhttp://www.chinasem.cnsql8.0修改配置文件my.ini的坑出现的问题

java如何调用kettle设置变量和参数

《java如何调用kettle设置变量和参数》文章简要介绍了如何在Java中调用Kettle,并重点讨论了变量和参数的区别,以及在Java代码中如何正确设置和使用这些变量,避免覆盖Kettle中已设置... 目录Java调用kettle设置变量和参数java代码中变量会覆盖kettle里面设置的变量总结ja

Perl 特殊变量详解

《Perl特殊变量详解》Perl语言中包含了许多特殊变量,这些变量在Perl程序的执行过程中扮演着重要的角色,:本文主要介绍Perl特殊变量,需要的朋友可以参考下... perl 特殊变量Perl 语言中包含了许多特殊变量,这些变量在 Perl 程序的执行过程中扮演着重要的角色。特殊变量通常用于存储程序的

变量与命名

引言         在前两个课时中,我们已经了解了 Python 程序的基本结构,学习了如何正确地使用缩进来组织代码,并且知道了注释的重要性。现在我们将进一步深入到 Python 编程的核心——变量与命名。变量是我们存储数据的主要方式,而合理的命名则有助于提高代码的可读性和可维护性。 变量的概念与使用         在 Python 中,变量是一种用来存储数据值的标识符。创建变量很简单,

两个月冲刺软考——访问位与修改位的题型(淘汰哪一页);内聚的类型;关于码制的知识点;地址映射的相关内容

1.访问位与修改位的题型(淘汰哪一页) 访问位:为1时表示在内存期间被访问过,为0时表示未被访问;修改位:为1时表示该页面自从被装入内存后被修改过,为0时表示未修改过。 置换页面时,最先置换访问位和修改位为00的,其次是01(没被访问但被修改过)的,之后是10(被访问了但没被修改过),最后是11。 2.内聚的类型 功能内聚:完成一个单一功能,各个部分协同工作,缺一不可。 顺序内聚: