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

发布于 2024-07-28 23:26:09 字数 573 浏览 3 评论 0原文

在 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()”每次调用时都会创建一个新数组吗? 如果我将它分配给一个局部变量,然后修改其中一个元素,会发生什么(显然这不会影响 value() 返回的值,这意味着每次都会分配一个新数组)。

它的代码是原生的吗? 或者JVM/编译器是否对其进行了特殊处理,只有在无法证明不会被修改时才从values()返回一个新实例。

In Java, you can create an enum as follows:

public enum Letter {
    A, B, C, D, E, F, G;

    static {
       for(Letter letter : values()) {
          // do something with letter
       }
    }
}

This question concerns the "values()" method. Specifically, how is it implemented? Usually, I could jump to the source for Java classes using F3 or CTRL+Click in Eclipse (even for classes like String, Character, Integer, and even Enum). It is possible to view the source of the other enum methods (e.g., valueOf(String)).

Does "values()" create a new array each time it is invoked? If I assign it to a local variable and then modify one of the elements, what happens (clearly this won't affect the value returned by values(), which implies that a new array is allocated each time).

Is the code for it native? Or does the JVM / compiler treat it specially, only returning a new instance from values() when it cannot prove that it will not be modified.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

〆一缕阳光ご 2024-08-04 23:26:09

基本上,编译器 (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

Basically, the compiler (javac) translates your enum into a static array containing all of your values at compile time. When you call values(), it gives you a .clone'd() copy of this array.

Given this simple enum:

public enum Stuff {
   COW, POTATO, MOUSE;
}

You can actually look at the code that Java generates:

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);
    }
}

You can look at how javac 'translates' your classes by making a temporary directory and running:

javac -d <output directory> -XD-printflat filename.java
我也只是我 2024-08-04 23:26:09

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

看来枚举实际上是单例,因此每个枚举中只能存在一个元素,这使得 == 运算符对于枚举来说是合法的。

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

If you assign it to a local variable the only thing that you can modify is assigning another enum to this variable. This will not change the enum itself because you are only changing the object your variable references.

It seems that the enums are in fact singletons so that only one element from each enum can exist in you whole program this makes the == operator legal for enums.

So there is no performance problem and you can't accidentally change something in your enum definition.

南城追梦 2024-08-04 23:26:09

它的代码是原生的吗? 或者JVM/编译器是否对其进行了特殊处理,只有在无法证明不会被修改时才从values()返回一个新实例。

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

2)据我所知,不。

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

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

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


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

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

Is the code for it native? Or does the JVM / compiler treat it specially, only returning a new instance from values() when it cannot prove that it will not be modified.

1) No. Or at least not in current implementations. See @lucasmo's answer for the evidence.

2) AFAIK, no.

Hypothetically it could do this. However, proving that an array is never modified locally would be complicated and relatively expensive for the JIT to perform. If the array "escapes" from the method that called values(), it gets more complex & more expensive.

The chances are that this (hypothetical) optimization would not pay off ... when averaged over all Java code.

The other issue is that this (hypothetical) optimization might open up security holes.


The interesting thing though is that the JLS does not seem to specify that the values() member returns an array copy. Common sense1 says that it must do ... but it is not actually specified.

1 - It would be a gaping security hole if values() returned a shared (mutable) array of enum values.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文