澳门新葡萄京娱乐场java enum的用法详解

在编制程序语言中大家,都会接触到枚举类型,常常大家开展夏朝的罗列来贯彻部分约束。Java也不例外。Java中的枚举类型为Enum,本文将对枚举举香港行政局部比较深远的剖判。

Java Enum原理 

什么是Enum

Enum是自Java 5
引进的性状,用来便于Java开荒者达成枚举应用。二个简易的Enum使用如下。

// ColorEnum.java
public enum ColorEmun {
    RED,
    GREEN,
    YELLOW
}

public void setColorEnum(ColorEmun colorEnum) {
    //some code here
}

setColorEnum(ColorEmun.GREEN);
public enum Size{ SMALL, MEDIUM, LARGE, EXTRA_LARGE };

为何会有Enum

在Enum此前的大家选用相像如下的代码实现枚举的成效.

public static final int COLOR_RED = 0;
public static final int COLOR_GREEN = 1;
public static final int COLOR_YELLOW = 2;

public void setColor(int color) {
    //some code here
}
//调用
setColor(COLOR_RED)

但是上边的依然有欠缺康健的地点

  • setColor(COLOR_RED卡塔尔国与setColor(0卡塔尔效果相近,而后人可读性比较糟糕,但却足以健康运作
  • setColor方法能够承担枚举之外的值,比方setColor(3卡塔尔,这种情形下程序恐怕出难题

归纳而言,守旧枚举宛如下多个缺陷

  • 安全性
  • 可读性,特别是打字与印刷日志时

所以Java引进了Enum,使用Enum,我们贯彻地点的枚举就很简短了,何况仍是可以够轻易制止传入违法值的危害.

其实,那些宣称定义的类型是四个类,它恰巧有多少个实例,在这里尽量不要结构新目的。

枚举原理是什么样

Java中Enum的面目实际上是在编写翻译时期调换到对应的类的花样。

首先,为了研究枚举的规律,我们先简单定义一个枚举类,这里以季节为例,类名称叫Season,包罗春夏季穷秋冬八个枚举条目款项.

public enum Season {
    SPRING,
    SUMMER,
    AUTUMN,
    WINTER
}

接下来我们运用javac编写翻译下边包车型客车类,获得class文件.

javac Season.java

然后,大家采取反编写翻译的不二等秘书诀来寻访字节码文件到底是什么.这里运用的工具是javap的粗略命令,先列举一下以此Season下的漫天成分.

company javap Season
Warning: Binary file Season contains com.company.Season
Compiled from "Season.java"
public final class com.company.Season extends java.lang.Enum<com.company.Season> {
  public static final com.company.Season SPRING;
  public static final com.company.Season SUMMER;
  public static final com.company.Season AUTUMN;
  public static final com.company.Season WINTER;
  public static com.company.Season[] values();
  public static com.company.Season valueOf(java.lang.String);
  static {};
}

从上反编写翻译结果可以预知

  • java代码中的Season调换来了世襲自的java.lang.enum的类
  • 既然如此隐式世襲自java.lang.enum,也就意味java代码中,Season不能够再持续其余的类
  • Season被标识成了final,意味着它不可能被接续

于是,在可比多个枚举类型的值时,永恒无需调用equals方法,而一向利用”==”就可以了。(equals(卡塔尔国方法也是一向运用==,
 两个是千人一面包车型大巴功用卡塔尔

static代码块

行使javap具体反编写翻译class文件,获得静态代码块相关的结果为

static {};
    Code:
       0: new           #4                  // class com/company/Season
       3: dup
       4: ldc           #7                  // String SPRING
       6: iconst_0
       7: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
      10: putstatic     #9                  // Field SPRING:Lcom/company/Season;
      13: new           #4                  // class com/company/Season
      16: dup
      17: ldc           #10                 // String SUMMER
      19: iconst_1
      20: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
      23: putstatic     #11                 // Field SUMMER:Lcom/company/Season;
      26: new           #4                  // class com/company/Season
      29: dup
      30: ldc           #12                 // String AUTUMN
      32: iconst_2
      33: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
      36: putstatic     #13                 // Field AUTUMN:Lcom/company/Season;
      39: new           #4                  // class com/company/Season
      42: dup
      43: ldc           #14                 // String WINTER
      45: iconst_3
      46: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
      49: putstatic     #15                 // Field WINTER:Lcom/company/Season;
      52: iconst_4
      53: anewarray     #4                  // class com/company/Season
      56: dup
      57: iconst_0
      58: getstatic     #9                  // Field SPRING:Lcom/company/Season;
      61: aastore
      62: dup
      63: iconst_1
      64: getstatic     #11                 // Field SUMMER:Lcom/company/Season;
      67: aastore
      68: dup
      69: iconst_2
      70: getstatic     #13                 // Field AUTUMN:Lcom/company/Season;
      73: aastore
      74: dup
      75: iconst_3
      76: getstatic     #15                 // Field WINTER:Lcom/company/Season;
      79: aastore
      80: putstatic     #1                  // Field $VALUES:[Lcom/company/Season;
      83: return
}

其中

  • 0~52为实例化SP库罗德ING, SUMMECRUISER, AUTUMN, WINTEENCORE
  • 53~83为创建Season[]数组$VALUES,并将地点的多少个对象归入数组的操作.

Java
Enum类型的语法布局纵然和java类的语法不适合,应该说差异比超大。不过透过编译器编写翻译之后发生的是三个class文件。该class文件通过反编写翻译能够看看实际是生成了多少个类,该类世襲了java.lang.Enum<E>。

values方法

values方法的的再次来到值实际上就是上边$VALUES数组对象

  例如:

swtich中的枚举

在Java中,switch-case是大家平日应用的流水生产线调节语句.当枚举出来以后,switch-case也很好的进展了帮忙.

举个例子说上边包车型大巴代码是一心正常编写翻译,符合规律运作的.

public static void main(String[] args) {
        Season season = Season.SPRING;
        switch(season) {
            case SPRING:
                System.out.println("It's Spring");
                break;

            case WINTER:
                System.out.println("It's Winter");
                break;

            case SUMMER:
                System.out.println("It's Summer");
                break;
            case AUTUMN:
                System.out.println("It's Autumn");
                break;
        }
    }

而是,平常状态下switch-case协理相同int的门类,那么它是怎么变成对Enum的支撑呢,大家反编写翻译上述办法看一下字节码的不务空名意况.

public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field com/company/Season.SPRING:Lcom/company/Season;
       3: astore_1
       4: getstatic     #3                  // Field com/company/Main$1.$SwitchMap$com$company$Season:[I
       7: aload_1
       8: invokevirtual #4                  // Method com/company/Season.ordinal:()I
      11: iaload
      12: tableswitch   { // 1 to 4
                     1: 44
                     2: 55
                     3: 66
                     4: 77
               default: 85
          }
      44: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      47: ldc           #6                  // String It's Spring
      49: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      52: goto          85
      55: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      58: ldc           #8                  // String It's Winter
      60: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      63: goto          85
      66: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      69: ldc           #9                  // String It's Summer
      71: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      74: goto          85
      77: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      80: ldc           #10                 // String It's Autumn
      82: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      88: return

在乎上边代码块有这么的一段代码

8: invokevirtual #4                  // Method com/company/Season.ordinal:()I

实情果真如此,在switch-case中,还是将Enum转成了int值(通过调用Enum.oridinal(卡塔尔国方法State of Qatar

public enum WeekDay { 
     Mon("Monday"), Tue("Tuesday"), Wed("Wednesday"), Thu("Thursday"), Fri( "Friday"), Sat("Saturday"), Sun("Sunday"); 
     private final String day; 
     private WeekDay(String day) { 
            this.day = day; 
     } 
    public static void printDay(int i){ 
       switch(i){ 
           case 1: System.out.println(WeekDay.Mon); break; 
           case 2: System.out.println(WeekDay.Tue);break; 
           case 3: System.out.println(WeekDay.Wed);break; 
            case 4: System.out.println(WeekDay.Thu);break; 
           case 5: System.out.println(WeekDay.Fri);break; 
           case 6: System.out.println(WeekDay.Sat);break; 
            case 7: System.out.println(WeekDay.Sun);break; 
           default:System.out.println("wrong number!"); 
         } 
     } 
    public String getDay() { 
        return day; 
     } 
}

枚举与混淆

在Android开辟中,实行模糊是大家在发表前必不可缺的办事,混下后,大家能增高反编写翻译的难度,在早晚水准上有限支撑了增加了防城港性.

而开拓人士管理混淆越来越多的是将或多或少因素参加不混淆的名单,这里枚举就是要求破除混淆的.

在暗中同意的混淆配置文件中,已经参预了关于对枚举混淆的拍卖

# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

有关为什么要封存values(State of Qatar方法和valueOf(卡塔尔国方法,请参见文章读懂 Android
中的代码混淆 关于枚举的局地

WeekDay经过反编写翻译(javap WeekDay命令卡塔尔之后得到的剧情如下(去掉了汇编代码State of Qatar:

使用proguard优化

利用Proguard举行优化,能够将枚举尽恐怕的调换来int。配置如下

-optimizations class/unboxing/enum

管教上述代码生效,须求确proguard配置文件不分包-dontoptimize指令。

当我们采取gradlew打包是,看见相仿下边的输出,即Number of unboxed enum classes:1代表曾经将三个枚举转变到了int的款式。

Optimizing...
  Number of finalized classes:                 0   (disabled)
  Number of unboxed enum classes:              1
  Number of vertically merged classes:         0   (disabled)
  Number of horizontally merged classes:       0   (disabled)
public final class WeekDay extends java.lang.Enum{ 
    public static final WeekDay Mon; 
    public static final WeekDay Tue; 
    public static final WeekDay Wed; 
    public static final WeekDay Thu; 
    public static final WeekDay Fri; 
    public static final WeekDay Sat; 
    public static final WeekDay Sun; 
    static {}; 
    public static void printDay(int); 
    public java.lang.String getDay(); 
    public static WeekDay[] values(); 
    public static WeekDay valueOf(java.lang.String); 
}

枚举单例

单例方式是大家在普通费用中可谓是最常用的设计方式.

下一场要规划好单例形式,无非酌量一下几点

  • 保证独有独一实例,十分的少创造多余实例
  • 保障实例按需创造.

因此古板的做法想要达成单例,大概有弹指间三种

  • 饿汉式加载
  • 懒汉式synchronize和再次检查
  • 利用java的静态加运载飞机制

对待上述的方法,使用枚举也能够完成单例,並且还尤其轻松.

public enum AppManager {
    INSTANCE;

    private String tagName;
    public void setTag(String tagName) {
        this.tagName = tagName;
    }

    public String getTag() {
        return tagName;
    }
}

调用起来也越来越简明

AppManager.INSTANCE.getTag();

用法一:常量

枚举怎么样保管独一实例

因为取得实例只可以通过AppManager.INSTANCE

上面包车型地铁方法是不能的

AppManager appManager = new AppManager(); //compile error

有关单例形式,能够翻阅单例这种设计方式了然越来越多。

在JDK1.5 在此以前,大家定义常量都以: public static fianl….
。未来好了,有了枚举,能够把相关的常量分组到叁个枚举类型里,况兼枚举提供了比常量越多的点子。

(Android中卡塔尔(قطر‎该不应当用枚举

既然下边提到了枚举会转变到类,这样辩白上形成了上面包车型大巴难点

  • 扩大了dex包的分寸,理论上dex包越大,加载速度越慢
  • 与此同期使用枚举,运营时的内部存储器占用也会相对变大

有关地点两点的证实,秋百万已经做了详实的实证,大家能够参见那篇小说《Android
中的 Enum
到底占多少内部存款和储蓄器?该怎么用?》

关于枚举是还是不是利用的定论,我们能够参照

  • 假如你付出的是Framework不提议利用enum
  • 倘如若归纳的enum,能够应用int很自在代替,则不建议采纳enum
  • 别的,借使是Android中,能够动用下边介绍的枚举注脚来促成。
  • 除了这么些之外,我们还供给相比可读性和易维护性来与质量实行衡量,从当中实行做出折中
public enum Color {  
  RED, GREEN, BLANK, YELLOW  
}

在Android中的代替

Android中新引进的代替枚举的疏解有IntDef和StringDef,这里以IntDef做例子说贝因美(BeingmateState of Qatar下.

public class Colors {
    @IntDef({RED, GREEN, YELLOW})
    @Retention(RetentionPolicy.SOURCE)
    public @interface LightColors{}

    public static final int RED = 0;
    public static final int GREEN = 1;
    public static final int YELLOW = 2;
}
  • 注明要求的int常量
  • 宣称叁个注脚为LightColors
  • 利用@IntDef修饰LightColors,参数设置为待枚举的聚众
  • 行使@Retention(RetentionPolicy.SOURCE卡塔尔钦点表明仅设有与源码中,不参加到class文件中

比如大家用来标记格局的参数

private void setColor(@Colors.LightColors int color) {
        Log.d("MainActivity", "setColor color=" + color);
}

调用的该措施的时候

setColor(Colors.GREEN);

关于Android中的枚举,能够参照商讨Android中的表明

上述便是自己对Java中enum的一对深深的深入分析,应接我们多多点拨。

用法二:switch

JDK1.6在此之前的switch语句只援救int,char,enum类型,使用枚举,能让大家的代码可读性越来越强。

enum Signal {
        GREEN, YELLOW, RED
    }

    public class TrafficLight {
        Signal color = Signal.RED;

        public void change() {
            switch (color) {
            case RED:
                color = Signal.GREEN;
                break;
            case YELLOW:
                color = Signal.RED;
                break;
            case GREEN:
                color = Signal.YELLOW;
                break;
            }
        }
    }

用法三:向枚举中加多新方式

万一筹划自定义自个儿的不二等秘书籍,那么必需在enum实例系列的最后增多一个根据地。何况Java 必要必需先定义 enum 实例。

public enum Color {
    RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
    // 成员变量
    private String name;
    private int index;

    // 构造方法
    private Color(String name, int index) {
        this.name = name;
        this.index = index;
    }

    // 普通方法
    public static String getName(int index) {
        for (Color c : Color.values()) {
        if (c.getIndex() == index) {
            return c.name;
        }
        }
        return null;
    }

    // get set 方法
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }
    }

用法四:覆盖枚举的主意

上面给出三个toString(State of Qatar方法覆盖的例证。

public class Test {
    public enum Color {
        RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
        // 成员变量
        private String name;
        private int index;

        // 构造方法
        private Color(String name, int index) {
            this.name = name;
            this.index = index;
        }

        // 覆盖方法
        @Override
        public String toString() {
            return this.index + "_" + this.name;
        }
    }

    public static void main(String[] args) {
        System.out.println(Color.RED.toString());
    }
}

用法五:完成接口

全部的枚举都卫冕自java.lang.Enum类。由于Java
不扶持多三番四回,所以枚举对象不能够再持续别的类。

public interface Behaviour {
    void print();

    String getInfo();
    }

    public enum Color implements Behaviour {
    RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
    // 成员变量
    private String name;
    private int index;

    // 构造方法
    private Color(String name, int index) {
        this.name = name;
        this.index = index;
    }

    // 接口方法

    @Override
    public String getInfo() {
        return this.name;
    }

    // 接口方法
    @Override
    public void print() {
        System.out.println(this.index + ":" + this.name);
    }
    }

用法六:使用接口组织枚举 

public interface Food {
        enum Coffee implements Food {
            BLACK_COFFEE, DECAF_COFFEE, LATTE, CAPPUCCINO
        }

        enum Dessert implements Food {
            FRUIT, CAKE, GELATO
        }
    }

用法七:关于枚举集结的利用

java.util.EnumSet和java.util.EnumMap是多个枚举集结。EnumSet保险集结中的成分不重复;EnumMap中的
key是enum类型,而value则能够是私下等级次序。关于那个四个汇集的应用就不在那赘述,

能够参谋JDK文档

三、 完整示例代码

枚举类型的完全演示代码如下:

public class LightTest {

    // 1.定义枚举类型

    public enum Light {

    // 利用构造函数传参

    RED(1), GREEN(3), YELLOW(2);

    // 定义私有变量

    private int nCode;

    // 构造函数,枚举类型只能为私有

    private Light(int _nCode) {

        this.nCode = _nCode;

    }

    @Override
    public String toString() {

        return String.valueOf(this.nCode);

    }

    }

    /**
     * 
     * @param args
     */

    public static void main(String[] args) {

    // 1.遍历枚举类型

    System.out.println("演示枚举类型的遍历 ......");

    testTraversalEnum();

    // 2.演示EnumMap对象的使用

    System.out.println("演示EnmuMap对象的使用和遍历.....");

    testEnumMap();

    // 3.演示EnmuSet的使用

    System.out.println("演示EnmuSet对象的使用和遍历.....");

    testEnumSet();

    }

    /**
     * 
     * 演示枚举类型的遍历
     */

    private static void testTraversalEnum() {

    Light[] allLight = Light.values();

    for (Light aLight : allLight) {

        System.out.println("当前灯name:" + aLight.name());

        System.out.println("当前灯ordinal:" + aLight.ordinal());

        System.out.println("当前灯:" + aLight);

    }

    }

    /**
     * 
     * 演示EnumMap的使用,EnumMap跟HashMap的使用差不多,只不过key要是枚举类型
     */

    private static void testEnumMap() {

    // 1.演示定义EnumMap对象,EnumMap对象的构造函数需要参数传入,默认是key的类的类型

    EnumMap<Light, String> currEnumMap = new EnumMap<Light, String>(

    Light.class);

    currEnumMap.put(Light.RED, "红灯");

    currEnumMap.put(Light.GREEN, "绿灯");

    currEnumMap.put(Light.YELLOW, "黄灯");

    // 2.遍历对象

    for (Light aLight : Light.values()) {

        System.out.println("[key=" + aLight.name() + ",value="

        + currEnumMap.get(aLight) + "]");

    }

    }

    /**
     * 
     * 演示EnumSet如何使用,EnumSet是一个抽象类,获取一个类型的枚举类型内容<BR/>
     * 
     * 可以使用allOf方法
     */

    private static void testEnumSet() {

    EnumSet<Light> currEnumSet = EnumSet.allOf(Light.class);

    for (Light aLightSetElement : currEnumSet) {

        System.out.println("当前EnumSet中数据为:" + aLightSetElement);

    }

    }

}

推行结果如下:

演示枚举类型的遍历 ......

当前灯name:RED

当前灯ordinal:0

当前灯:1

当前灯name:GREEN

当前灯ordinal:1

当前灯:3

当前灯name:YELLOW

当前灯ordinal:2

当前灯:2

演示EnmuMap对象的使用和遍历.....

[key=RED,value=红灯]

[key=GREEN,value=绿灯]

[key=YELLOW,value=黄灯]

演示EnmuSet对象的使用和遍历.....

当前EnumSet中数据为:1

当前EnumSet中数据为:3

当前EnumSet中数据为:2

 

  1. 装有的枚举类型都以Enum类的子类。
    它们继续了那些类的洋洋情势。个中最可行的三个方式是toString(卡塔尔,这些方法能够回到枚举常量名。
      toString(卡塔尔国方法的逆方法是静态方法valueOf(Class, StringState of Qatar. 比方 Light lt
    = (Light卡塔尔国 Enum.valueOf(Light.class, “RED”State of Qatar; 将lt设置为 Light.RED。
    每一种枚举类型都有二个静态的values(卡塔尔国方法,它将回来一个包蕴全体枚举值的数组。
      ordinal(State of Qatar方法重返enum注解中枚举常量的任务,地点从0起初计数。比如 Light.GREEN.ordinal(State of Qatar重返1。   Enum类实现了Comparable接口,  int
     compareTo( E other卡塔尔(قطر‎  即使枚举常量在other以前,则赶回叁个负值;
    如果this==other,则重回0;不然,重临正值。 枚举常量的现身程序在enum
    评释中付出。(所以无法一直用<,>符号相比较四个枚举值)

2. 方可成立二个enum类,把它看做三个经常的类。除了它无法继续别的类了。(java是单世袭,它早就持续了Enum卡塔尔,

能够增进其它方法,覆盖它本人的主意

  1. switch(卡塔尔(قطر‎参数能够应用enum了

4.
values(卡塔尔方法是编写翻译器插入到enum定义中的static方法,所以,当您将enum实例向上转型为父类Enum是,values(State of Qatar就不得访谈了。化解办法:在Class中有三个getEnumConstants(卡塔尔方法,所以固然Enum接口中并未有values(State of Qatar方法,我们依旧能够通过Class对象获得具有的enum实例

5. 不能够从enum世袭子类,要是急需扩大enum中的成分,在三个接口的此中,创立实现该接口的枚举,以此将成分进行分组。达到将枚举成分举办分组。

6. 接受EnumSet取代标记。enum要求其成员都是当世无双的,但是enum中没办法去除添新币素。

  1. EnumMap的key是enum,value是别的其余Object对象。

8.
enum允许技士为eunm实例编写方法。所以可感觉各样enum实例授予各自分化的行事。

9. 施用enum的职责链(Chain of ResponsibilityState of Qatar.那几个涉及到设计格局的义务链方式。以各类差异的主意来消释二个主题材料。然后将他们链接在联合。当几个呼吁到来时,遍历这些链,直到链中的某部实施方案能够管理该诉求。

10. 利用enum的状态机

11. 行使enum多路分发

发表评论

电子邮件地址不会被公开。 必填项已用*标注