枚举学习

枚举学习

1. 问题

jdk1.5开始引入了枚举,可以很方便地组织一些固定类型的常量。
看到《Effective Java》这本书中关于枚举那一条建议中有提到“试图使每个常量都从自己的构造器将自身放到map中,会导致编译时错误”,但是这是为什么呢?

2. 写一个枚举类试验下先

public enum StatusEnum {
    Initial("initial"),
    Done("done")
    ;

    // 自定义的静态map去存储value和枚举实例的键值对
    private static final Map<String, StatusEnum> statusEnumMap = Maps.newHashMap();
    static {
        // 静态构造函数中将枚举实例填充到statusEnumMap中
        for (StatusEnum statusEnum : values()) {
            statusEnumMap.put(statusEnum.value, statusEnum);
        }
    }

    private String value;

    StatusEnum(String value) {
        this.value = value;
        // statusEnumMap.put(value, this);
    }

    /**
     * 通过value的字符串值获取枚举实例项
     * @param value
     * @return
     */
    public static StatusEnum fromString(String value) {
        return statusEnumMap.get(value);
    }
}

如果试图在构造器中引用这个statusEnumMap, 就会出错:

接下来我们用javap反编译其class文件去查看编译后的字节码去探下究竟:

javap -verbose StatusEnum.class

可以看到:

public final class com.sv.lite.StatusEnum extends java.lang.Enum<com.sv.lite.StatusEnum>
/*
 * 常量池省略
 */
{
  public static final com.sv.lite.StatusEnum Initial;
    descriptor: Lcom/sv/lite/StatusEnum;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static final com.sv.lite.StatusEnum Done;
    descriptor: Lcom/sv/lite/StatusEnum;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static com.sv.lite.StatusEnum[] values();
    descriptor: ()[Lcom/sv/lite/StatusEnum;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: getstatic     #1                  // Field $VALUES:[Lcom/sv/lite/StatusEnum;
         3: invokevirtual #2                  // Method "[Lcom/sv/lite/StatusEnum;".clone:()Ljava/lang/Object;
         6: checkcast     #3                  // class "[Lcom/sv/lite/StatusEnum;"
         9: areturn
      LineNumberTable:
        line 7: 0

  public static com.sv.lite.StatusEnum valueOf(java.lang.String);
    descriptor: (Ljava/lang/String;)Lcom/sv/lite/StatusEnum;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: ldc           #4                  // class com/sv/lite/StatusEnum
         2: aload_0
         3: invokestatic  #5                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
         6: checkcast     #4                  // class com/sv/lite/StatusEnum
         9: areturn
      LineNumberTable:
        line 7: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      10     0  name   Ljava/lang/String;

  public static com.sv.lite.StatusEnum fromString(java.lang.String);
    descriptor: (Ljava/lang/String;)Lcom/sv/lite/StatusEnum;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #8                  // Field statusEnumMap:Ljava/util/Map;
         3: aload_0
         4: invokeinterface #9,  2            // InterfaceMethod java/util/Map.get:(Ljava/lang/Object;)Ljava/lang/Object;
         9: checkcast     #4                  // class com/sv/lite/StatusEnum
        12: areturn
      LineNumberTable:
        line 26: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      13     0 value   Ljava/lang/String;

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=5, locals=4, args_size=0
         0: new           #4                  // class com/sv/lite/StatusEnum
         3: dup
         4: ldc           #10                 // String Initial
         6: iconst_0
         7: ldc           #11                 // String initial
         9: invokespecial #12                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
        12: putstatic     #13                 // Field Initial:Lcom/sv/lite/StatusEnum;
        15: new           #4                  // class com/sv/lite/StatusEnum
        18: dup
        19: ldc           #14                 // String Done
        21: iconst_1
        22: ldc           #15                 // String done
        24: invokespecial #12                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
        27: putstatic     #16                 // Field Done:Lcom/sv/lite/StatusEnum;
        30: iconst_2
        31: anewarray     #4                  // class com/sv/lite/StatusEnum
        34: dup
        35: iconst_0
        36: getstatic     #13                 // Field Initial:Lcom/sv/lite/StatusEnum;
        39: aastore
        40: dup
        41: iconst_1
        42: getstatic     #16                 // Field Done:Lcom/sv/lite/StatusEnum;
        45: aastore
        46: putstatic     #1                  // Field $VALUES:[Lcom/sv/lite/StatusEnum;
        49: invokestatic  #17                 // Method com/google/common/collect/Maps.newHashMap:()Ljava/util/HashMap;
        52: putstatic     #8                  // Field statusEnumMap:Ljava/util/Map;
        55: invokestatic  #18                 // Method values:()[Lcom/sv/lite/StatusEnum;
        58: astore_0
        59: aload_0
        60: arraylength
        61: istore_1
        62: iconst_0
        63: istore_2
        64: iload_2
        65: iload_1
        66: if_icmpge     93
        69: aload_0
        70: iload_2
        71: aaload
        72: astore_3
        73: getstatic     #8                  // Field statusEnumMap:Ljava/util/Map;
        76: aload_3
        77: getfield      #7                  // Field value:Ljava/lang/String;
        80: aload_3
        81: invokeinterface #19,  3           // InterfaceMethod java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
        86: pop
        87: iinc          2, 1
        90: goto          64
        93: return
      LineNumberTable:
        line 8: 0
        line 9: 15
        line 7: 30
        line 12: 49
        line 14: 55
        line 15: 73
        line 14: 87
        line 17: 93
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
           73      14     3 statusEnum   Lcom/sv/lite/StatusEnum;
      StackMapTable: number_of_entries = 2
        frame_type = 254 /* append */
          offset_delta = 64
          locals = [ class "[Lcom/sv/lite/StatusEnum;", int, int ]
        frame_type = 248 /* chop */
          offset_delta = 28
}
Signature: #49                          // Ljava/lang/Enum<Lcom/sv/lite/StatusEnum;>;

通过反编译后的代码,不难看出其实我们定义的枚举

  • 枚举是一个类继承自java.lang.Enum<>
  • 定义的每一个枚举项其实是该枚举类中的一个静态域,如:public static final com.sv.lite.StatusEnum Initial

从代码中找到其中的类构造器static块 可以看到先实例化定义的每个静态成员项Initial和Done,然后才是实例化我们定义的静态常量statusEnumMap,所以就解答了前面的问题。

3. 结论

在枚举的实例构造器被调用的时候,枚举中定义的静态变量还没有被实例化,因为枚举类会先去实例化我们定义的枚举项(这时需要调用实例构造器)。

作者:SV

出处:https://www.cnblogs.com/sv00

版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

原文地址:https://www.cnblogs.com/sv00/p/9763786.html

时间: 2024-08-30 14:30:53

枚举学习的相关文章

Java高级特性之枚举学习总结

在Java SE5之前,我们要使用枚举类型时,通常会使用static final 定义一组int常量来标识,代码如下 public static final int MAN = 0; public static final int WOMAN = 1; 相信很多小伙伴,在实际开发中也是那么干的,既然这样已经能实现枚举的功能,为什么还要引入枚举呢?我们接着往下看当我们需要是同这组“int枚举”是代码如下 public void showSex(int sex){ switch(sex){ case

ENUM枚举学习

示例1:#include "stdio.h"void main(){ enum {Mon, Tue, Wed, Thr, Fri, Sat, Sun}; printf("Mon = %d\n", Mon); printf("Tue = %d\n", Tue); printf("Wed = %d\n", Wed); printf("Thr = %d\n", Thr); printf("Fri = %

Rhythmk 一步一步学 JAVA (18): Enum枚举学习

枚举定义: public enum SizeEnum { SMALL, BIG, BIGEST }; public enum SizeStringEnum { SMALL("小"), BIG("大"), BIGEST("巨大"); private String strSizeString; public String toString() { return strSizeString; } private SizeStringEnum(Strin

Enum枚举学习- java5之前和之后的写法

enum枚举 JDK1.5以前枚举的写法 /** * java5.0以前,定义枚举类.类中的对象是有限的 * @author amber * */ public class Season { private final String seasonName; private final String seasonDescription; private Season(String seasonName, String seasonDescription) { this.seasonName = s

数据结构 枚举学习笔记

5 5 5 5 5 = 5 填上  加减乘除 #include<stdio.h> int main() { int j,i[5]; int sign; int result; int count; int num[6]; float left ,right; char oper[5]={' ','+','-','*','/'}; printf("请输入5个数:"); for(j=1;j<=5;j++) scanf("%d",&num[j])

Swift面向对象基础(上)——Swift中的枚举

Swift中枚举 学习笔记来自<极客学院> 1 import Foundation 2 3 /**********1*Swift定义枚举的语法格式*************/ 4 /* 5 enum 枚举名 { 6 使用case关键字列出所有枚举值 7 枚举的其他成员 8 } 9 */ 10 //定义枚举 11 enum Season{ 12 case Spring 13 case Summer 14 case Fall 15 case Winter 16 } 17 //使用一个case列举所

c#编程基础之枚举

枚举的意义就在于限制变量取值范围. 当可以确定的几种取值时才可以用. 如果输入一个字符串需要进行判断是否是我们需要的字符串时,则一般需要这样写: using System; using System.Collections.Generic; using System.Text; namespace 枚举学习 { class Program { static void Main(string[] args) { string s = "Male"; if (s == "Male

选课【树形dp】

树形dp入门经典题 大意就是学每门课可以获得相应的学分 sc[ i ],但学一门课必须先学习他的先修课 给定能学的课程数,求能得的最大学分. 很容易想象出一个树形结构 设 dp [ u ][ j ] 表示以 u 为根节点选 j 门课的学大得分. 接下来我们可以选择学习 u 的子节点, 也可以继续选择子节点的子节点. 我们枚举学习 u 的子节点的个数 k 可得动态转移方程 dp[u][j] = max(dp[u][j], dp[u][j-k-1]+dp[v][k]+sc[v]); #include

算法学习 -- 枚举

在学习枚举算法之前,首先问有关枚举的几个问题 1. 为什么要进行枚举? 2. 需要对哪些对象进行枚举? 3. 如何进行枚举? 4. 枚举的结束条件是什么? 现在针对两个使用枚举算法的实例对以上问题进行分析. 实例一:熄灯问题 给定一个5×6的棋盘,上面有灯,每个灯都有各自的按钮,每个按钮按下去都会使其自己和周围的上下左右四盏灯改变(原来熄灭的变亮,原来亮的变灭),如何操作按钮使这个棋盘上所有灯都熄灭. 实例二:青蛙问题 一个5000×5000的稻田,有很多青蛙从这个稻田上跳过,每只青蛙跳过的步长