在Java中,您可以按如下方式创建枚举:
public enum Letter {
A, B, C, D, E, F, G;
static {
for(Letter letter : values()) {
// do something with letter
}
}
}
这个问题涉及“values()”方法。具体来说,它是如何实现的?通常,我可以在Eclipse中使用F3或CTRL + Click跳转到Java类的源代码(即使对于String,Character,Integer甚至Enum这样的类)。可以查看其他枚举方法的来源(例如,valueOf(String))。
“values()”每次调用时都会创建一个新数组吗?如果我将它分配给局部变量然后修改其中一个元素,会发生什么(显然这不会影响values()返回的值,这意味着每次都会分配一个新数组。)
它的代码是原生的吗?或者JVM /编译器是否专门处理它,只有当它无法证明它不会被修改时才从values()返回一个新实例。
答案 0 :(得分:105)
基本上,编译器(javac)会将您的枚举转换为包含编译时所有值的静态数组。当您调用values()时,它会为您提供此数组的.clone'd()副本。
鉴于这个简单的枚举:
public enum Stuff {
COW, POTATO, MOUSE;
}
您实际上可以查看Java生成的代码:
public enum Stuff extends Enum<Stuff> {
/*public static final*/ COW /* = new Stuff("COW", 0) */,
/*public static final*/ POTATO /* = new Stuff("POTATO", 1) */,
/*public static final*/ MOUSE /* = new Stuff("MOUSE", 2) */;
/*synthetic*/ private static final Stuff[] $VALUES = new Stuff[]{Stuff.COW, Stuff.POTATO, Stuff.MOUSE};
public static Stuff[] values() {
return (Stuff[])$VALUES.clone();
}
public static Stuff valueOf(String name) {
return (Stuff)Enum.valueOf(Stuff.class, name);
}
private Stuff(/*synthetic*/ String $enum$name, /*synthetic*/ int $enum$ordinal) {
super($enum$name, $enum$ordinal);
}
}
您可以通过创建临时目录并运行来查看javac如何“翻译”您的类:
javac -d <output directory> -XD-printflat filename.java
答案 1 :(得分:2)
如果将其分配给局部变量,则可以修改的唯一内容是为此变量分配另一个枚举。这不会改变枚举本身,因为您只是更改变量引用的对象。
似乎枚举实际上是单例,因此每个枚举中只有一个元素可以存在于整个程序中,这使得==运算符对于枚举是合法的。
因此没有性能问题,您不能在枚举定义中意外更改某些内容。
答案 2 :(得分:1)
它的代码是原生的吗?或者JVM /编译器是否专门处理它,只有当它无法证明它不会被修改时才从values()返回一个新实例。
1)否或至少在目前的实施中没有。请参阅@ lucasmo的答案以获取证据。
2)AFAIK,没有。
假设它可以做到这一点。然而,证明从不在本地修改数组对于JIT来说将是复杂且相对昂贵的。如果数组从调用values()
的方法中“逃脱”,则会变得更加复杂。更贵。
当对所有Java代码进行平均时,这种(假设的)优化可能无法获得回报。
另一个问题是这种(假设的)优化可能会打开安全漏洞。
有趣的是,JLS似乎没有指定values()
成员返回数组副本。常识 1 表示必须做......但实际上没有指定。
1 - 如果values()
返回enum
值的共享(可变)数组,那将是一个巨大的安全漏洞。