枚举常量是什么时候创建的?构造器能读静态字段吗?values() 为什么每次都是新数组?构造抛异常之后会怎样?这篇用打印顺序、javap 与反射逐项验证。
main 开始;访问编译期常量 Color.K = 常量
现在第一次用 Color.GREEN
实例块(每个常量一次)
构造器 r,this=RED,ordinal=0
实例块(每个常量一次)
构造器 g,this=GREEN,ordinal=1
实例块(每个常量一次)
构造器 b,this=BLUE,ordinal=2
static 块:此时 VALUES 已建好,共 3 个
g
构造器里读静态字段(非常量):
编译失败:
<jtmp>/j142b/Main.java:1: error: illegal reference to static field from initializer
public class Main { enum E { A; static int count = 0; E() { count++; } } public static void main(String[] a) { } }
^
1 error
构造器里读 static final 常量: 编译通过
构造器里用 E.n 限定名读静态字段: <jtmp>/j142d/Main.java:1: error: illegal reference to static field from initializer
public class Mai
构造器通过静态方法读 n(绕过检查):
0
main:n=7 1 E.K(编译期常量)= 1
2 E.class.getName() = Main$E
3 E[] arr = new E[1]
4 E.v
[E 初始化]
5 E.m()
6 E.A
单独只做 E.m()(静态方法): 前 → [E 初始化] → 后
单独只做 E.A(枚举常量): 前 → [E 初始化] → 后
单独只做 E.values(): 前 → [E 初始化] → 后
单独只做 E.valueOf("A"): 前 → [E 初始化] → 后
单独只做 E.v(非常量静态字段): 前 → [E 初始化] → 后
Class.forName 的 initialize 参数:
Class.forName(E, false)
Class.forName(E, true)
[E 初始化]
EnumSet.noneOf(E.class) 会触发初始化吗:
switch 前
没碰 E
[E 初始化]
0 生成的 class 文件: ['Main$Color.class', 'Main$Op$1.class', 'Main$Op$2.class', 'Main$Op.class', 'Main.class']
javap -p Main$Color:
final class Main$Color extends java.lang.Enum<Main$Color> {
public static final Main$Color RED;
public static final Main$Color GREEN;
private static final Main$Color[] $VALUES;
public static Main$Color[] values();
public static Main$Color valueOf(java.lang.String);
private Main$Color();
private static Main$Color[] $values();
static {};
}
javap -p Main$Op:
abstract class Main$Op extends java.lang.Enum<Main$Op> {
public static final Main$Op ADD;
public static final Main$Op MUL;
private static final Main$Op[] $VALUES;
public static Main$Op[] values();
public static Main$Op valueOf(java.lang.String);
private Main$Op();
abstract int ap(int, int);
private static Main$Op[] $values();
static {};
}
javap -p Main$Op$1:
final class Main$Op$1 extends Main$Op {
private Main$Op$1(java.lang.String, int);
int ap(int, int);
}
Main$Color 的 <clinit> 字节码前几行:
0: new #1 // class Main$Color
3: dup
4: ldc #29 // String RED
6: iconst_0
7: invokespecial #30 // Method "<init>":(Ljava/lang/String;I)V
10: putstatic #3 // Field RED:LMain$Color;
13: new #1 // class Main$Color
16: dup
17: ldc #31 // String GREEN
19: iconst_1
20: invokespecial #30 // Method "<init>":(Ljava/lang/String;I)V
23: putstatic #7 // Field GREEN:LMain$Color;
26: invokestatic #32 // Method $values:()[LMain$Color;
29: putstatic #10 // Field $VALUES:[LMain$Color;
32: return
}
values() 的字节码:
Code:
0: getstatic #10 // Field $VALUES:[LMain$Color;
3: invokevirtual #14 // Method "[LMain$Color;".clone:()Ljava/lang/Object;
6: checkcast #15 // class "[LMain$Color;"
9: areturn
switch 用到的合成类 Main$1: values() 两次调用是同一个数组吗:false,内容相同:true
改掉返回数组后再取:[RED, GREEN]
valueOf(PINK):java.lang.IllegalArgumentException: No enum constant Main.Color.PINK
valueOf(null):java.lang.NullPointerException: Name is null
valueOf(red):IllegalArgumentException
构造器参数:[class java.lang.String, int]
反射 newInstance:java.lang.IllegalArgumentException: Cannot reflectively create enum objects
getMethod(clone):NoSuchMethodException
getEnumConstants == values 内容:true;getEnumConstants 两次是同一个数组:false
Enum.valueOf(String.class):IllegalArgumentException java.lang.String is not an enum class 第 1 次:ExceptionInInitializerError:null cause=java.lang.IllegalStateException: 构造 B 时失败
第 2 次:NoClassDefFoundError:Could not initialize class Main$Bad cause=java.lang.ExceptionInInitializerError: Exception java.lang.IllegalStateException: 构造 B 时失败 [in threa
第 3 次:NoClassDefFoundError:Could not initialize class Main$Bad cause=java.lang.ExceptionInInitializerError: Exception java.lang.IllegalStateException: 构造 B 时失败 [in threa
X 的构造里引用 Y,Y 的构造里引用 X(循环):
先触发 X:X.A.y = P;Y.P.x = null
对照:普通类里 new 自身的静态字段先于 W 赋值:
类里的 static 字段顺序:seen(W)=0 seenZ(常量)=5 最终 W=5