==使用枚举名称()和toString()

时间:2016-03-31 20:25:27

标签: java string enums jvm string-pool

任何人都可以解释为什么toString()和name()引用相同的String?当我使用==将它们与字符串文字进行比较时,它们都会通过!枚举名如何与JVM中的字符串池一起使用?

static enum User
{
   BASIC, PREMIUM;
}

System.out.println("BASIC" == User.BASIC.toString()); // true
System.out.println("BASIC" == User.BASIC.name());     // true

4 个答案:

答案 0 :(得分:5)

好吧,Enum.name()Enum.toString()返回相同的私有字段,因此引用始终是相同的。两个调用返回namename == name始终为真。

但是,为了更好地回答您的问题,JVM的内部字符串池仅存储一个不同字符串的副本。您只是请求一个不同的字符串"BASIC",并且由于String是不可变的,因此它只存储一次,因此.toString().name()可能会返回相同的引用,即使那些电话回归了不同的领域。

编辑: 此外,字符串文字(源代码中的引号中的字符串)都在编译时收集,任何重复项都映射到相同的引用。因此,例如,如果您在源代码的所有位置使用文字"Hello I am a string literal",那么这个确切的字符串只存储一次,因为字符串是不可变的并且永远不会改变,所以每个地方都是在源代码中使用该文字现在使用对它存储在JVM字符串池中的单个位置的引用。这是因为,如果可能的话,最好不要制作同一件事的一堆副本。这是一个巨大的过度简化,但你明白了。

答案 1 :(得分:3)

编译你的枚举类并用javap -verbose反汇编得到这个(部分)输出:

final class User extends java.lang.Enum<User>
  minor version: 0
  major version: 52
  flags: ACC_FINAL, ACC_SUPER, ACC_ENUM
Constant pool:
   #7 = String             #13            // BASIC
   #9 = Fieldref           #4.#38         // User.BASIC:LUser;
  #10 = String             #15            // PREMIUM
  #11 = Fieldref           #4.#39         // User.PREMIUM:LUser;
  #13 = Utf8               BASIC
  #15 = Utf8               PREMIUM

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=4, locals=0, args_size=0
         0: new           #4                  // class User
         3: dup
         4: ldc           #7                  // String BASIC
         6: iconst_0
         7: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
        10: putstatic     #9                  // Field BASIC:LUser;
        13: new           #4                  // class User
        16: dup
        17: ldc           #10                 // String PREMIUM
        19: iconst_1
        20: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
        23: putstatic     #11                 // Field PREMIUM:LUser;
        26: iconst_2
        27: anewarray     #4                  // class User
        30: dup
        31: iconst_0
        32: getstatic     #9                  // Field BASIC:LUser;
        35: aastore
        36: dup
        37: iconst_1
        38: getstatic     #11                 // Field PREMIUM:LUser;
        41: aastore
        42: putstatic     #1                  // Field $VALUES:[LUser;
        45: return
      LineNumberTable:
        line 1: 0

编译枚举时,它只是一个普通的Java .class文件,它在运行时唯一的区别特征是它扩展Enum并具有ACC_ENUM标志组;其他一切都只是普通的字节码。

要设置枚举常量,包括它们的名称,编译器理论上可以使用复杂的反射从值名称中派生名称,但是它更简单,并且将名称内联为字符串常量同样有效。静态初始化程序遍历名称,调用私有构造函数来实例化值实例并将它们分配给私有$VALUES数组。

由于这些字符串位于常量池中,因此通常的重复数据删除逻辑适用。 toString()返回相同的对象,因为它的默认实现只是返回name

答案 2 :(得分:1)

因为java中的枚举类看起来像这样:

public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {

private final String name;

public final String name() {
    return name;
}

private final int ordinal;

public final int ordinal() {
    return ordinal;
}

protected Enum(String name, int ordinal) {
    this.name = name;
    this.ordinal = ordinal;
}

public String toString() {
    return name;
}

一般情况下,您应该将枚举与==进行比较,但如果您想使用名称请使用.equals()

答案 3 :(得分:0)

JVM实现正在重用相同的String常量,因为它们同时被加载到同一个类中。这是您正在使用的特定JVM实现(可能是大多数现有实现)所做的优化。如果你这样做,你就会变得虚假。

String s = (new StringBuilder("BAS")).append("IC").toString();
System.out.println(s == User.BASIC.toString());

这是因为字符串引用s是在运行时创建的。 如果他们从不同的班级加载,你也可能会错误。