前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【小家java】final修饰的变量真的不可变吗?

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

作者头像
YourBatman
发布2019-09-03 11:56:11
7560
发布2019-09-03 11:56:11
举报
文章被收录于专栏:BAT的乌托邦BAT的乌托邦
每篇一句

穷不练酒,富不占赌

1、概述

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

2、栗子

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

代码语言:javascript
复制
 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);
 }

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

代码语言:javascript
复制
 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一下,看到是如下情况:

这里写图片描述
这里写图片描述

这里面我解释两个东西: 1、为什么能够撼动final的值? field.getModifiers()&~Modifier.FINAL 这句话就是去掉final。其实java的访问权限信息啥的都是以2的N次幂来作为表示的,具体都是在java.lang.reflect.Modifier这个类里。so,咱们都把它的修饰符干掉,当然可以对Field set值了 所以,java的反射机制直接打破了封装有木有,哈哈哈

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

代码语言:javascript
复制
System.out.println(str2); //efg
System.out.println(field.get(this)); //gfe  通过反射拿到的值是对的

我们通过反射拿到的值是正确的,而直接输出变量的值却是不对的。究其原因:这其实是Java编译器对 final 属型的内联优化(java的内联机制和jvm底层有关,对程序调优有非常重要的作用。后续JVM相关博文,我会重点讨论),即编译时把该 final 的值直接放到了引用它的地方。即使是反射修改了该属性,但这种事后处理于事无补。 所以,咱们确实是可以通过反射来修改final的值,但是我们在后续代码中却不能用,尴尬。为了解决这个问题,设计的面实在是有点多,所以此处不适合展开来说。等后面讲述了JVM相关的东西后,会回到这里补充,请持续关注。。。

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

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

3、使用场景

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

4、最后

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年05月24日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 每篇一句
  • 1、概述
  • 2、栗子
  • 3、使用场景
  • 4、最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档