我正在用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 SomeEnum {
CONSTANT;
}
编译成类似于
final class SomeEnum extends Enum {
public static final SomeEnum CONSTANT = new SomeEnum();
}
Next, execute either the class variable initializers and static
initializers of the class, or the field initializers of the interface,
in textual order, as though they were a single block.
在下面的
final class SomeEnum extends Enum {
public static final SomeEnum CONSTANT = new SomeEnum();
public static final SomeEnum CONSTANT_2 = new SomeEnum();
}
首先将初始化CONSTANT,然后第二次初始化CONSTANT_2.
第四,如果当前线程正在初始化一个类,则可以正常进行.
If the Class object for C indicates that initialization is in progress
for C by the current thread, then this must be a recursive request for
initialization. Release LC and complete normally.
这一切如何融合在一起?
这个
ENUM.ANIMALS.CATS.GARFIELD.RIVAL
被评估为
CATS cat = ENUM.ANIMALS.CATS.GARFIELD;
DOGS rvial = cat.RIVAL;
首次访问GARFIELD会强制初始化枚举类型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.FELIX. CATS当前正在初始化,因此我们继续. CATS.FELIX尚未分配值.目前,它在堆栈中的位置较低.因此其值为空.因此,GIN.RIVALS获得对null的引用.所有DOGS的RIVAL都发生相同的情况.
当所有DOGS都初始化后,执行返回到
private static final CATS FELIX = new CATS(DOGS.AKAME);
其中DOGS.AKAME现在引用完全初始化的DOGS对象.这将分配给它的CATS#RIVAL字段.每个CATS都相同.换句话说,为所有CATS的RIVAL字段分配了DOGS参考,但没有相反的方式.
对语句重新排序只需确定首先枚举哪种枚举类型.
标签:enums,java
来源: https://codeday.me/bug/20191120/2046446.html