当前位置: 首页 > 知识库问答 >
问题:

最终定义不清吗?

董和风
2023-03-14

首先,一个难题:下面的代码打印什么?

public class RecursiveStatic {
    public static void main(String[] args) {
        System.out.println(scale(5));
    }

    private static final long X = scale(10);

    private static long scale(long value) {
        return X * value;
    }
}

静态修饰符与最终修饰符结合使用也用于定义常量。最后一个修饰符指示此字段的值不能更改。

来源:https://docs.oracle.com/javase/tutorial/java/javaoo/classvars.html[加重点]

我的问题是:这是一个bug吗?final定义不清吗?

public class RecursiveStatic {
    public static void main(String[] args) {
        System.out.println(scale(5));
    }

    private static final long X = scale(10) + 3;

    private static long scale(long value) {
        System.out.println("X = " + X);
        return X * value;
    }
}
a=5
a=5

共有1个答案

印劲
2023-03-14

一个非常有趣的发现。为了理解它,我们需要深入研究Java语言规范(JLS)。

原因是final只允许一次赋值。但是,默认值是不赋值。事实上,每一个这样的变量(类变量、实例变量、数组组件)在赋值之前从一开始就指向它的默认值。然后第一个赋值更改引用。

看一下下面的例子:

private static Object x;

public static void main(String[] args) {
    System.out.println(x); // Prints 'null'
}
public static void main(String[] args) {
    Object x;
    System.out.println(x);
    // Compile-time error:
    // variable x might not have been initialized
}

局部变量(§14.4,§14.14)在使用之前必须通过初始化(§14.4)或赋值(§15.26),以一种可以使用明确赋值规则(§16(明确赋值))来验证的方式显式地给出一个值。

现在我们来看看§4.12.4final:

最终变量

public static void main(String[] args) {
    System.out.println("After: " + X);
}

private static final long X = assign();

private static long assign() {
    // Access the value before first assignment
    System.out.println("Before: " + X);

    return X + 1;
}
Before: 0
After: 1
private static long assign() {
    // Assign X
    X = 1;

    // Second assign after method will crash
    return X + 1;
}

多亏了@Andrew,我找到了一个JLS段落,正好涵盖了这个场景,它还演示了这个场景。

但首先让我们来看看

private static final long X = X + 1;
// Compile-time error:
// self-reference in initializer

为什么这是不允许的,而来自方法的访问是允许的?请看§8.3.3,它讨论了如果字段尚未初始化,对字段的访问何时受到限制。

>

  • 引用出现在C的类变量初始化器中,或者出现在C的静态初始化器中(§8.7);和

    引用可以出现在F自己的声明符的初始值设定项中,也可以出现在F声明符的左侧;和

    引用不在赋值表达式的左手边(§15.26);和

    class Z {
        static int peek() { return j; }
        static int i = peek();
        static int j = 1;
    }
    class Test {
        public static void main(String[] args) {
            System.out.println(Z.i);
        }
    }
    

    产生输出:

    0
    

    因为i的变量初始化器使用类方法peek在j被其变量初始化器初始化之前访问变量j的值,此时它仍然具有默认值(§4.12.5)。

  •  类似资料:
    • 问题内容: 首先,一个难题:以下代码显示什么? 回答: 0 扰流板如下。 如果您打印的规模(长),并重新定义,印刷品会那么。这意味着暂时设置为,后来又设置为。这是违反! 静态修饰符与最终修饰符结合使用,还可以定义常量。最后的修饰符指示 此字段 的值 不能更改 。 来源:https : //docs.oracle.com/javase/tutorial/java/javaOO/classvars.h

    • 从现在起,在退出后,每当重新进入 Chroot 环境,请使用下面修改过的 chroot 命令: chroot "$LFS" /usr/bin/env -i \ HOME=/root TERM="$TERM" PS1='\u:\w\$ ' \ PATH=/bin:/usr/bin:/sbin:/usr/sbin \ /bin/bash --login 这样做的理由是起初在

    • 问题内容: 从http://docs.oracle.com/javase/6/docs/api/java/lang/String.html中我可以读到: 从某种意义上说该属性是多余的,这是否意味着a 在Java中实际上没有意义? 问题答案: 该对象是不可变的,但实际上是对可以更改的对象的引用。 例如: 您可以重新分配此变量保存的值(以使其引用其他字符串): 但是,这样做: 然后,上述重新分配将是不

    • 到目前为止,我认为有效的final和final或多或少是等价的,如果在实际行为中不完全相同,JLS会将它们视为相似的。然后我发现了这个人为的场景: 显然,JLS在这两者之间产生了重要的区别,我不知道为什么。 我阅读其他线程,如 最终和有效最终之间的差异 有效的最终变量vs最终变量 变量“有效最终”是什么意思 但他们并没有详细说明。毕竟,在更广泛的层面上,它们似乎几乎相当。但深入研究,他们显然有所不

    • 问题内容: 我想在UITextField上创建自定义清除按钮,即使用rightView并将图像放在此处,问题是将原始的清除按钮事件附加到该自定义rightView上。 在Objective-C中,我可以这样做: 现在如何将其转换为Swift?或任何解决方法? 问题答案: 您可以将自定义按钮添加为类似这样的右视图

    • 在Java中,重写方法会受到很大的批评,尽管我不明白这是为什么。像这样的类使用它来确保在Java8和Java10中调用。然而,Java9引入了,它使用PhantomReference机制而不是GC终结。一开始,我认为这只是一种向第三方类添加终结的方法。但是,它的javadoc中给出的示例显示了一个可以很容易地用终结器重写的用例。 我是否应该用清洁器重写所有的方法?(当然,我没有很多。只有一些使用O