【发布时间】:2013-07-11 11:47:41
【问题描述】:
来自有效的 Java
您可以在
enum类型中添加或重新排序常量,而无需重新编译其客户端,因为导出常量的字段在enum类型及其客户端之间提供了一层绝缘:常量值不会编译到客户端中因为它们在intenum模式中。
我从this link 了解到int enum patterns 是编译时常量。我想知道enum在内部是如何工作的?
【问题讨论】:
来自有效的 Java
您可以在
enum类型中添加或重新排序常量,而无需重新编译其客户端,因为导出常量的字段在enum类型及其客户端之间提供了一层绝缘:常量值不会编译到客户端中因为它们在intenum模式中。
我从this link 了解到int enum patterns 是编译时常量。我想知道enum在内部是如何工作的?
【问题讨论】:
枚举不是编译时常量。所以它们的值不会被编译复制到每个使用它们的类。这与 int 值不同,它可以是编译时常量。
所以如果你有这样的课程
public class Constants {
public static final int FOO = 1;
}
还有另一门课
public class Client {
public static void main(String[] args) {
System.out.println(Constants.FOO);
}
}
该类将打印 1。现在将 FOO 的声明更改为
public static final int FOO = 27;
并在不重新编译客户端的情况下重新编译常量。执行客户端。打印出来的值还是1,因为在Client类编译的时候已经被编译器拷贝到Client类了。
没有办法使用枚举来产生这种效果。如果您将值存储为枚举的实例变量,或者如果您引用它的 ordinal(),您将始终获得正确的值,即使不重新编译 Client 类。
【讨论】:
.ordinal() 值了:它可能会改变。
重要的是要理解枚举是按名称引用的。
假设我们有一个枚举:
package com.example;
public enum MyEnum {
ONE, TWO, THREE
}
还有一个简单的测试类:
package com.example;
public class EnumTest {
public static void main(String[] args) {
System.out.println(MyEnum.TWO);
}
}
现在让我们使用javap命令行工具反编译测试类:
javap -verbose -c com/example/EnumTest 产生这个(为简洁起见被截断):
Compiled from "EnumTest.java"
...
const #22 = Field #23.#25; // com/example/MyEnum.TWO:Lcom/example/MyEnum;
const #23 = class #24; // com/example/MyEnum
const #24 = Asciz com/example/MyEnum;
const #25 = NameAndType #26:#27;// TWO:Lcom/example/MyEnum;
const #26 = Asciz TWO;
const #27 = Asciz Lcom/example/MyEnum;;
...
{
public static void main(java.lang.String[]);
Code:
Stack=2, Locals=1, Args_size=1
0: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream;
3: getstatic #22; //Field com/example/MyEnum.TWO:Lcom/example/MyEnum;
6: invokevirtual #28; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
9: return
}
请注意,此代码引用了常量 #22,而后者又间接引用了 #26,即 ASCII 字符串“TWO”。
只要MyEnum的类名不变,实例TWO的名称不变,就不必重新编译类EnumTest。
【讨论】: