> For the complete documentation index, see [llms.txt](https://ldbmcs.gitbook.io/java/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ldbmcs.gitbook.io/java/java-39/java-ji-chu/java-ji-chu-mei-ju.md).

# Java 基础 - 枚举

## 1. 基础

### 1.1 基本用法

定义：`enum`

```java
public enum Size {
    SMALL, MEDIUM, LARGE
}
```

1. `toString()`方法返回其字面值，所有枚举类型也都有一个name()方法，返回值与toString()一样。

   ```java
   Size size = Size.SMALL;
   System.out.println(size.toString());
   System.out.println(size.name());
   ----
   Output: "SMALL"  
   ```
2. `valueOf(String)`返回字符串对应的枚举值。

   ```java
   System.out.println(Size.SMALL==Size.valueOf("SMALL"));
   ---
   Output: true
   ```
3. `values()`返回一个包括所有枚举值的数组，顺序与声明时的顺序一致。

   ```java
   for(Size size : Size.values()){
       System.out.println(size);
   }
   ---
   Output: SMALL, MEDIUM, LARGE
   ```
4. `int ordinal()`表示枚举值在声明时的顺序，从0开始。

   ```java
   Size size = Size.MEDIUM;
   System.out.println(size.ordinal());
   ---
   Output: 1
   ```

### 1.2 枚举的好处

* 定义枚举的语法更为简洁。
* **枚举更为安全**，一个枚举类型的变量，它的值要么为null，要么为枚举值之一，不可能为其他值，但使用整形变量，它的值就没有办法强制，值可能就是无效的。
* 枚举类型自带很多便利方法(如values, valueOf, toString等)，易于使用。

### 1.3 实现原理

**枚举类型实际上会被Java编译器转换为一个对应的类，这个类继承了Java API中的`java.lang.Enum`类。**

Enum类有两个实例变量`name`和`ordinal`，在构造方法中需要传递，`name(), toString(), ordinal(), compareTo(), equals()`方法都是由Enum类根据其实例变量name和ordinal实现的。

values和valueOf方法是编译器给每个枚举类型自动添加的，上面的枚举类型Size转换后的普通类的代码大概如下所示：

```java
public final class Size extends Enum<Size> {
    public static final Size SMALL = new Size("SMALL",0);
    public static final Size MEDIUM = new Size("MEDIUM",1);
    public static final Size LARGE = new Size("LARGE",2);
    
    private static Size[] VALUES =
            new Size[]{SMALL,MEDIUM,LARGE};
    
    private Size(String name, int ordinal){
        super(name, ordinal);
    }
    
    public static Size[] values(){
        Size[] values = new Size[VALUES.length];
        System.arraycopy(VALUES, 0,
                values, 0, VALUES.length);
        return values;
    }
    
    public static Size valueOf(String name){
        return Enum.valueOf(Size.class, name);
    }
}
```

* Size是final的，不能被继承，`Enum<Size>`表示父类。
* Size有一个私有的构造方法，接受`name`和`ordinal`，传递给父类，私有表示不能在外部创建新的实例。
* 三个枚举值实际上是三个静态变量，也是final的，不能被修改。
* values方法是编译器添加的，内部有一个`values数组`保持所有枚举值。
* valueOf方法调用的是父类的方法，额外传递了参数Size.class，表示类的类型信息，父类实际上是回过头来调用values方法，根据name对比得到对应的枚举值的。

## 2. 典型场景

```java
public enum Size {
    SMALL("S","小号"),
    MEDIUM("M","中号"),
    LARGE("L","大号");
    
    private String abbr;
    private String title;
    
    private Size(String abbr, String title){
        this.abbr = abbr;
        this.title = title;
    }

    public String getAbbr() {
        return abbr;
    }

    public String getTitle() {
        return title;
    }
    
    public static Size fromAbbr(String abbr){
        for(Size size : Size.values()){
            if(size.getAbbr().equals(abbr)){
                return size;
            }
        }
        return null;
    }
}
```

### 2.2 实现原理

```java
public final class Size extends Enum<Size> {
  public static final Size SMALL =        
          new Size("SMALL",0, "S", "小号");     
  public static final Size MEDIUM =       
          new Size("MEDIUM",1,"M","中号");      
  public static final Size LARGE =        
          new Size("LARGE",2,"L","大号");       
                                          
  private String abbr;                    
  private String title;                   
                                          
  private Size(String name, int ordinal,  
          String abbr, String title){         
      super(name, ordinal);               
      this.abbr = abbr;                   
      this.title = title;                 
  }
  //... 其他代码
}
```

每个枚举值经常有一个关联的标示(id)，通常用int整数表示，使用整数可以节约存储空间，减少网络传输。一个自然的想法是使用枚举中自带的ordinal值，但ordinal并不是一个好的选择。

为什么呢？因为ordinal的值会随着枚举值在定义中的位置变化而变化，但一般来说，我们希望id值和枚举值的关系保持不变，尤其是表示枚举值的id已经保存在了很多地方的时候。

## 3. 高级用法

枚举还有一些高级用法，比如说，**每个枚举值可以有关联的类定义体，枚举类型可以声明抽象方法，每个枚举值中可以实现该方法，也可以重写枚举类型的其他方法。**

```java
public enum Size {
    SMALL {
        @Override
        public void onChosen() {
            System.out.println("chosen small");
        }
    },MEDIUM {
        @Override
        public void onChosen() {
            System.out.println("chosen medium");
        }
    },LARGE {
        @Override
        public void onChosen() {
            System.out.println("chosen large");
        }
    };
    
    public abstract void onChosen();
}
```

这种写法内部是怎么实现的呢？每个枚举值都会生成一个类，这个类继承了枚举类型对应的类，然后再加上值特定的类定义体代码，枚举值会变成这个子类的对象，具体代码我们就不赘述了。
