被final修饰的部分变量后依然可以被修改

技术探究:

被final修饰的 变量可以被修改吗 ?

在java的学习中,从刚接触final这个关键词时,就被告知:
------“被final修饰的变量只能一次赋值,以后不能被修改,即常量“。

对于这种比较基础的知识,我对老师的说法还是深信不疑的,总觉得这么基础的东西,作为老师不应该教错吧。而且日常工作里也确实是这么用的,使用final修饰变量作为常量,然后直接在其他类中使用的例子也比比皆是。

但最近在偶然的机会下,跟朋友深入探究了一下关于“被final修饰后的变量是否可以被修改”,才发现里面确实是有猫腻的 ,感觉被各种坑了!!!

首先,先说一下结论。被final修饰的变量中,有一部分变量是可以被修改的!!!


技术分析:

不谈理论,咱们先直接上代码瞅一眼:

首先:我们准备一个空的实体类ObjectOV:

public class ObjectVO {
}

然后,我们再准备一个实体类,用来放用final修饰的各种变量。

public class FinalVO {
    private final int intValue = 1;
    private final Integer integerValue = new Integer(1);
    private final int intNoValue;

    private final String stringValue =  "1";
    private final String newStringValue = new String("1");
    private final String stringNoNValue;

    private final ObjectVO objectVO = new ObjectVO();

    public int getIntValue() {
        return intValue;
    }

    public Integer getIntegerValue() {
        return integerValue;
    }

    public int getIntNoValue() {
        return intNoValue;
    }

    public String getStringValue() {
        return stringValue;
    }

    public String getNewStringValue() {
        return newStringValue;
    }

    public String getStringNoNValue() {
        return stringNoNValue;
    }

    @Override
    public String toString() {
        return "FinalVO{" +
                "intValue=" + intValue +
                ", integerValue=" + integerValue +
                ", intNoValue=" + intNoValue +
                ", stringValue='" + stringValue + '\'' +
                ", newStringValue='" + newStringValue + '\'' +
                ", stringNoNValue='" + stringNoNValue + '\'' +
                ", objectVO=" + objectVO +
                '}';
    }

    public FinalVO(int intNoValue, String stringNoNValue) {
        this.intNoValue = intNoValue;
        this.stringNoNValue = stringNoNValue;
    }
}

上面这个实体类中,所有变量都被final修饰。变量类型包括-----已赋值的基础变量、已赋值的包装类变量、未赋值的基础变量、直接赋值的String类型、new String方式赋值的String类型、未赋值的String类型,还有一个对象。

最后,我们再创建一个测试类,把所有被final修饰的内容都修改一下,然后再来看看结果:

public class FinalTest {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        FinalVO finalVO = new FinalVO(1,"1");
        Class clz = FinalVO.class;
        Field field = null;

        System.out.println(finalVO.toString());

        modifyValue(field,clz,finalVO,"intValue",2);
        modifyValue(field,clz,finalVO,"integerValue",2);
        modifyValue(field,clz,finalVO,"intNoValue",2);
        modifyValue(field,clz,finalVO,"stringValue","2");
        modifyValue(field,clz,finalVO,"newStringValue","2");
        modifyValue(field,clz,finalVO,"stringNoNValue","2");
        modifyValue(field,clz,finalVO,"objectVO",new ObjectVO());

        System.out.println(finalVO.toString());
    }

    private static void modifyValue(Field field,Class clz, FinalVO finalVO, String fieldName, Object obj) throws NoSuchFieldException, IllegalAccessException {

        field = clz.getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(finalVO,obj);
    }
}

接下来就是见证结果的时刻了:

FinalVO{intValue=1, integerValue=1, intNoValue=1, stringValue='1', newStringValue='1', stringNoNValue='1', objectVO=com.company.finalTest.ObjectVO@4b67cf4d}
FinalVO{intValue=1, integerValue=2, intNoValue=2, stringValue='1', newStringValue='2', stringNoNValue='2', objectVO=com.company.finalTest.ObjectVO@12a3a380}

让我们来分析一下这个结果:
从结果可以看到,只有被初始赋值的基础类型和被初始直接赋值的String类型的值没有改变,其余被final修改的内容都已出现变化。


技术总结:

让我们来分析一下上面这个结果:
对于这个结果,我一开始也不是太明白,然后开始大量的搜原因,最后得出来个名词—“内联函数“。
内联函数,编译器将指定的函数体插入并取代每一处调用该函数的地方(上下文),从而节省了每次调用函数带来的额外时间开支。
我们可以理解成:JVM再编译代码的时候,会对代码进行一些优化。比如这段代码中的获取变量值,其实在编译阶段就已经被直接赋值好了。

用上面代码举例:

public int getIntValue() {
    return intValue;
}

这段代码经过JVM的内联优化之后会变成(可以通过反编译查看):

public int getIntValue() {
    return 1;
}

由此可见,我们可以修改对应变量的值,但无论我们怎么修改,在获取这个int值的时候都不会获取到修改后的值。

让我们来总结一下结果:

被final修饰的部分变量是可以被修改的,其中被初始赋值的基础类型和被初始直接赋值的String类型,这两种会被JVM进行内联优化 ,无法通过对象获取修改后的值。


期间,还发现了一个变量--StringBugger:
String是字符串变量,它的对象是可以扩充和修改的,即使是被final修饰,依然可以修改。

public class Test02 {
    public static void main(String[] args) {
        final StringBuffer sb = new StringBuffer("sb");
        System.out.println(sb);
        sb.append("a");
        System.out.println(sb);
    }
}

结果是:

sb
sba
  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拉结尔之书

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值