我在Java Puzzlers VI中发现了一个错误 - 有人可以解释一下吗?

rip*_*234 23 java

看看Josh Bloch和William Pugh的这个java谜题视频,时间指数0:25:00-0:33:00.

其中一个发言者说,如果你使用小写boolean而不是Boolean,那么LIVING将被视为一个真正的"编译时常量",并且它在初始化时不再重要.

好吧,这一切都很好,但是,看看当你恢复静态初始化和构造函数之间的原始顺序时会发生什么,然后通过一个简单的"提取方法"操作来跟进它.这两个程序打印不同的输出:

public class Elvis {
    private static final Elvis ELVIS = new Elvis();

    private Elvis () {}
    private static final boolean LIVING = true;
    private final boolean alive = LIVING;
    private final boolean lives () {return alive;}

    public static void main(String[] args) {
        System.out.println(ELVIS.lives()); // prints true
    }
}
Run Code Online (Sandbox Code Playgroud)

并使用重构的returnTrue()方法

public class Elvis {
    private static final Elvis ELVIS = new Elvis();

    private Elvis () {}
    private static final boolean LIVING = returnTrue();

    private static boolean returnTrue() {
        return true;
    }

    private final boolean alive = LIVING;
    private final boolean lives () {return alive;}

    public static void main(String[] args) {
        System.out.println(ELVIS.lives()); // prints false
    }
}
Run Code Online (Sandbox Code Playgroud)

为什么在这种情况下提取returnTrue()方法会改变程序输出?

Jos*_*och 47

你所观察到的行为的关键是"常量变量"的概念.这个矛盾在JLS 4.12.4中定义为原始类型或类型String的变量,它是final并用编译时常量表达式初始化.在JLS 13.1中,它表示对常量字段的引用在编译时被解析为它们表示的常量值(即,它们是内联的).视频中的难题依赖于布尔既不是基元也不是字符串的事实.您的变体依赖于以下事实:在表达式中调用方法(returnTrue)会阻止它成为编译时常量表达式.无论哪种方式,LIVING都不是常量变量,程序会显示违反直觉的行为.

Java Puzzlers中的Puzzle 93("Class Warfare")是相关的,甚至更令人惊讶.

  • 我没有完全创建一个帐户; 我刚刚输入了我的名字.但我是"真正的"Josh Bloch.是的,"阶级战争"是Puzzler 93,而不是39.哎呀. (12认同)
  • 等一下.这是真正的Josh Bloch吗?您是否创建了一个帐户来回答这个问题? (10认同)
  • 感谢您的回答.我不能爱Stack Overflow.伟大的视频顺便说一句,我喜欢你和威廉如何呈现每个谜语并假装不知道答案. (4认同)
  • 显然,因为他知道他的东西.你的答案有多权威:) (2认同)

axt*_*avt 5

在第二种情况下LIVING,由运行时表达式初始化,因此它不再是编译时常量,其值falseELVIS构造时.