界说

1)界说

枚举类是Java 5引入的,在Java 5之前,Java并没有内置的枚举类型,只能经过自界说类来完成相似枚举的功用。例如:

public class EnumClass {
    public static final int CONSTANT1 = xxx;  
    public static final int CONSTANT2 = xxx;  
    public static final int CONSTANT3 = xxx;  
    ...  
}  

Java 枚举类是一种特别类型的数据结构,一般用来存储界说一些字符串,数字等数据结构。枚举类中的每个常量都称为枚举常量。枚举类在Java中运用关键字enum界说。一般枚举类格式如下:

public enum EnumClass {
    CONSTANT1,  
    CONSTANT2,  
    CONSTANT3,  
    ...
}  

其间,EnumClass为枚举类的称号,CONSTANT1、CONSTANT2、CONSTANT3等为枚举常量的称号。枚举类的引入,供给了一种更简洁、更安全的界说枚举类型的方式。

枚举类能够直接在其他类中引用,例如:

public enum Color {
    RED,  
    GREEN,  
    BLUE  
}  
public class ColorUtil {
    private ColorUtil() {  
    }  
    public static void main(String[] args) {  
        Color red = Color.RED;  
    }  
}  

枚举类是类,所以当然也能够在其他类内部界说:

public class Example {
    public enum Color {  
        RED,  
        GREEN,  
        BLUE  
    }  
    public static void main(String[] args) {  
        System.out.println(Color.RED);  
    }  
}  

枚举类在内部界说时,能够将其作为内部类来界说,也能够将其作为静态内部类来界说,内部界说的枚举类能够拜访外部类的成员变量和办法,比方:

public class Example {
    private static int privateVariable = 10;  
    public enum Color {  
        RED, GREEN, BLUE;  
        public void print() {  
            System.out.println(name() + " has a value of " + Example.privateVariable);  
        }  
    }  
    public static void main(String[] args) {  
        Example.Color.RED.print();  
    }  
}  

这段代码运转main办法的成果为:

【Java】深化分析Java枚举类

2)内部完成

咱们借助IDEA中的一个反编译分析插件Jadx Class Decompiler反编译刚刚创立的Color枚举类,首要装置插件如下:

【Java】深化分析Java枚举类

装置完成后,右键点击需要反编译的java文件,这里挑选Color.java:

【Java】深化分析Java枚举类

挑选01 分析字节码,能够得到分析成果如下:

【Java】深化分析Java枚举类

能够看到,枚举类是public和final润饰的,这表明它不能像一般的类一样被承继,也能够看到枚举类承继自java.lang.Enum类型,而且界说了public static final润饰的三个实例变量RED GREEN BLUE,这三个实例变量实践都是Color的实例目标。 此外其内部还界说了静态的values办法,它会回来一个包括所有枚举实例的Color[]结构的列表,以及valuesOf(String input)办法,它经过传入对应的枚举常量字符串,回来对应的Color实例。

3)办法与源码

咱们翻阅官方文档能够得知java.lang.Enum包括了如下一些办法(查看Enum类源码也能看到):

【Java】深化分析Java枚举类

其间特有办法应属valuesOf()和ordinal(),valuesOf()刚刚已经阐明,它会回来指定称号的枚举常量,例如:

public enum Color {
    RED,   
    GREEN,   
    BLUE;  
}  
Color color = Color.valueOf("GREEN");  

ordinal()办法主要是获取枚举量在枚举类中的次序,比方在上面比如中RED排在第一位,ordinal()回来值为0,示例如下:

public static void main(String[] args) {
    Color red = Color.RED;  
    int ordinal = red.ordinal();  // ordinal = 0  
}  

别的在字节码分析成果中能够看到其实Enum是有结构办法的,可是这个结构办法用户无法调用,只有编译器能够调用,咱们翻阅java.lang.Enum源码能够看到:

【Java】深化分析Java枚举类

别的Enum还针对枚举常量完成了compareTo办法,而且这个compareTo办法默认是依据枚举的ordinal来比照的,也便是依据枚举在枚举类的声明次序来比照的。

【Java】深化分析Java枚举类

高档特性

1)switch用法

咱们知道一般在运用switch进行判别时,能够运用整数、字符串,乃至表达式作为条件,但其实switch也支持枚举,而且不需要运用枚举的引用,代码示例如下:

import java.util.Arrays;
import java.util.List;  
import java.util.stream.Collectors;  
public class ColorUtil {  
    private ColorUtil() {  
    }  
    private static String getDescription(Color color) {  
        switch(color) {  
            case RED:  
                return "The color of blood";  
            case GREEN:  
                return "The color of grass";  
            case BLUE:  
                return "The color of the sky";  
            default:  
                return "";  
        }  
    }  
    public static void main(String[] args) {  
        Color red = Color.RED;  
        String description = getDescription(red);  
        System.out.println(description);  
    }  
}  

运转能够得到成果如下:

【Java】深化分析Java枚举类

2)自界说传值与结构函数

这个特性我本人经常运用到,首要枚举类中能够界说好特点,然后自界说结构函数,在声明枚举类实例的时分传入对应的特点值,比方在事务中可能用到的isDelete字段,咱们能够界说枚举类如下:

import lombok.Getter;
public enum IsDeleteEnum {  
    TRUE(1, "是"),  
    FALSE(0, "否"),  
    ;  
    //值描述  
    @Getter  
    private String desc;  
    //枚举值  
    @Getter  
    private Integer code;  
    IsDeleteEnum(Integer code, String desc) {  
        this.code = code;  
        this.desc = desc;  
    }  
}  

这里包括了两个成员变量code和desc,分别代表枚举的值和描述信息。 这个类运用了Lombok库中的@Getter注解,枚举值和称号能够被Getter办法直接拜访。

假如上面这个还是初级版本,那么下面这个高档版本则更为典型和常用,咱们界说了ColorEnum的枚举类,它包括RED、BLUE、GREEN三个枚举值。每个枚举值都有一个值和称号,而且有一个静态的Map用于依据值获取枚举常量。这个类还供给了一个公共的静态办法of(),用于依据给定的值回来对应的枚举常量,假如没有找到则回来null。

import lombok.Getter;
import java.util.HashMap;  
import java.util.Map;  
public enum ColorEnum {  
    RED(0, "红色"),  
    BLUE(1, "蓝色"),  
    GREEN(2, "绿色"),  
    ;  
    // 枚举值  
    @Getter  
    private Integer value;  
    @Getter  
    private String name;  
    private static Map<Integer, ColorEnum> map = new HashMap<>();  
    static {  
        for (ColorEnum r : ColorEnum.values()) {  
            map.put(r.getValue(), r);  
        }  
    }  
    ColorEnum(Integer value, String name) {  
        this.value = value;  
        this.name = name;  
    }  
    public static ColorEnum of(Integer value) {  
        return map.getOrDefault(value, null);  
    }  
}  

假如我想知道某个常量值x是否包括在枚举类ColorEnum 中,就能够运用of()办法:

public static void main(String[] args) {
    System.out.println(ColorEnum.of(0) == null);  // false  
    System.out.println(ColorEnum.of(3) == null);  // true  
}  

运转成果如下:

【Java】深化分析Java枚举类

3)枚举完成笼统办法

枚举中不只能够界说一些整数或是字符串常量,乃至能够从头完成笼统办法,咱们界说了一个枚举类型Weekday,表明一周中的每一天。每个枚举常量都重写了笼统办法doSomething(),并完成了具体的事务逻辑,代码如下:

public enum Weekday {
    MONDAY {  
        @Override  
        public void doSomething() {  
            System.out.println("Doing something on Monday");  
        }  
    },  
    TUESDAY {  
        @Override  
        public void doSomething() {  
            System.out.println("Doing something on Tuesday");  
        }  
    },  
    WEDNESDAY {  
        @Override  
        public void doSomething() {  
            System.out.println("Doing something on Wednesday");  
        }  
    },  
    THURSDAY {  
        @Override  
        public void doSomething() {  
            System.out.println("Doing something on Thursday");  
        }  
    },  
    FRIDAY {  
        @Override  
        public void doSomething() {  
            System.out.println("Doing something on Friday");  
        }  
    },  
    SATURDAY {  
        @Override  
        public void doSomething() {  
            System.out.println("Doing something on Saturday");  
        }  
    },  
    SUNDAY {  
        @Override  
        public void doSomething() {  
            System.out.println("Doing something on Sunday");  
        }  
    };  
    // 笼统办法  
    public abstract void doSomething();  
    public static void main(String[] args) {  
        Weekday day = Weekday.MONDAY;  
        day.doSomething();  
    }  
}  

在main函数中,经过Weekday的枚举常量来调用对应的办法,这样能够依据枚举常量来履行不同的操作,大大增加代码的可读性和可维护性。 这段代码履行成果为:

【Java】深化分析Java枚举类

4)枚举注解特点

咱们自界说一个名为ColorInterface的注解用于符号字段。注解界说一个value特点,类型为咱们上文创立的的ColorEnum枚举类型。注解的保存策略为RetentionPolicy.RUNTIME,表明在运转时能够拜访到该注解。

import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.FIELD)  
public @interface ColorInterface {  
    ColorEnum value();  
}  

如下面代码所示,咱们能够经过咱们自界说的ColorInterface注解符号Flower类中的特点。main办法创立了一个Flower目标,运用反射获取Flower类中的Lily字段的注解,然后经过反射获取注解的value特点,也便是咱们的枚举:

import java.lang.reflect.Field;
public class Flower {  
    @ColorInterface(ColorEnum.BLUE)  
    private int Lily;  
    @ColorInterface(ColorEnum.RED)  
    private int rose;  
    public static void main(String[] args) throws Exception {  
        Flower flower = new Flower();  
        Field field = Flower.class.getDeclaredField("Lily");  
        ColorInterface annotation = field.getAnnotation(ColorInterface.class);  
        ColorEnum value = annotation.value();  
        System.out.println(value);  
    }  
}  

代码运转如下:

【Java】深化分析Java枚举类

5)枚举完成接口

Java枚举类还能够完成接口,能够为枚举类型增加更多的办法和行为,然后扩展枚举类的功用,枚举类能够具有更多的灵活性和可扩展性此外。

public interface Animal {
    String makeSound();
    String getName();
}

public enum Dog implements Animal {
    BROWN("Buddy", "Buddy the Brown Dog"),
    BLACK("Max", "Max the Black Dog")
    ;
    private String name;
    private String sound;
    Dog(String name, String sound) {
        this.name = name;
        this.sound = sound;
    }
    @Override
    public String makeSound() {
        return sound;
    }
    @Override
    public String getName() {
        return name;
    }
    public static void main(String[] args) {
        Animal dog = Dog.BLACK;
        System.out.println(dog.getName()); // 输出: Max
        System.out.println(dog.makeSound()); // 输出: Max the Black Dog
    }
}

运转成果如下!:

【Java】深化分析Java枚举类

总结

枚举类是Java中的原始类型之一,能够作为办法参数和回来值。枚举类能够经过类内的枚举常量表明特定的值。本文从源码和字节码完成的角度对枚举类的原理做了论述,而且还展示了枚举类的多种高档用法,枚举类不只能够像一般的类常量一样进行拜访和运用,而且能够进行多态操作,不同的枚举常量能够有不同的办法完成,而且能够作为办法的参数和回来值。 因此枚举类有如下长处:

1.进步代码的可读性。枚举类的命名规范明晰明晰,能够更直观地表达代码的含义,进步代码的可读性

2.更简单的调试和维护。枚举类中的办法和变量都在一个当地,便利调试和维护。

3.更好的类型安全性。运用枚举类能够供给更好的类型安全性,避免了运用整数或其他类型的过错。

4.能够便利的进行多态操作:枚举类支持办法的重写和多态,能够便利的扩展功用。