values() 是如何为 Java 6 枚举实现的?

IT小君   2021-09-26T06:31:22

在 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()返回一个新的实例。

点击广告,支持我们为你提供更好的服务
评论(3)
IT小君

基本上,编译器 (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
2021-09-26T06:31:22   回复
IT小君

如果将其分配给局部变量,则唯一可以修改的是将另一个枚举分配给该变量。这不会更改枚举本身,因为您只是在更改变量引用的对象。

似乎枚举实际上是单例,因此每个枚举中只有一个元素可以存在于整个程序中,这使得 == 运算符对于枚举是合法的。

因此不存在性能问题,您不能意外更改枚举定义中的某些内容。

2021-09-26T06:31:23   回复
IT小君

它的代码是原生的吗?还是JVM/编译器有特殊处理,只有在不能证明不会被修改的情况下才从values()返回一个新的实例。

1) 否。或者至少在当前的实现中不是。请参阅@lucasmo 的答案以获取证据。

2)AFAIK,没有。

假设它可以做到这一点。然而,证明数组从未在本地修改对于 JIT 来说是复杂且相对昂贵的。如果数组从调用的方法中“转义” values(),它会变得更复杂和更昂贵。

很可能这种(假设的)优化不会得到回报……当对所有 Java 代码进行平均时。

另一个问题是这种(假设的)优化可能会打开安全漏洞。


有趣的是,JLS 似乎没有指定values()成员返回数组副本。常识1说它必须做......但实际上并没有指定。

1 - 如果values()返回共享(可变)enum数组,这将是一个巨大的安全漏洞

2021-09-26T06:31:23   回复