为局部变量声明final有意义吗?

3,431 阅读2分钟

概述

final在java中作为不可变语义的修饰词,接下来我们通过一段示例代码从字节码角度分析以下两点:

  1. final在类中不同的地方声明是怎么样标识的?
  2. 为局部变量声明final有意义吗?

示例代码

public final class TestFinal {

    private final int a = 10;

    public final void testFinalMethod() {
        System.out.println("testFinalMethod...");
    }

    public final void testFianlLocalVar() {
        final Object obj = new Object();
    }

}

这段代码本身没有什么需要解读的, 我们来关注一下final声明在不同的位置,字节码层面是怎么标识的。

1. 声明final在类定义中

通过final修饰类, 根据虚拟机规范ClassFile结构如下:

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

可以看到ClassFile结构中会通过access_flags两个字节的长度来记录访问修饰符。 access_flags取值如下: image

借助字节码查看工具可以看到access_flags的值就是public以及final。 image

2. 声明final在字段定义中

通过final修饰字段,同样查看虚拟机规范Fields的结构标识如下:

field_info {
    u2             access_flags;
    u2             name_index;
    u2             descriptor_index;
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

可以看到也会通过两个字节长度的access_flags来记录访问修饰符。再通过字节码工具查看生成的字节码的值就是private final: image

声明final在方法Methods中与之类似,此处不再复述。感兴趣可以自己查看一下。

3. 声明final在局部变量中

在方法中将一个局部变量声明成final时,虚拟机规范描述方法中的字节码是存在code属性集中,并且通过局部变量表来记录局部变量信息。

我们查看一下虚拟机的局部变量表结构信息:

LocalVariableTable_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 local_variable_table_length;
    {   u2 start_pc;
        u2 length;
        u2 name_index;
        u2 descriptor_index;
        u2 index;
    } local_variable_table[local_variable_table_length];
}

通过局部变量表结构可以看到,虚拟机层面根本没有access_flags这个属性。

通过字节码工具查看实际的局部变量表中也是如此: image

由此可以证明: 在虚拟机层面,将局部变量声明为final是没有意义的。

为局部变量声明final有意义吗?

回答这个问题个人的理解应该从下面两个维度来阐述:

  1. 从虚拟机层面来说,局部变量声明final是没有意义的,生成的字节码根本不会记录。
  2. 从java语言层面来说是有意义的,如我们通过内部类访问外部对象需要通过final修饰,另外在做代码重构时,将对象临时修饰为final来判断对象的作用域以及是否变化等情况也是有意义的。

总结

在日常开发中,经常看见项目中的代码很多时候都不假思索的将局部变量修饰为final,可能是对final的一种误解。理解其本质才能更好的使用。