枚举(enum)

转载:https://segmentfault.com/a/1190000007688908

枚举类

实例有限而且固定的类,在Java里被称为枚举类。

早期采用通过定义类的方式来实现,可以采用如下设计方式

  • 通过private将构造器隐藏起来
  • 把这个类的所有可能实例都使用public static final 修饰的类变量来保存
  • 如果与必要,可以提供一些静态方法,允许其他程序根据特定参数来获取与之匹配的实例
  • 使用枚举类可以使程序更加健壮,避免创建对象的随意性
    Java从JDK1.5后就增加了对枚举类的支持。
枚举类入门

Java5新增了一个enum关键字(它与class、interface关键字的地位相同),用以定义枚举类。枚举类是一种特殊的类,它一样可以有自己的成员变量、方法,可以实现一个或多个接口,也可以定义自己的构造器。一个Java源文件中最多只能定义一个public访问权限的枚举类,且该Java源文件也必须和该枚举类的类名相同。

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

编译上面Java程序,将生成一个SeasonEnum.class文件,这表明枚举类是一个特殊的Java类。
所有的枚举值之间以英文逗号(,)隔开,枚举值列举结束后以英文分号作为结束。这些枚举值代表了该枚举类的所有可能的实例。

public class EnumTest 
{
    public void judge(SeasonEnum s)
    {
        //switch语句里的表达式可以是枚举值
        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) 
    {
        //枚举类默认有一个values()方法,返回该枚举类的所有实例
        for(SeasonEnum s : SeasonEnum.values())
        {
            System.out.println(s);
        }
        //使用枚举实例时,可通过EnumClass.variable形式来访问
        new EnumTest().judge(SeasonEnum.SPRING);
    }
}

当switch控制表达式使用枚举类型时,后面case表达式中的值直接使用枚举值的名字,无须添加枚举类作为限定。
java.lang.Enum类中提供了如下几个方法:

  • int compareTo(E o):该方法用于与指定枚举对象比较顺序,同一个枚举实例只能与相同类型的枚举实例进行比较。如果该枚举对象位于指定枚举对象之后,则返回正整数;如果该枚举对象位于指定枚举对象之前,则返回负整数,否则返回零。
  • String name():返回此枚举实例的名称,这个名称就是定义枚举类时列出的所有枚举值之一。与此方法相比,大多数程序员应该优先考虑使用toString()方法,因为toString()方法返回更加用户友好的名称。
    int ordinal():返回枚举值在枚举类中的索引值(就是枚举值在枚举声明中的位置,第一个枚举值的索引值为零)。
  • String toString():返回枚举常量的名称,与name方法相似,但toString()方法更常用。
  • public static <T extends Enum <T>> T valueOf(Class<T>enumType, String name):这是一个静态方法,用于返回指定枚举类中指定名称的枚举值。名称必须与在该枚举类中声明枚举值时所用的标识符完全匹配,不允许使用额外的空白字符。

当程序使用System.out.println(s)语句来打印枚举值时,实际上输出的是该枚举值的toString()方法,也就是输出该枚举值的名字。

枚举类的成员变量、方法和构造器

枚举类的实例只能是枚举值,而不是随意地通过new来创建枚举类对象。

public enum Gender 
{
    MALE,FEMALE;
    //定义一个public修饰的实例变量
    private String name;
    public void setName(String name) 
    {
        switch (this) {
        case MALE:
            if (name().equals("男")) 
            {
                this.name = name;
            }
            else 
            {
                System.out.println("参数错误");
                return;
            }
            break;
        case FEMALE:
            if (name().equals("女")) 
            {
                this.name = name;
            }
            else 
            {
                System.out.println("参数错误");
                return;
            }
            break;
        }
    }
    public String getName() 
    {
        return this.name();
    }
}

public class GenderTest 
{
    public static void main(String[] args) 
    {
        //通过Enum的valueOf()方法来获取指定枚举类的枚举值
        //Gender gender = Enum.valueOf(Gender.class, "FEMALE");
        Gender g = Gender.valueOf("FEMALE");
        g.setName("女");
        System.out.println(g+"代表:"+g.getName());
        g.setName("男");
        System.out.println(g+"代表:"+g.getName());
    }
}

枚举类通常应该设计成不可变类,成员变量值不允许改变,将枚举类的成员变量都使用private final修饰。如果将所有的成员变量都使用final修饰符来修饰,必须在构造器里为这些成员变量指定初始值,为枚举类显式定义带参数的构造器。

一旦为枚举类显式定义了带参数的构造器,列出枚举值时就必须对应地传入参数。

public enum Gender 
{
    //此处的枚举值必须调用对应的构造器来创建
    MALE("男"),FEMAL("女");
    private final String name;
    private Gender(String name)
    {
        this.name =name;
    }
    public String getName()
    {
        return this.name();
    }
}
实现接口的枚举类

枚举类也可以实现一个或多个接口。与普通类实现一个或多个接口完全一样,枚举类实现一个或多个接口时,也需要实现该接口所包含的方法。

public interface GenderDesc
{
    void info();
}

如果由枚举类来实现接口里的方法,则每个枚举值在调用该方法时都有相同的行为方式(因为方法体完全一样)。如果需要每个枚举值在调用该方法时呈现出不同的行为方式,则可以让每个枚举值分别来实现该方法,每个枚举值提供不同的实现方式,从而让不同的枚举值调用该方法时具有不同的行为方式。

public enum Gender implements GenderDesc
{
    // 此处的枚举值必须调用对应构造器来创建
    MALE("男")
    // 花括号部分实际上是一个类体部分
    {
        public void info()
        {
            System.out.println("这个枚举值代表男性");
        }
    },
    FEMALE("女")
    {
        public void info()
        {
            System.out.println("这个枚举值代表女性");
        }
    };
    // 其他部分与codes\06\6.9\best\Gender.java中的Gender类完全相同
    private final String name;
    // 枚举类的构造器只能使用private修饰
    private Gender(String name)
    {
        this.name = name;
    }
    public String getName()
    {
        return this.name;
    }
    // 增加下面的info()方法,实现GenderDesc接口必须实现的方法
    public void info()
    {
        System.out.println(
            "这是一个用于用于定义性别的枚举类");
    }
}

当创建MALE和FEMALE两个枚举值时,后面又紧跟了一对花括号,这对花括号里包含了一个info()方法定义。花括号部分实际上就是一个类体部分,在这种情况下,当创建MALE和FEMALE枚举值时,并不是直接创建Gender枚举类的实例,而是相当于创建Gender的匿名子类的实例。

并不是所有的枚举类都使用了final修饰。非抽象的枚举类才默认使用final修饰。对于一个抽象的枚举类而言——只要它包含了抽象方法,它就是抽象枚举类,系统会默认使用abstract修饰,而不是使用final修饰。

编译上面的程序,生成了Gender.class、Gender$1.class和Gender$2.class三个文件,证明了:MALE和FEMALE实际上是Gender匿名子类的实例,而不是Gender类的实例。当调用MALE和FEMALE两个枚举值的方法时,就会看到两个枚举值的方法表现不同的行为方式。

包含抽象方法的枚举类
public enum Operation 
{
    PLUS
    {
        public double eval(double x, double y) 
        {
            return x + y;
        }
    },
    MINUS
    {
        public double eval(double x, double y) 
        {
            return x - y;
        }
    },
    TIMES
    {
        public double eval(double x, double y) 
        {
            return x * y;
        }
    },
    DIVIDE
    {
        public double eval(double x, double y) 
        {
            return x / y;
        }
    };
    //为枚举类定义一个抽象方法
    //这个抽象方法由不同的枚举值提供不同的实现
    public abstract double eval(double x, double y);
    public static void main(String[] args) 
    {
        System.out.println(Operation.PLUS.eval(3, 4));
        System.out.println(Operation.MINUS.eval(5, 4));
        System.out.println(Operation.TIMES.eval(8, 8));
        System.out.println(Operation.DIVIDE.eval(1, 5));
    }
}

枚举类里定义抽象方法时不能使用abstract关键字将枚举类定义成抽象类(因为系统自动会为它添加abstract关键字),但因为枚举类需要显式创建枚举值,而不是作为父类,所以定义每个枚举值时必须为抽象方法提供实现,否则将出现编译错误。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容

  • 本文包括:枚举由来如何使用?枚举类特性单例设计模式定义特殊结构枚举星期输出中文的案例枚举类API 枚举(enum)...
    廖少少阅读 2,838评论 2 14
  • 枚举类 (enum) 在某些情况下,一个类的对象时有限且固定的,如季节类,它只有春夏秋冬4个对象这种实例有限且固定...
    AshengTan阅读 85,845评论 6 49
  • 由于本人能力有限,文中若有错误之处,欢迎指正。转载请注明出处:http://www.jianshu.com/p/6...
    WaitingAnd阅读 1,691评论 6 13
  • 枚举类构造器 只能使用 private 访问修饰符,所以无法从外部调用构造器,构造器只在构造枚举值时被调用; 使用...
    天空在微笑阅读 276评论 0 0
  • 裁判要旨 人民法院可以根据当事人的请求对案件所涉商标是否为驰名商标进行认定,以判断他人的使用行为是否构成侵权,从而...
    小好阅读 337评论 0 1