Java基础:枚举Enum

在某些情况下,一个类的对象是有限而且固定的,比如季节类,它只有4个对象:春、夏、秋、冬。这种实例有限而且固定的类,在Java里被称为枚举类。

1 手动实现枚举类

示例:

package com.demo2;

public class Season {

	public static final Season SPRING = new Season("春天", "趁春踏青");
	public static final Season SUMMER = new Season("夏天", "夏日炎炎");
	public static final Season FALL = new Season("秋天", "秋高气爽");
	public static final Season WINTER = new Season("冬天", "围炉观雪");

	public static Season getSeason(int seasonNum) {

		switch (seasonNum) {
		case 1:
			return SPRING;
		case 2:
			return SUMMER;
		case 3:
			return FALL;
		case 4:
			return WINTER;
		default:
			return null;
		}

	}

	private final String name;
	private final String desc;

	private Season(String name, String desc) {
		this.name = name;
		this.desc = desc;
	}

	public String getName() {
		return name;
	}

	public String getDesc() {
		return desc;
	}
}
package com.demo2;

public class Test2 {
	public Test2(Season s) {
		System.out.println(s.getName() + "-----" + s.getDesc());
	}

	public static void main(String[] args) {
		new Test2(Season.SPRING);
	}
}

如果需要手动实现枚举,可以采用如下设计方式:

  • 通过private把构造器隐藏起来。
  • 把这个类的所有实例都使用public static final修饰的类变量来保存。
  • 如果有必要,提供一些静态方法,允许其他程序根据特定参数来获取与之匹配的实例。

PS:有些程序员喜欢使用简单地定义几个静态常量来表示枚举类。

例如:

public static final int SEASON_SPRING=1;
public static final int SEASON_SUMMER=2;
public static final int SEASON_FALL=3;
public static final int SEASON_WINTER=4;

这种方式存在几个问题:

  • 类型不安全。例子中都是整型,所以季节可以进行+,-,*,/等int运算。
  • 没有命名空间。这些静态常量只能靠前缀“SEASON_”来划分所属类型。
  • 打印输出,意义不明确。如果使用System.out.println()打印SEASON_SPRING,输出是1,不是SPRING。

2 枚举类Enum

JDK 5新增了一个关键字Enum,它与class,interface的地位相同,用来定义枚举类。枚举类其实是一个特殊的类,它可以有自己的Field,方法,构造函数,可以实现一个或多个接口。

2.1 定义枚举类

示例:

package com.demo2;

public enum SeasonEnum{
	SPRING, SUMMER, FALL, WINTER;
}

编译该段代码,将生成一个SeasonEnum.class文件,说明枚举类是一种特殊的类。

定义枚举类时,需要显式地列出所有的枚举值,例如上面的SPRING, SUMMER, FALL, WINTER;所示,所有的枚举值之间以英文逗号(,)隔开,枚举值列举结束后,以英文分号作为结束。这些枚举值代表了该枚举类的所有可能实例。

2.2 枚举类本质

将2.1中生成的SeasonEnum.class文件反编译:

javap -c SeasonEnum.class > SeasonEnum.txt

结果如下:

Compiled from "SeasonEnum.java"
public final class com.demo2.SeasonEnum extends java.lang.Enum<com.demo2.SeasonEnum> {
  public static final com.demo2.SeasonEnum SPRING;

  public static final com.demo2.SeasonEnum SUMMER;

  public static final com.demo2.SeasonEnum FALL;

  public static final com.demo2.SeasonEnum WINTER;

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

  public static com.demo2.SeasonEnum[] values();
    Code:
       0: getstatic     #31    // Field ENUM$VALUES:[Lcom/demo2/SeasonEnum;
       3: dup           
       4: astore_0      
       5: iconst_0      
       6: aload_0       
       7: arraylength   
       8: dup           
       9: istore_1      
      10: anewarray     #1     // class com/demo2/SeasonEnum
      13: dup           
      14: astore_2      
      15: iconst_0      
      16: iload_1       
      17: invokestatic  #39    // Method java/lang/System.arraycopy:(Ljava/lang/Object;ILjava/lang/Object;II)V
      20: aload_2       
      21: areturn       

  public static com.demo2.SeasonEnum valueOf(java.lang.String);
    Code:
       0: ldc           #1    // class com/demo2/SeasonEnum
       2: aload_0       
       3: invokestatic  #47   // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
       6: checkcast     #1    // class com/demo2/SeasonEnum
       9: areturn       
}

java.lang.Enum的源码结构如下:

根据字节码和java.lang.Enum源码结构可以得出如下结论:

  • 使用enum定义的枚举类默认直接继承了java.lang.Enum类,而不是直接继承java.lang.Object。其中java.lang.Enum实现了java.lang.Comparable<E>, java.lang.Serializable两个接口。
  • 使用enum定义、非抽象的枚举类默认会使用final修饰,因此非抽象枚举类不能派生子类。
  • 枚举类的构造函数只能使用private访问控制符。如果省略了构造函数的访问控制符,默认会使用private修饰;如果强制指定访问控制符,只能使用private。
  • 枚举类的所有实例必须在枚举类的第一行显示列出,否则这个枚举类永远不都不能产生实例。列出这些实例时,系统会自动添加public statci final修饰符,无须程序员显示添加。

Java实现枚举的本质:

  • java.lang.Enum定义枚举对象的组成成分。一个枚举对象包含两个属性“ordinal”和“name”,一系列实例方法,例如toString、compareTo等等。“ordinal”是索引值,根据枚举声明的位置赋值,从0开始。“name”是枚举对象的名称。例如public enum SeasonEnum{SPRING, SUMMER, FALL, WINTER;}中,SPRING的索引值为0,name为“SPRING”;SUMMER的索引值为1,name为“SUMMER”.........。
  • 编译时,编译器(javac)在自定义枚举类的.class文件中,添加static{}初始化块,初始化块包括生成枚举对象的指令。在示例字节码中,static{}初始化块里依次生成SPRING枚举对象并赋值索引值0,name为“SPRING”,生成SUMMER枚举对象并赋值索引值1,name为“SUMMER”..........最后,在SeasonEnum类中还定义了一个静态成员“ENUM$VALUES”,它是一个SeasonEnum数组,依据索引对象的“ordinal”值按顺序存放索引对象。

2.3 枚举类与switch结构

如果需要使用枚举类的某个实例,可以使用“枚举类.某个实例”的形式,例如SeasonEnum.SPRING。

示例:

package com.demo2;

public class Test {

	public static void judge(SeasonEnum s) {

		switch (s) {
		case SPRING:
			System.out.println("春天,趁春踏青");
			break;
		case SUMMER:
			System.out.println("夏天,夏日炎炎");
			break;
		case FALL:
			System.out.println("秋天,秋高气爽");
			break;
		case WINTER:
			System.out.println("冬天,围炉观雪");
			break;
		}

	}

	public static void main(String[] args) {
		judge(SeasonEnum.FALL);
	}
}

上面程序中的switch表达式中,使用了SeasonEnum对象作为表达式,这是JDK 5增加枚举后对switch的扩展:switch的控制表达式可以是任何枚举类型。不仅如此,当switch控制表达式使用枚举类型时,后面case表达式中的值直接使用枚举值名字,无须添加枚举类作为限定。

3 枚举类与构造函数

4 枚举类与接口

5 枚举类与抽象方法

待续

时间: 2024-10-09 17:24:09

Java基础:枚举Enum的相关文章

JAVA基础学习-enum枚举

枚举enum是一种特殊的类(还是类),使用枚举可以很方便的定义常量比如设计一个枚举类型 季节,里面有4种常量 public enum Season {     SPRING,SUMMER,AUTUMN,WINTER } 使用枚举的好处:假设在使用switch的时候,不是使用枚举,而是使用int,而int的取值范围就不只是1-4,有可能取一个超出1-4之间的值,这样判断结果就似是而非了.(因为只有4个季节),但是使用枚举,就能把范围死死的限定在这四个当中 SPRING,SUMMER,AUTUMN,

[Java 基础]枚举

概念 枚举(enum)是一个被命名的整型常量的集合. enum相对于普通常量最明显的好处在于更有组织性. 尽管enum看起来像是一种新的数据类型,事实上,enum是一种受限制的类,并且具有自己的方法. 创建enum时,编译器会为你生成一个相关的类,这个类继承自java.lang.Enum. java.lang.Enum类声明: public abstract class Enum<E extends Enum<E>> implements Comparable<E>,

C++和Java中枚举enum的用法

在C++和java中都有枚举enum这个关键字,但是它们之间又不太一样.对于C++来说,枚举是一系列命名了的整型常量,而且从枚举值转化为对应的整型值是在内部进行的.而对于Java来说,枚举更像一个类的命名的实例,你可以自定义枚举的成员,枚举值转化为对应的整型值是再外部进行的.下面以我之前的一篇博客8.1 Implement Blackjack 实现21点纸牌来说明,里面有一个扑克牌花色的枚举类: // C++ defination enum Suit {Club, Diamond, Heart,

关于Java中枚举Enum的深入剖析

在编程语言中我们,都会接触到枚举类型,通常我们进行有穷的列举来实现一些限定.Java也不例外.Java中的枚举类型为Enum,本文将对枚举进行一些比较深入的剖析. 什么是Enum Enum是自Java 5 引入的特性,用来方便Java开发者实现枚举应用.一个简单的Enum使用如下. // ColorEnum.javapublic enum ColorEmun { RED, GREEN, YELLOW} public void setColorEnum(ColorEmun colorEnum) {

聊一聊Java的枚举enum

一. 什么是枚举 枚举是一种数据类型,具有集合的一些特点,可以存放多个元素,但存储对象有限且固定,枚举也有比较常见的使用场景,如我们需要表达性别(男.女),颜色(红.黄.蓝),星期(星期一.星期二...星期日),四季(春.夏.秋.冬),地理位置(东.西.南.北),方向(前.后.左.右)等,这些场景都非常适合枚举. 二. 定义枚举 java中使用enum来定义枚举,和class,interface具有同样的等级,(注意是enum,而不是Enum),定义枚举可以有两种情况 第一种:默认构造器(空构造

java基础:enum枚举

一:enum的用法 (1)JDK1.5之前没有Enum这个类型,那时候一般用接口常量来替代.有了JavaEnum之后,可以更贴近的表示这种常量.  (2)只能取特定值中的一个 (3)使用enum关键字,java.lang.enum(注意java.lang下的类不用导包) (4)枚举对象里面的值都必须是唯一的 二:举例

JAVA基础——枚举详解

前言: 在第一次学习面向对象编程时,我记得最深的一句话就是"万物皆对象".于是我一直秉承着这个思想努力的学习着JAVA,直到学习到枚举(Enum)时,看着它颇为奇怪的语法--我一直在想,这TM是个什么鬼???当时学习OOP时也是被类啊接口什么的整的有点昏头转向的于是就把这个小细节忽略掉了.后来到了公司工作后慢慢的又需要用上枚举了,看着它一副神秘兮兮的样子我还是决定要好好的深挖一下!以下链接是了解枚举时所参考的博客.如发现本文有错误或知识遗漏欢迎在评论中指正! 反编译那些事儿(二)-枚举

Java基础——枚举与注解

枚举类: 1.自定义枚举类 1.提供类的属性,声明为private final 2.声明为final的属性,在构造器中初始化,私有化构造器, 保证不能在类的外部创建其对象 3.通过公共的方法调用属性 4.创建枚举类的对象:将类的对象声明为public static final 2.关键字 enum定义枚举类 1.enum中的常用方法 (1)values();//以数组的形式返回整个枚举类型的所有的对象 Season[] seasons = Season.values();//返回的是一个数组 (

Java枚举enum以及应用:枚举实现单例模式

枚举作为一个常规的语言概念,一直到Java5才诞生不得不说有点奇怪,以至于到现在为止很多程序员仍然更喜欢用static final的形式去命名常量而不使用,一般情况下,Java程序员用这种方式去实现枚举: class EnumByClass{ public static final int RED=0; public static final int GREEN=1; public static final int BLUE=2; } 这种方式实现的枚举也叫int枚举模式,尽管很常用,但是由in