当前位置: 首页 > 面试题库 >

Java枚举属性根据访问顺序返回null

怀刚毅
2023-03-14
问题内容

我正在用Java探索枚举,看看它们如何被滥用,并且遇到了无法解释的行为。考虑以下类别:

public class PROGRAM {

public enum ENUM {;
    public enum ANIMALS {;
        public enum CATS {
            FELIX(DOGS.AKAME),
            GARFIELD(DOGS.WEED),
            BUBSY(DOGS.GIN);

            CATS(DOGS dog) {this.RIVAL = dog;}
            public DOGS RIVAL;
        }           
        public enum DOGS {
            GIN(CATS.FELIX), WEED(CATS.BUBSY), AKAME(CATS.GARFIELD);

            DOGS(CATS cat) {this.RIVAL = cat;}
            public CATS RIVAL;
        }
    }
}


public static void main(String[] args) {
    System.out.println(ENUM.ANIMALS.CATS.GARFIELD.RIVAL);
    System.out.println(ENUM.ANIMALS.DOGS.GIN.RIVAL);
}
}

如预期的那样,主函数中的第一条语句将显示“ WEED”。第二个将打印“ null”。但是,如果您切换它们,即

    System.out.println(ENUM.ANIMALS.DOGS.GIN.RIVAL);
    System.out.println(ENUM.ANIMALS.CATS.GARFIELD.RIVAL);

第一条语句将显示“ FELIX”,第二条语句将显示“ null”。有没有人可以解释这种现象?

作为参考,我正在运行Java(TM)SE运行时环境(内部版本1.8.0_05-b13)


问题答案:

这与枚举和类初始化有关。

首先,enum只是class具有恒定字段的幻想。也就是说,您声明的枚举常量实际上只是static字段。所以

enum SomeEnum {
    CONSTANT;
}

编译成类似于

final class SomeEnum extends Enum<SomeEnum> {
    public static final SomeEnum CONSTANT = new SomeEnum();
}

其次,static字段以从左到右的顺序初始化,它们出现在源代码中。

接下来,以文本顺序执行类的类变量初始化器和静态初始化器,或接口的字段初始化器,就好像它们是单个块一样。

在下面的

final class SomeEnum extends Enum<SomeEnum> {
    public static final SomeEnum CONSTANT = new SomeEnum();
    public static final SomeEnum CONSTANT_2 = new SomeEnum();
}

CONSTANT将首先被初始化,然后被初始化CONSTANT_2

第三,当您访问类型的常量之一(实际上只是一个字段)时,该enum类型将被[初始化]
[3]static

第四,如果当前线程正在初始化一个类,则可以正常进行。

如果Class对象C表示C当前线程正在进行初始化,则这必须是对初始化的递归请求。释放LC并正常完成。

这一切如何融合在一起?

这个

ENUM.ANIMALS.CATS.GARFIELD.RIVAL

被评估为

CATS cat = ENUM.ANIMALS.CATS.GARFIELD;
DOGS rvial = cat.RIVAL;

首次访问将GARFIELD强制enum类型的初始化CATS。这开始初始化中的枚举常量CATS。编译后,这些看起来像

private static final CATS FELIX = new CATS(DOGS.AKAME);
private static final CATS GARFIELD = new CATS(DOGS.WEED);
private static final CATS BUBSY = new CATS(DOGS.GIN);

这些按顺序初始化。所以FELIX先去。作为其新实例创建表达式的一部分,它访问DOGS.AKAME,而该类型DOGS尚未初始化,因此Java开始对其进行初始化。该DOGS枚举类型,编译,看起来像

private static final DOGS GIN = new DOGS(CATS.FELIX);
private static final DOGS WEED = new DOGS(CATS.BUBSY);
private static final DOGS AKAME = new DOGS(CATS.GARFIELD);

因此,我们从开始GIN。在其新的实例创建表达式中,它尝试访问CATS.FELIXCATS当前正在初始化,因此我们继续。CATS.FELIX尚未分配值。目前,它在堆栈中的位置较低。因此其值为null。因此GIN.RIVALS获得对的引用null。同样的情况,给所有DOGS
RIVAL

DOGS初始化所有后,执行返回

private static final CATS FELIX = new CATS(DOGS.AKAME);

其中,DOGS.AKAME现指完全初始化DOGS的对象。那被分配给它的CATS#RIVAL领域。每个都相同CATS。换句话说,所有的CATS
RIVAL字段都被分配了一个DOGS引用,但是没有相反的方式。

对语句重新排序只是确定enum首先要初始化的类型。



 类似资料:
  • 问题内容: 我正在创建一个可能更改语言的简单程序,并且将字符串数组中的myBundle.properties文件中的属性列表转换为。 这是文件myBundle.properties: 这是Java代码: 但是,令人惊讶的是,当我执行程序时,它以随意的顺序返回了字符串。为什么枚举有这种奇怪的行为? 问题答案: 我不知道ResourceBundle类的确切详细信息,但是在查看您的代码示例时,似乎它具有

  • 问题内容: 通过阅读SCJP书籍,我在第1章“自测”中发现了类似的内容: 注意:代码编译正常。我不明白的是为什么我们可以从变量访问DOG,CAT或FISH常量。我认为(并且也写在书中)DOG,FISH,CAT是常量,其实现方式类似于。 所以,如果它们确实是静态的,为什么我们可以从中访问它们呢?最后一行是我熟悉的方式。 问题答案: 写作 和写作一样。也就是说,编译器将用其编译时类型Animal替换变

  • 问题内容: 我想声明一个枚举Direction,它具有一个返回相反方向的方法(以下语法不正确,即,不能实例化枚举,但它说明了我的观点)。这在Java中可行吗? 这是代码: 问题答案: 对于那些按标题吸引的人:是的,您可以在枚举中定义自己的方法。如果您想知道如何调用这种非静态方法,则可以使用与其他任何非静态方法相同的方法- 在定义或继承该方法的类型实例上调用它。如果是枚举,则此类实例仅为s。 因此,

  • 枚举具有名为'hash value'的属性,该属性是枚举内的索引。

  • 问题内容: 是否可以将属性文件转换为枚举。 我有一个带有很多设置的属性文件。例如 开发人员必须知道密钥才能获得属性的值。而是可以做一些事情,开发人员可以在其中键入MyPropertyEnum。并且键列表将显示在IDE中,就像它显示为Enum一样 问题答案: 我经常使用属性文件+枚举组合。这是一个例子: 现在,您还需要一个属性文件(我经常将其放在src中,因此将其打包到JAR中),其属性与在枚举中使