文档章节

Java枚举用法整理

王孟君
 王孟君
发布于 2017/01/18 16:27
字数 1909
阅读 1490
收藏 3

在项目中,一般推荐使用枚举来代替常量接口和常量类。

但是,枚举类的用途不仅仅是定义常量,还有其它较多的方法,比如:实现接口定义抽象方法当作单例使用等。

本文是对Java枚举使用方法的一个整理,并给出示例。

在JDK 1.5之前,定义常量,我们一般使用两种方式:

  • 定义一个常量类
  • 定义一个常量接口

定义常量类,如:

/**
 * @author wangmengjun
 */
public class SeasonConstants {

    public static final int SPRING = 0;
    public static final int SUMMER = 1;
    public static final int FALL = 2;
    public static final int WINTER = 3;
}

定义常量接口,如:

/**
 * @author wangmengjun
 */
public interface SeasonInterface {

    public static final int SPRING = 0;
    public static final int SUMMER = 1;
    public static final int FALL = 2;
    public static final int WINTER = 3;
}

这样一来,凡是实现SeasonInterface接口的类都会自动继承这些常量

如:

public class Main implements SeasonInterface {

    public static void main(String[] args) {
        int season = SPRING;
    }
}

Java枚举类在JDK 1.5引入的,枚举类在项目中已经不可或缺。

正是因为Java枚举类可以有自定义的方法,可以实现接口、定义抽象类等,更加的灵活,已经被广大开发人员推荐在项目中使用 -- 使用枚举类替换接口常量或者类常量等。

接下来,我们一起来看一些使用枚举的示例:

Java枚举类使用

声明枚举的语法如下:

[public/protected/private] enum Enum_name {

... ...
}

无构造函数的枚举

我们可以定义一个Season的枚举,包含四个季节,如:

/**
 * @author wangmengjun
 */
public enum Season {

    SPRING,
    SUMMER,
    FALL,
    WINTER
}

每个枚举类的常量是 public、static、final修饰的。

可以结合switch来使用,如:

public class Main {

    public static void main(String[] args) {
        Season season = Season.SPRING;
        switch (season) {
            case SPRING:
                System.out.println("Spring ~~~");
                break;
            case SUMMER:
                System.out.println("Summer ~~~");
                break;
            case FALL:
                System.out.println("Fall ~~~");
                break;
            case WINTER:
                System.out.println("Winter ~~~");
                break;
            default:
                break;
        }
    }
}

有构造函数的枚举

构造函数中的参数可以一个或者多个。

  • 一个参数的构造函数示例

比如,美国硬币的种类,可以添加一个币值作为参数,如:

public enum Coin {

    PENNY(1),
    NICKEL(5),
    DIME(10),
    QUARTER(25); //US coins

    private int value;

    private Coin(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}

使用示例:

int coidValue = Coin.NICKEL.getValue(); //  5
  • 多个参数的构造函数示例:

有时候,我们需要不止一个参数,如一个产品类型的枚举类,可以包含2个参数,一个int值,一个String值,分别用于存入数据库的值和页面显示的值。

如:

public enum ProductType {

    SMART_HOME(0, "智能家居"),
    HEALTH_CARE(1, "医疗健康"),
    MOTION_DETECTION(2, "运动检测"),
    INDUSTRIAL_PRODUCTION(3, "工业生产"),
    ENVIRONMENT_MONITORING(4, "环境监测"),
    INTELLIGENT_OFFICE(6, "智能办公"),
    LOCATION_DEVICE(7, "定位器/防丢器"),
    SMART_GATEWAY(8, "智能网关"),
    OTHERS(5, "其它");

    private int code;

    private String name;

    private ProductType(int code, String name) {
        this.code = code;
        this.name = name;
    }

    /**
     * @return the code
     */
    public int getCode() {
        return code;
    }

    /**
     * @return the name
     */
    public String getName() {
        return name;
    }


}

这种情况,比较适合页面上下拉框选项的场景,如使用JSTL来循环一下传给页面的枚举类集:

<select class="form-control" id="J_pro_type" name="productType"
		data-change="click">
		<option value="">请选择产品类型</option>
	    <c:forEach items="${types}" var="type">
		   <option value="${type.code }"> ${type.name}</option>
		</c:forEach>

</select>

下拉框效果图如下:

获取枚举类中所有常量

可以通过枚举类的values() 方法获取指定枚举的常量数组,如


        Coin[] coins = Coin.values();

        for (Coin coin : coins) {
            System.out.println(String.format("%s --> value is %d", coin.name(), coin.getValue()));
        }

输出结果:

PENNY --> value is 1
NICKEL --> value is 5
DIME --> value is 10
QUARTER --> value is 25

包含自定义方法

我们可以在Coin枚举类中,添加一个自定的方法,比

    public boolean isPenny() {
        return this == PENNY;
    }

修改后的Coin.java内容如下:

public enum Coin {

    PENNY(1),
    NICKEL(5),
    DIME(10),
    QUARTER(25); //US coins

    private int value;

    private Coin(int value) {
        this.value = value;
    }

    public boolean isPenny() {
        return this == PENNY;
    }

    public int getValue() {
        return value;
    }

}

这样,我们就能通过isPenny方法知道该Coin对象是否为PENNY。

       Coin penny = Coin.PENNY;
       System.out.println(penny.isPenny());//true
       
       Coin dime = Coin.DIME;
       System.out.println(dime.isPenny());//false

实现接口

定义一个接口Next,包含nextSeason方法,用于输出下一个季节是什么。

public interface Next {
    void nextSeason();
}

修改Season枚举内容:

/**
 * @author wangmengjun
 */
public enum Season implements Next {

    SPRING,
    SUMMER,
    FALL,
    WINTER;

    @Override
    public void nextSeason() {
        switch (this) {
            case SPRING:
                System.out.println("Next season is Summer");
                break;
            case SUMMER:
                System.out.println("Next season is Fall");
                break;
            case FALL:
                System.out.println("Next season is Winter");
                break;
            case WINTER:
                System.out.println("Next season is Spring");
                break;
            default:
                break;
        }

    }
}

测试一下:

      Season spring = Season.SPRING;
      spring.nextSeason();//输出Next season is Summer

包含抽象方法

除了可以实现接口外,枚举类还可以包含抽象方法

/**
 * @author wangmengjun
 */
public enum RegularExpressionEnum {

    NUMERIC("^[0-9]+$") {
        @Override
        public boolean match(String value) {
            return Pattern.matches(this.getRegPattern(), value);
        }
    },

    ALPHABETIC("^[a-zA-Z]+$") {
        @Override
        public boolean match(String value) {
            return Pattern.matches(this.getRegPattern(), value);
        }
    };

    private String regPattern;

    private RegularExpressionEnum(String regPattern) {
        this.regPattern = regPattern;
    }

    /**
     * @return the regPattern
     */
    public String getRegPattern() {
        return regPattern;
    }

    public abstract boolean match(String value);

}

示例:

        RegularExpressionEnum numeric = RegularExpressionEnum.NUMERIC;
        System.out.println(numeric.match("123"));//true
        System.out.println(numeric.match("1a2"));//false

        RegularExpressionEnum alphabetic = RegularExpressionEnum.ALPHABETIC;
        System.out.println(alphabetic.match("123"));//false
        System.out.println(alphabetic.match("abc"));//true

如何选择,是选择实现接口或者使用抽象方法呢?

如果一个方法,每个枚举常量的方法实现都是一样的那么最好使用接口,不用抽象方法。实现接口,只要在枚举中实现一个接口方法即可;使用抽象方法,每个枚举中的常量都需要实现一遍抽象方法。

如果每个常量的行为各异,变化大,那么使用抽象方法来做,较为合适。

比如,将上述抽象方法的例子,改成实现接口的方式,一起来看一下代码的变化。

public interface RegularExpressionInterface {

    boolean match(String value);
}

RegularExpressionEnum修改如下:

/**
 * @author wangmengjun
 */
public enum RegularExpressionEnum implements RegularExpressionInterface {

    NUMERIC("^[0-9]+$"),
    ALPHABETIC("^[a-zA-Z]+$");

    private String regPattern;

    private RegularExpressionEnum(String regPattern) {
        this.regPattern = regPattern;
    }

    /**
     * @return the regPattern
     */
    public String getRegPattern() {
        return regPattern;
    }

    @Override
    public boolean match(String value) {
        return Pattern.matches(this.getRegPattern(), value);
    }
}

测试代码和结果和上述抽象方法的一样。

        RegularExpressionEnum numeric = RegularExpressionEnum.NUMERIC;
        System.out.println(numeric.match("123"));//true
        System.out.println(numeric.match("1a2"));//false

        RegularExpressionEnum alphabetic = RegularExpressionEnum.ALPHABETIC;
        System.out.println(alphabetic.match("123"));//false
        System.out.println(alphabetic.match("abc"));//true

作为单例使用

public enum Attendant {

    INSTANCE;

    private Attendant() {
        // perform some initialization routine
    }

    public void sayHello() {
        System.out.println("Hello!");
    }
}


public class Main {

    public static void main(String[] args) {
        Attendant.INSTANCE.sayHello();// instantiated at this point
    }
}

包含静态变量和静态方法

以上面说过的产品类型(ProductType)枚举为例,int值存在数据库中,但是,需要在页面上根据int值显示对应的产品详细类型,这个时候我们可以在枚举类中添加一个Map, 然后添加一个静态方法getNameByCode,来实现,如:

    private static final Map<Integer, ProductType> MAP = new HashMap<>();

    static {
        for (ProductType type : ProductType.values()) {
            MAP.put(type.getCode(), type);
        }
    }

 

    public static String getTypeNameByCode(int code) {
        if (MAP.containsKey(code)) {
            return MAP.get(code).getName();
        }
        return "";
    }

完整的ProductType枚举内容如下:

import java.util.HashMap;
import java.util.Map;

public enum ProductType {

    SMART_HOME(0, "智能家居"),
    HEALTH_CARE(1, "医疗健康"),
    MOTION_DETECTION(2, "运动检测"),
    INDUSTRIAL_PRODUCTION(3, "工业生产"),
    ENVIRONMENT_MONITORING(4, "环境监测"),
    INTELLIGENT_OFFICE(6, "智能办公"),
    LOCATION_DEVICE(7, "定位器/防丢器"),
    SMART_GATEWAY(8, "智能网关"),
    OTHERS(5, "其它");

    private int code;

    private String name;

    private static final Map<Integer, ProductType> MAP = new HashMap<>();

    static {
        for (ProductType type : ProductType.values()) {
            MAP.put(type.getCode(), type);
        }
    }

    private ProductType(int code, String name) {
        this.code = code;
        this.name = name;
    }


    /**
     * 
     * @param code
     * @return
     */
    public static String getTypeNameByCode(int code) {
        if (MAP.containsKey(code)) {
            return MAP.get(code).getName();
        }
        return "";
    }

    /**
     * @return the code
     */
    public int getCode() {
        return code;
    }


    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

}

这样一来,如果获取一个产品对象,其type为1,那么直接可以通过1获取对应的产品类型名称为医疗健康。可以设置到VO中,用于展示。

        String name = ProductType.getTypeNameByCode(1);
		System.out.println(name);//医疗健康

大家如果对枚举类的使用,还有其它使用方法也请留言给出,大家一起学习分享~~ :)

© 著作权归作者所有

王孟君

王孟君

粉丝 227
博文 94
码字总数 221044
作品 0
杭州
高级程序员
私信 提问
Java 枚举(enum) 详解7种常见的用法

版权声明:觉得此文有用的,不嫌麻烦的,就留个言呐,或者点个赞呐(额,就是文章底部的“顶”啦),要是嫌弃麻烦呢,也麻烦点个赞嘛,要是实在不想点赞呢,也不是不可以。 但是,你要是想踩一...

李学凯
2016/08/11
0
0
java中枚举(enum)常见的7种用法

在JDK1.5之前,JAVA可以有两种方式定义新类型:类和接口,对于大部分面向对象编程,有这两种似乎就足够了,但是在一些特殊情况就不合适。例如:想要定义一个Color类,它只能有Red,Green,Blu...

淡日临窗
2017/05/24
0
0
大概优秀的java程序员都要会分析class文件吧

相信大家在学java的时候都会听到这样的一些结论: enum 是一个类 泛型的实现使用了类型擦除技术 非静态内部类持有外部类的引用 需要将自由变量声明成final才能给匿名内部类访问 ... 初学的时候...

嘉伟咯
03/22
0
0
为什么我墙裂建议大家使用枚举来实现单例。

关于单例模式,我的博客中有很多文章介绍过。作为23种设计模式中最为常用的设计模式,单例模式并没有想象的那么简单。因为在设计单例的时候要考虑很多问题,比如线程安全问题、序列化对单例的...

2018/06/10
0
0
一份关于 Java、Kotlin 与 Android 的学习笔记

JavaKotlinAndroidLearn 这是一份关于 Java 、Kotlin 、Android 的学习笔记,既包含对基础知识点的介绍,也包含对一些重要知识点的源码解析,笔记的大纲如下所示: Java 重拾Java(0)-基础知...

叶应是叶
2018/08/08
0
0

没有更多内容

加载失败,请刷新页面

加载更多

通过四道常问面试题,带你了解什么是数据库分库分表

编者语:为了避免被误解为:「手里有把锤子,看什么都是钉子!」,说明一下不是什么业务都适合分布式数据库,更不是用了分布式数据库性能就一定能得到扩展。 其次:本文为纯干货,建议先转发...

老道士
38分钟前
2
0
springmvc 整体流程

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR......

architect刘源源
46分钟前
2
0
磁盘管理

先来看两个查看的命令 查看磁盘使用情况df 用法:df, df -h, df -m, df -k 查看目录或文件大小 用法:du -sh, du -sm, du -s(默认以k为单位) 新加一块盘如何操作 步骤:分区(可选)--> 格...

wzb88
55分钟前
2
0
在 Linux 下确认 NTP 是否同步的方法

NTP 意即网络时间协议Network Time Protocol,它通过网络同步计算机系统之间的时钟。NTP 服务器可以使组织中的所有服务器保持同步,以准确时间执行基于时间的作业。NTP 客户端会将其时钟与 ...

Linux就该这么学
今天
8
0
《剖析Java线程到底是并行还是并发》

我们都知道线程是最基本的执行单元,包含在进程内部,也就是说,进程是由线程构成。那么当我们编写多线程的时候,大部分教科书上都是称其为并发,而非是并行,这2种截然不同的概念,完全误导...

hiuh
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部