枚举类型是Java 5中新增特性的一部分,它是一种特殊的数据类型,之所以特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性。

△有的地方还没有学的透彻,之后会继续学习修改更新本文章

1.枚举类学习

1.1 定义枚举类

  • 枚举类可以实现一个或多个接口,使用enum定义的枚举类默认继承了java.lang.Enum类,而不是默认继承 类,因此枚举类不能显示继承其他父类。其中java.lang.Enum类实现了java.lang.Serializable和java.lang.Comparable两个接口。
    使用enum定义、非抽象的枚举类默认会使用final修饰,因此枚举类不能派生子类。
  • 枚举类的构造器只能使用private访问控制符,如果省略了构造器的访问控制符,则默认使用private修饰;如果强制指定访问控制符,则只能指定private修饰符。
  • 枚举类的所有实例必须在枚举类的第一行显式列出,否则这个枚举类永远都不能产生实例。列出这些实例时,系统会自动添加public static final 修饰,无须程序员显式添加。
  • 枚举类默认提供了一个values()方法,该方法可以很方便地遍历所有的枚举值。

如下定义周一到周日的常量

//Day.class
//枚举类型,使用关键字enum
enum Day {
    MONDAY, TUESDAY, WEDNESDAY,
    THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

相当简洁,在定义枚举类型时我们使用的关键字是enum,与class关键字类似,只不过前者是定义枚举类型,后者是定义类类型。

1.2 枚举类的实现原理

我们大概了解了枚举类型的定义与简单使用后,现在有必要来了解一下枚举类型的基本实现原理。实际上在使用关键字enum创建枚举类型并编译后,编译器会为我们生成一个相关的类,这个类继承了Java API中的java.lang.Enum类,也就是说通过关键字enum创建枚举类型在编译后事实上也是一个类类型而且该类继承自java.lang.Enum类。

查看反编译Day.class文件:

//反编译Day.class
final class Day extends Enum
{
    //编译器为我们添加的静态的values()方法
    public static Day[] values()
    {
        return (Day[])$VALUES.clone();
    }
    //编译器为我们添加的静态的valueOf()方法,注意间接调用了Enum也类的valueOf方法
    public static Day valueOf(String s)
    {
        return (Day)Enum.valueOf(com/zejian/enumdemo/Day, s);
    }
    //私有构造函数
    private Day(String s, int i)
    {
        super(s, i);
    }
     //前面定义的7种枚举实例
    public static final Day MONDAY;
    public static final Day TUESDAY;
    public static final Day WEDNESDAY;
    public static final Day THURSDAY;
    public static final Day FRIDAY;
    public static final Day SATURDAY;
    public static final Day SUNDAY;
    private static final Day $VALUES[];

    static 
    {    
        //实例化枚举实例
        MONDAY = new Day(\"MONDAY\", 0);
        TUESDAY = new Day(\"TUESDAY\", 1);
        WEDNESDAY = new Day(\"WEDNESDAY\", 2);
        THURSDAY = new Day(\"THURSDAY\", 3);
        FRIDAY = new Day(\"FRIDAY\", 4);
        SATURDAY = new Day(\"SATURDAY\", 5);
        SUNDAY = new Day(\"SUNDAY\", 6);
        $VALUES = (new Day[] {
            MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
        });
    }
}
  • ①从反编译的代码可以看出编译器确实帮助我们生成了一个Day类(注意该类是final类型的,将无法被继承)而且该类继承自java.lang.Enum类,该类是一个抽象类(稍后我们会分析该类中的主要方法)。
  • ②除此之外,编译器还帮助我们生成了7个Day类型的实例对象分别对应枚举中定义的7个日期,这也充分说明了我们前面使用关键字enum定义的Day类型中的每种日期枚举常量也是实实在在的Day实例对象,只不过代表的内容不一样而已。注意编译器还为我们生成了两个静态方法,分别是values()和 valueOf()
  • ③到此我们也就明白了,使用关键字enum定义的枚举类型,在编译期后,也将转换成为一个实实在在的类,而在该类中,会存在每个在枚举类型中定义好变量的对应实例对象,如上述的MONDAY枚举类型对应public static final Day MONDAY;,同时编译器会为该类创建两个方法,分别是values()和valueOf()。到此相信我们对枚举的实现原理也比较清晰。下面我们深入了解一下java.lang.Enum类以及values()和valueOf()的用途。

1.3 枚举的常见方法

\"\"

2.枚举类使用

2.1常量

系统里实现常量的三种方式接口常量、类常量、枚举常量

2.1.1接口常量

如java的swing里有一个SwingConstant:

public interface SwingConstants {

        /** 
         * The central position in an area. Used for
         * both compass-direction constants (NORTH, etc.)
         * and box-orientation constants (TOP, etc.).
         */
        public static final int CENTER  = 0;

        // 
        // Box-orientation constant used to specify locations in a box.
        //
        /** 
         * Box-orientation constant used to specify the top of a box.
         */
        public static final int TOP     = 1;
        /** 
         * Box-orientation constant used to specify the left side of a box.
         */
        public static final int LEFT    = 2;
       
       //。。。省略其他代码
   }

2.1.2类常量

写法(1)利弊:用到 DefaultValues.DEFAULT_AP 的含义,必须看类里的注释,知道他表示中心。如果常量很多的话,把所有的常量都放在这一个接口里边,这种方式感觉也不是很友好。

/**
 * 系统默认值
 *
 */
public class DefaultValues {

	/**
	 * 默认密码
	 */
	public static final String DEFAULT_PASSWORD = \"000000\";
	/**
	 * 默认用户类型
	 */
	public static final String DEFAULT_USER_TYPE = UserType.NormalUser.value();	
	/**
	 * 默认获取api名称
	 */
	public static final String DEFAULT_API = \"api\";
	
	/**
	 * 默认系统字符编码
	 */
	public static final String DEFAULT_ENCODING = \"UTF-8\";
	
	/**集群规模*/
	public static final  long CLUSTER_SIZE = 1000;
}

写法(2)利弊:公司的接口常量是在接口里定义静态内部类,他可以把不同的功能的常量类进一步分类。把不同功能的常量放在了接口的内部类里,通过不同的内部类可以清楚的知道一个常量的含义。

public class Constants {
	public static class MimeType{
		public static final String BIN = \"application/octet-stream\";
		public static final String CSS = \"text/css\";
		public static final String DOC = \"application/msword\";
		public static final String DOCX = \"\";
		public static final String EXE = \"application/octet-stream\";
		public static final String GTAR = \"application/x-gtar\";
		public static final String GZ = \"application/x-gzip\";
		public static final String HTM = \"text/html;charset=utf-8\";
		public static final String ICO = \"image/x-icon\";
		public static final String JPEG = \"image/jpeg\";
		public static final String JPG = \"image/jpeg\";
		public static final String JS = \"application/x- ;charset=utf-8\";
		public static final String JSON = \"application/json;charset=utf-8\";
		public static final String FORM = \"application/x-www-form-urlencoded; charset=UTF-8\";
		public static final String MULTIPART = \"multipart/form-data; charset=UTF-8\";
		public static final String MHT = \"message/rfc822\";
		public static final String MHTML = \"message/rfc822\";
		public static final String MOV = \"video/quicktime\";
		public static final String MP3 = \"audio/mpeg\";
		public static final String MPE = \"video/mpeg\";
		public static final String MPEG = \"video/mpeg\";
		public static final String MPG = \"video/mpeg\";
		public static final String PDF = \"application/pdf\";
		public static final String PPT = \"application/vnd.ms-powerpoint\";
		public static final String RTF = \"application/rtf\";
		public static final String SWF = \"application/x-shockwave-flash\";
		public static final String TAR = \"application/x-tar\";
		public static final String TXT = \"text/plain;charset=utf-8\";
		public static final String WAV = \"audio/x-wav\";
		public static final String   = \"text/ ;charset=utf-8\";
		public static final String ZIP = \"application/zip\";
		
	}
	
	public static class DataState{
		public static final String FLAG_REMOVE = \"Y\";
		public static final String FLAG_NORMAL = \"N\";
	}
	
	/**
	 * 应用服务器实例运行状态
	 */
	public static class ServerASInstanceState{
		public static final int RUNNING = 1;
		public static final int SHUT_OFF = 2;
	}
	/**
	 * WebServices接口分析
	 */
	public static class WebServicesType{
		/**先接收数据,在返回接口情况的接口 **/ 
		public static final String IN_OUT = \"IO\";
		/**先发数据请求,后返回数据的接口 **/ 
		public static final String OUT_IN = \"OI\";
		/**只发送数据的接口**/ 
		public static final String OUT= \"O\";
		/**只接收数据的接口 **/ 
		public static final String IN = \"I\";
	}
	
	/**
	 * 任务调度使用
	 */
	public static class TaskScheduling{
		/**任务ID **/ 
		public static final String TASK_ID = \"taskID\";
		/**任务URL **/ 
		public static final String TASK_URI = \"taskURI\";
		/**任务URL **/ 
		public static final String TASK_NAME = \"taskName\";
		/**任务目标服务器IP **/ 
		public static final String TASK_SERVER_IP = \"taskServerIp\";
		/**任务目标服务器IP **/ 
		public static final String TASK_SERVER_PORT = \"taskServerPort\";
		
		/**任务状态启用**/
		public static final int TASK_ENABLED = 1;
		
		/**任务状态禁用**/
		public static final int TASK_DISABLE = 0;
		
		/**每年任务**/
		public static final int TYPE_EVERY_YEAR= 1;
		
		/**每月任务**/
		public static final int TYPE_EVERY_MONTH = 2;
		
		/**每日任务**/
		public static final int TYPE_EVERY_DAY = 3;
		
		/**每周任务**/
		public static final int TYPE_EVERY_WEEK = 4;
		
		/**单次任务**/
		public static final int TYPE_SINGLE = 5;
		
	}
}

虽然有了枚举,可能是由于设计者习惯问题,还有很多人用的类常量, 定义了类常量,用一个Map<Integer, String>来封装常量对应的信息,在static代码块里,类初始化的时候执行一次put。用的时候 ResponseCode.RESP_INFO.get(\"DATA _EXCEPTION\");就能取出响应信息 由于项目是前后端分离,在接口文档里需要写上状态码,还得写上状态码对应的提示信息,而且我们的响应类 RespInfo 有message属性,就是保存常量类里状态码对应的信息的。

public class ResponseCode {

    /** 系统处理正常 */
    public static final int SUCCESS_HEAD = 0;

    /** 系统处理未知异常 */
    public static final int EXCEPTION_HEAD = 1;

    /** JSON解析错误 */
    public static final int JSON_RESOLVE = 2;

    /** 类型不匹配 */
    public static final int TRANSTYPE_NO = 3;

    /** Head - messageID未赋值 */
    public static final int HEAD_messageID = 4;

    /** Head - timeStamp未赋值 */
    public static final int HEAD_timeStamp = 5;

    /** Head - messengerID未赋值 */
    public static final int HEAD_messengerID = 6;

    /** Head - transactionType 未赋值 */
    public static final int HEAD_transactionType = 7;

    /** digest校验不通过 */
    public static final int HEAD_DIGEST = 8;
    
    /** src校验不通过 */
    public static final int HEAD_SRC_NULL = 10;
    
    /** 协议包含非法字符 */
    public static final int ILLEGAL_MESSAGE = 11;

    /** 数据库异常 */
    public static final int DATA _EXCEPTION = 9;
    public static final Map<Integer, String> RESP_INFO = new HashMap<Integer, String>();

    static {
        // Head 相关
        RESP_INFO.put(SUCCESS_HEAD, \"系统处理正常\");
        RESP_INFO.put(EXCEPTION_HEAD, \"系统处理未知异常\");
        RESP_INFO.put(JSON_RESOLVE, \"JSON解析错误\");
        RESP_INFO.put(TRANSTYPE_NO, \"类型不匹配\");
        RESP_INFO.put(HEAD_messageID, \"messageID未赋值\");
        RESP_INFO.put(HEAD_timeStamp, \"timeStamp未赋值\");
        RESP_INFO.put(HEAD_messengerID, \"messengerID未赋值\");
        RESP_INFO.put(HEAD_transactionType, \"transactionType未赋值\");
        RESP_INFO.put(HEAD_DIGEST, \"digest校验不通过\");
        RESP_INFO.put(DATA _EXCEPTION, \"数据库异常\");
        RESP_INFO.put(HEAD_SRC_NULL, \"src未赋值\");
        RESP_INFO.put(ILLEGAL_MESSAGE, \"协议包含非法字符\");
        
    }
}

2.1.3枚举常量

所有的枚举类都是Enum类的子类,就行 类一样,只是没有写出来,所以可以枚举类可调用Enum的方法。注意是逗号分隔属性,只有属性后边没有方法的话,最后加不加分号都行。

写法(1)

public enum StateType {
	/**
	 * 成功返回状态
	 */
	OK(200,\"OK\"), 
	
	/**
	 * 请求格式错误
	 */
	BAD_REQUEST(400,\"bad request\"),
	/**
	 * 未授权
	 */
	UNAUTHORIZED(401,\"unauthorized\"),
	/**
	 * 没有权限
	 */
	FORBIDDEN(403,\"forbidden\"),
	
	/**
	 * 请求的资源不存在
	 */
	NOT_FOUND(404,\"not found\"),
	/**
	 * 该http方法不被允许
	 */
	NOT_ALLOWED(405,\"method not allowed\"),
	/**
	 * 请求处理发送异常
	 */
	PROCESSING_EXCEPTION(406,\"Handling Exceptions\"),
	/**
	 * 
	 * 请求处理未完成
	 */
	PROCESSING_UNFINISHED(407,\"To deal with unfinished\"),
	
	/**
	 * 登录过期
	 */
	BEOVERDUE(408,\"Be overdue\"),
	
	/**
	 * 用户未登录
	 */
	NOT_LOGIN(409,\"Not logged in\"),
	
	/**
	 * 这个url对应的资源现在不可用
	 */
	GONE(410,\"gone\"),
	/**
	 * 请求类型错误
	 */
	UNSUPPORTED_MEDIA_TYPE(415,\"unsupported media type\"),
	/**
	 * 校验错误时用
	 */
	UNPROCESSABLE_ENTITY(422,\"unprocessable entity\"),
	/**
	 * 请求过多
	 */
	TOO_MANY_REQUEST(429,\"too many request\");

	private int code;
	private String value = null;

	private StateType(int code,String value) {
		this.code = code;
		this.value = value;
	}

	public String value() {
		return this.value;
	}

	public int getCode() {
		return code;
	}

	public static Boolean isValidateStateType(String... stateType) {
		for (int i = 0; i < stateType.length; i++) {
			StateType [] value = StateType.values();
			boolean falg = false;
			for(StateType type : value) {
				if(type.value.equals(stateType[i])) {
					falg = true;
				}
				
			}
			if(!falg) {
				return falg;
			}
		}
		return true;
	}
}

/*使用*/
public static void main(String[] args) {
	System.out.println(\"状态码:\"+StateType.getCode());
        System.out.println(\"错误信息:\"+StateType.getValue());
}

写法(2)

public enum Level {
	/**
	 * 第一层
	 */
	One(1),
	/**
	 * 第二层 
	 */
	Two(2),
	/**
	 * 第三层
	 */
	Three(3),
	/**
	 * 第四层
	 */
	Four(4),
	/**
	 * 第五层
	 */
	Five(5);
	
	private int value;
	
	Level(int value) {
		this.value = value;
	}
	
	public int value() {
		return this.value;
	}
	
	public static Boolean isValidateLevel(int level) {
		Level [] value = Level.values();
		boolean falg = false;
		for (Level pl : value){
			if(pl.value == level){
				falg = true;
			}
		}
		return falg;
	}
}

/*使用*/
public static void main(String[] args) {
	System.out.println(\"楼层:\"+Level.Three);
}

2.2 switch结合枚举类

JDK1.6之前的switch语句只支持int,char,enum类型,使用枚举,能让我们的代码可读性更强。

枚举是声明一组命名的常数,当一个变量有几种可能的取值时,可以将它定义为枚举类型。枚举是将变量的值一一列出来,变量的值只局限于列举出来的值的范围内。

△注意:枚举只是枚举类型,不能够赋值操作。如下:GREEN默认值为0,但是GREEN不能=0,因为数据类型不一样。枚举中变量未直接赋值,默认等于前一个变量值加一,起始值默认为0。

enum Signal { 
 GREEN, YELLOW, RED 
} 
public class TrafficLight { 
 Signal color = Signal.RED; 
 public void change() { 
  					
收藏 打印