2

toString() と name() が同じ文字列を参照している理由を誰か説明できますか? == を使用して文字列リテラルと比較すると、すべて合格です! 列挙型名は JVM の文字列プールでどのように機能しますか?

static enum User
{
   BASIC, PREMIUM;
}

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

4 に答える 4

5

まあ、同じプライベート フィールドEnum.name()Enum.toString()返すので、参照は常に同じになります。両方の呼び出しが返さnameれ、name == name常に true になります。

ただし、あなたの質問に適切に答えるために、JVM の内部文字列プールには、個別の文字列のコピーが 1 つだけ格納されます。"BASIC"1 つの異なる文字列 のみを要求しています。Strings は不変であるため.toString()、一度だけ格納され.name()、それらの呼び出しが異なるフィールドを返したとしても、同じ参照を返す可能性があります。

編集: また、文字列リテラル (ソース コードの引用符で囲まれた文字列) はすべてコンパイル時にまとめられ、重複はすべて同じ参照にマップされます。したがって、たとえば、リテラルを使用している場所がソース コード全体にある場合、"Hello I am a string literal"その正確な文字列は 1 回だけ格納され、文字列は不変で決して変更されないため、そのリテラルを使用したすべての場所でソース コードは、JVM 文字列プールに保存されている単一の場所への参照を使用するようになりました。これは、可能であれば、同じもののコピーを大量に作成しない方が明らかに優れているためです。これは非常に単純化しすぎていますが、アイデアはわかります。

于 2016-03-31T20:44:00.837 に答える
3

enum クラスをコンパイルして逆アセンブルすると、次の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

enum がコンパイルされるまでには、それはただの普通の Java.classファイルであり、実行時に唯一の特徴は、それが拡張されEnumACC_ENUMフラグが設定されているという事実だけです。他のすべては単なるバイトコードです。

名前を含む列挙定数を設定するために、コンパイラは理論的には複雑なリフレクションを使用して値の名前から名前を派生させることができますが、代わりに名前を文字列定数とインライン化する方がはるかに簡単で効果的です。静的初期化子は名前をループし、プライベート コンストラクターを呼び出して値のインスタンスをインスタンス化し、それらをプライベート$VALUES配列に割り当てます。

これらの文字列は定数プールにあるため、通常の重複排除ロジックが適用されます。toString()デフォルトの実装は単純に return であるため、同じオブジェクトを返しnameます。

于 2016-03-31T20:41:42.213 に答える