Java中局部final变量的行为

3
我知道这可能会被点踩,但这是一件让我感到有趣的事情。
    public class finaltesting
{

    public static final String  v=900; //requires initialization


    public static void main(String []args)
    {
        final int c; // doesn't need initialization
        switch(get())
        {
            case 0:
            System.out.println("zero");
            break;
            case 1:
            System.out.println("one");
            break;
            case 2:
            System.out.println("two");
            break;
            case 3:
            System.out.println("three");
        }

    }
    static int get()
    {
        return (int)(Math.random()*4);

    }
}

但是,最终变量需要初始化,那么为什么“final int c;”没有任何编译错误呢?

这是否意味着最终实例变量和最终局部变量完全不同?

(*对于那些认为这可能是“如何使用最终变量”的重复问题的人来说,这不是关于本地和实例最终变量的问题。)


4
如果你将它声明为非静态字段,编译器会知道它没有被使用;但如果你将它声明为静态字段,则编译器不能知道。 - zapl
@sinclair 是的,这是完全不同的问题...它涉及到本地 final 变量和实例 final 变量... - manifold
@zapl,你能再说些什么吗? - manifold
2
请参考此链接:https://dev59.com/tVwZ5IYBdhLWcg3wVO1U。希望对您有所帮助。 - s7vr
2个回答

4
这个规则如下:
每个局部变量(§14.4)和每个空白 final 字段(§4.12.4,§8.3.1.2)在其值的任何访问发生时都必须具有明确定义的值。
对其值的访问包括变量的简单名称(或者对于字段,由 this 限定的字段的简单名称)在表达式中出现,除了作为简单赋值运算符 =(§15.26.1)的左操作数之外。
对于每个局部变量或空白 final 字段 x 的每次访问,在访问之前必须明确定义 x 的值,否则会出现编译时错误。
所以重要的是当你访问一个变量时,最终字段(或任何局部变量)已经被分配。如果你从未访问过它,就没有什么需要强制执行的。 (值得注意的是,对于最终和非最终局部变量,规则是相同的。唯一的区别是最终局部变量不能再次赋值。)
使用局部变量时,编译器可以保证不会访问它,因此不需要分配。

对于某些领域,这种情况并不总是可能的,因此只有在构造函数/静态初始化器块中初始化(根据字段是否为静态)时,最终字段才被视为已经被完全分配。

所有这些都在明确分配规则中有更详细的描述。


为什么编译器不能保证带有字段的对象在内存中是连续的? - manifold
2
他怎么可能这样做?如果字段是公共的或受保护的,任何在运行时出现在类路径中的类都可以访问它,编译器不知道这些类并且无法推理它们。对于私有字段,情况当然是不同的,但他们可能完全省略了字段。 - Polygnome
@Polygnome,是的,这很有道理。如果允许最终实例变量在方法中进行初始化,那将是完全错误的,因为该方法可以被调用任意次数... - manifold
@viru 这不是原因。编译器只有在具有全局知识的情况下才能确定方法是否可以被多次调用。但它没有,因为JAR文件可能在运行时出现在类路径中。另一个原因是对象构造 - 通过强制在构造函数中初始化字段,您可以确保对象不能部分构造。 - Polygnome

1

您的本地变量c不会出错,因为它不是静态字段。您的实例变量被定义为静态常量,因此需要初始化。


1
@kamat 这不仅仅是关于静态字段的问题,即使非静态的最终变量没有被初始化,编译也会失败。 - manifold
1
你不能将局部变量声明为静态... - manifold

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接