java 枚举随机_java枚举类型学习

本文详细介绍了Java枚举的各种特性和使用方式,包括枚举的基本用法、自定义方法、实现接口、使用EnumSet和EnumMap等高级特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

用的不多,但用的时候仅仅简单的使用,不太明白原理,今天就系统的学一下枚举。参考:java编程思想。

Update:

枚举可以当做数据字典来存储,通常只要一个字段即instance本身,toString()或者name()打印的string。

枚举的数据都是一个实例对象,比如 enum Test{A}中A就是一个对象,A的toString和name()的结果是“A”。而如果一个字符串为"A",可以转为对应的枚举实例:Test.valueOf("A")

1.简单创建

枚举就是一个固定的集合,内容是声明的类。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

packagecom.test.java.tenum;/*** 一个简单的enum实例

* Created by Administrator on 2016/3/30.*/

public enumSimpleEnumUse {

NOT,MILD,MEDIUM,HOT,FLAMING

}classTestSE{public static voidmain(String[] args) {

SimpleEnumUse medium=SimpleEnumUse.MEDIUM;

System.out.println(medium);

System.out.println(medium.ordinal());

}

}

View Code

创建enum的时候,里面的元素就是类,默认创建了toString(),odinal()方法以及value字段。其中odinal是声明的顺序(从0开始),value就是声明的名字。简单的使用的话,这样就可以了,配合switch即可。

2.深入了解

2.1基本特性

创建enum时,编译器会为你生成一个相关的类,这个类继承自java.lang.Enum。下面实例:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

packagecom.test.java.tenum;/*** 了解enum特性

* Created by Administrator on 2016/3/30.*/

public classEnumClass {public static voidmain(String[] args) {for(Shrubbery s : Shrubbery.values()) {//s继承Enum//ordinal表示声明的顺序

System.out.print(s+" ordinal:"+s.ordinal());//enum自动实现了Comparable接口

System.out.println(" compareTo:"+s.compareTo(Shrubbery.CRAWING));//编译器提供了equals和hashCode

System.out.println(s.equals(Shrubbery.CRAWING));

System.out.println(s==Shrubbery.CRAWING);

System.out.println(s.getDeclaringClass());

System.out.println(s.name());

System.out.println("======================");

}for(String s :"HANGING CRAWING GROUND".split(" ")) {

Shrubbery shrubbery= Enum.valueOf(Shrubbery.class, s);

System.out.println(shrubbery);

}

}

}enumShrubbery{

GROUND,CRAWING,HANGING

}

View Code

ordinal()方法返回一个int值,这个每个enum实例在声明时的次序,从0开始。可以使用==比较enum实例,编译器自动为你提供了equals()和hashCode()方法。Enum类实现了Comparable接口和Serializable接口。

2.1.1静态导入

在使用enum的实例的时候发现人家不是EnumClass.INSTANCE,即不是通过类名调用的,而是直接调用,看了下。原来是在包下静态导入了。即:

import static package.EnumClass.*。

究竟显示的修饰enum实例还是静态导入要看代码的复杂度,看看静态导入是不是会让代码难以理解。

2.2可以在enum中添加自己的方法

enum可以添加方法和构造器。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

packagecom.test.java.tenum;/*** 可以在enum中添加方法和构造器

* Created by Administrator on 2016/3/30.*/

public enumOzWitch {

WEST("Miss Gulch, aka the Wiched Witch of the West"),

NORTH("Glinda,the Good Witch of the North"),

EAST("Wicked Witch of the East,wearer of the Ruby Slippers,crushed by Dorothy's house"),

SOUTH("Good by inference,but missing")

;privateString description;privateOzWitch(String description){this.description =description;

}publicString getDescription(){returndescription;

}public static voidmain(String[] args) {for(OzWitch with :

OzWitch.values()) {

System.out.println(with+":"+with.getDescription());

}

}

}

View Code

通常,将enum的构造方法声明private,而实际上对于它的可访问性来说没有什么变化,因为即使不是private也只能在enum内部使用创建enum实例。一旦enum定义结束,编译器就不允许我们再使用其构造器来创建任何实例了。另外,必须优先声明实例,然后声明方法或属性,否则编译报错。

2.3探究values()

经过一下检测,values是编译器添加到你创建的enum类的,而Enum类本身中并没有values方法。我们可以通过Class对象获取所有的enum实例。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

packagecom.test.java.tenum;importjava.lang.reflect.Method;importjava.lang.reflect.Type;importjava.util.Set;importjava.util.TreeSet;/*** 通过反射查看enum的values

* 结果:values是编译器添加到enum类的,而Enum类本身没有values方法

* Created by Administrator on 2016/3/30.*/

public classReflection {public static Set analyze(Class>enumClass){

System.out.println("-----------Analyzing "+enumClass+"-------");

System.out.println("Interfaces:");for(Type t :

enumClass.getGenericInterfaces()) {

System.out.println(t);

}

System.out.println("Base: "+enumClass.getSuperclass());

System.out.println("Method:");

Set methods = new TreeSet<>();for(Method m :

enumClass.getMethods()) {

methods.add(m.getName());

}

System.out.println(methods);returnmethods;

}public static voidmain(String[] args){

Set exploreMethods = analyze(Explore.class);

Set enumMethods = analyze(Enum.class);

System.out.println("Explore.containsAll(Enum)?"+enumMethods.containsAll(enumMethods));

System.out.println("Explore.removeAll(Enum):");

exploreMethods.removeAll(enumMethods);

System.out.println(exploreMethods);

}

}enumExplore{

HERE,THERE

}/************************通过class获取实例********************************/

enumSearch{

HITHER,YON

}classUpcastEnum{public static voidmain(String[] args) {

Search[] values=Search.values();

Enum e=Search.HITHER;//e.values();//编译报错,说明No values() in Enum

for(Enum anEnum : e.getClass().getEnumConstants()) {

System.out.println(anEnum);

}

}

}classNonEnum{public static voidmain(String[] args) {

Class integerClass = Integer.class;try{for(Object en : integerClass.getEnumConstants()) {

System.out.println(en);

}

}catch(Exception e){

System.out.println(e);

}

}

}

View Code

2.4通过实现接口而不是继承他类来扩展

应为所有enum类都继承java.lang.Enum类。由于java不支持多继承,所以你的enum不能再继承其他类。但是可以实现多个接口。

2.5随机选取

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

packagecom.test.java.tenum;importjava.util.Random;/*** 随机抽取的工具类

* Created by Administrator on 2016/3/30.*/

public classEnums {private static Random random = new Random(47);public static > T random(Classec){returnrandom(ec.getEnumConstants());

}public static T random(T[] values){returnvalues[random.nextInt(values.length)];

}

}

View Code

2.6使用接口组织枚举

无法从enum继承子类有时很令人沮丧。这种需求有时源自我们希望扩展原enum中的元素,有时我们希望使用子类将一个enum中的元素进行分组。

在一个接口内部创建实现该接口的枚举,以此将元素进行分组,可以达到将枚举元素分类的目的。举例来说,假设想用enum表示不同的食物,同时还希望每个enum元素仍然保持Food类型。可以这样:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

packagecom.test.java.tenum;/*** 使用接口组织枚举

* Created by Administrator on 2016/3/30.*/

public interfaceFood {//开胃食物

enum Appetizer implementsFood{

SALAD,SOUP,SPRING_ROLLS;

}//主菜

enum MainCourse implementsFood{

LASNGE,BURRITO,PAD_THAI,LENTILS,HUMMOUS,VINDALOO;

}//甜点

enum Dessert implementsFood{

TIRAMISU,GELATO,BLACK_FOREST_CAKE,FRUIT,CREME_CARMEL;

}//coffe

enum Coffee implementsFood{

BLACK_COFFEE,DECAF_COFFEE,ESPRESSO,LATTE,CAPPUCCINO,TEA,HERB_TEA;

}//....

}classTypeOfFood{public static voidmain(String[] args) {

Food food=Food.Appetizer.SALAD;

food=Food.MainCourse.PAD_THAI;

}

}

View Code

如果enum类型实现了Food接口,那么我们就可以将其实例向上转型为Food,所以上例中的所有东西都是Food。

然而当你需要与一大堆类型打交道时,接口就不如enum好用。例如,你想创建一个枚举的枚举。那么可以创建一个新的enum,然后用其实例包装Food中的每一个enum类。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

packagecom.test.java.tenum;/*** 枚举的枚举

* Created by Administrator on 2016/3/30.*/

public enumCourse {

APPETIZER(Food.Appetizer.class),

MAINCOURSE(Food.MainCourse.class);privateFood[] values;private Course(Class extends Food>kind){

values=kind.getEnumConstants();

}publicFood randomSelection(){returnEnums.random(values);

}

}

View Code

在上面的程序中,每个Course实例都将其对应的Class对象作为构造器的参数。通过getEnumConstants方法,可以从该Class对象中取得某个Food子类的所有enum实例。因此,我们通过随机可以生成一份菜单:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classMeal{public static voidmain(String[] args) {for (int i = 0; i < 5; i++) {for(Course course : Course.values()) {

Food food=course.randomSelection();

System.out.println(food);

}

System.out.println("-------");

}

}

}

View Code

在这个例子中,我们通过遍历每个Course实例来获得枚举的枚举的值。此外,还可以更简洁:将一个enum嵌套在另一个enum内。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

packagecom.test.java.tenum;/*** 枚举嵌套

* Created by Administrator on 2016/3/30.*/

public enumSecurityCategory {

STOCK(Security.Stock.class),

BOND(Security.Bond.class);

Security[] values;

SecurityCategory(Class extends Security>kind){

values=kind.getEnumConstants();

}interfaceSecurity{enum Stock implementsSecurity{SHORT,LONG,MARGIN}enum Bond implementsSecurity{MUNICIPAL,JUNK}

}publicSecurity randomSelection(){returnEnums.random(values);

}public static voidmain(String[] args) {for (int i = 0; i < 10; i++) {

SecurityCategory random= Enums.random(SecurityCategory.class);

System.out.println(random+":"+random.randomSelection());

}

}

}

View Code

Security接口的作用是将其所包含的enum组成一个公共类型,这一点是必要的。然后SecurityCategory才能将Security中的enum作为其构造器的参数作用,以起到组织的效果。

如果我们将这种方式应用于Food实例,结果是这样:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

/*** 同理,food*/

enumMeal2{

APPETIZER(Food.Appetizer.class),

MAINCOURSE(Food.MainCourse.class),

DESSERT(Food.Dessert.class);privateFood[] values;private Meal2(Class extends Food>kind){

values=kind.getEnumConstants();

}public interfaceFood{//开胃食物

enum Appetizer implementsFood{

SALAD,SOUP,SPRING_ROLLS;

}//主菜

enum MainCourse implementsFood{

LASNGE,BURRITO,PAD_THAI,LENTILS,HUMMOUS,VINDALOO;

}//甜点

enum Dessert implementsFood{

TIRAMISU,GELATO,BLACK_FOREST_CAKE,FRUIT,CREME_CARMEL;

}//coffe

enum Coffee implementsFood{

BLACK_COFFEE,DECAF_COFFEE,ESPRESSO,LATTE,CAPPUCCINO,TEA,HERB_TEA;

}//....

}publicFood randomSelection(){returnEnums.random(values);

}public static voidmain(String[] args) {for (int i = 0; i < 5; i++) {for(Meal2 meal2 : Meal2.values()) {

Food food=meal2.randomSelection();

System.out.println(food);

}

System.out.println("----------------------------");

}

}

}

View Code

这仅仅是重新组织了下代码,不过多数情况下,这种方式使你的代码具有更清晰的结构。

2.7使用EnumSet代替标识

java se5引入EnumSet,是为了通过enum创建一种替代品,以替代传统的基于int的“标志位”。这种标识可以用来表示某种“开/关”信息。

下面enum表示在一座大楼中,警报传感器的安防位置:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

packagecom.test.java.tenum;importjava.util.EnumSet;/*** 酱爆传感器位置

* Created by Administrator on 2016/3/31.*/

public enumAlarmPoints {

STAR1,STAR2,LOBBY,OFFICE1,OFFICE2,OFFICE3,OFFICE4,BATHROOM,UTILITY,KITCHEN

}/*** 我们使用enumSet来跟踪警报器的状态*/

classEnumSets{public static voidmain(String[] args) {

EnumSet points = EnumSet.noneOf(AlarmPoints.class);//Empty set

points.add(AlarmPoints.BATHROOM);

System.out.println(points);//添加多个

points.addAll(EnumSet.of(AlarmPoints.STAR1,AlarmPoints.STAR2,AlarmPoints.KITCHEN,AlarmPoints.OFFICE3));

System.out.println(points);//去除office1到office4范围内的

points.removeAll(EnumSet.range(AlarmPoints.OFFICE1,AlarmPoints.OFFICE4));

System.out.println(points);//其他的

points =EnumSet.complementOf(points);

System.out.println(points);

}

}

View Code

2.8 使用EnumMap

EnumMap是一种特殊的Map,它要求其中的key必须来自一个enum。由于enum本身的限制,所以EnumMap在内部可由数组实现。因此EnumMap速度很快。

下面演示了命令设计模式的用法。一般来说,命令模式首先需要一个只有单一方法的接口,然后从该接口实现具有各自不同的行为的多个子类。接下来,程序员就可以构造命令对象,并在需要的时候使用它们。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

packagecom.test.java.tenum;importjava.util.EnumMap;importjava.util.Map;/*** Created by Administrator on 2016/3/31.*/

public classEnumMaps {public static voidmain(String[] args){

EnumMap em = new EnumMap(AlarmPoints.class);

em.put(AlarmPoints.KITCHEN,newCommand() {

@Overridepublic voidaction() {

System.out.println("kitchen fire!");

}

});

em.put(AlarmPoints.BATHROOM,newCommand() {

@Overridepublic voidaction() {

System.out.println("bathroom alert!");

}

});for (Map.Entryentry : em.entrySet()) {

System.out.println(entry.getKey()+":");

entry.getValue().action();

}try{

em.get(AlarmPoints.UTILITY).action();

}catch(Exception e){

System.out.println(e);

}

}

}interfaceCommand{voidaction();

}

View Code

与EnumSet一样,enum实例定义时的次序决定了其在EnumMap中的顺序。enum的每个实例作为一个键总是存在的,如果你没有为这个键调用put来存入相应的值,则对应的值为null。

与常量相关的方法相比,EnumMap有一个有点,允许程序员改变值对象,而常量相关的方法在编译器就被固定了。

2.9常量相关方法

java的enum有个特性:允许程序员为enum实例编写方法,从而为每个enum实例赋予各自不同的行为。要实现常量相关的方法,你需要为enum定义一个或者多个abstract方法,然后为每个enum实例实现该抽象方法。如下:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

packagecom.test.java.tenum;importjava.text.DateFormat;importjava.util.Date;/*** Created by Administrator on 2016/3/31.*/

public enumConstantspecificMethod {

DATE_TIME{

String getInfo(){return DateFormat.getDateInstance().format(newDate());

}

},

CLASSPATH{

String getInfo(){return System.getenv("CLASSPATH");

}

},

VERSION{

String getInfo(){return System.getProperty("java.version");

}

};abstractString getInfo();public static voidmain(String[] args) {for(ConstantspecificMethod csm : values()) {

System.out.println(csm.getInfo());

}

}

}

View Code

通过相应的enum实例,我们可以调用其上的方法。这通常也成为表驱动的代码(table-driven code),请注意它与前面提到的命令模式的相似之处。

在面向对象的程序设计中,不同的行为与不同的类关联。而通过常量相关的方法,每个enum实例可以具备自己独特的行为,这似乎说明每个enum实例就是一个特殊的类。我们并不能真的将enum实例当做一个类来使用。

与使用匿名内部类相比较,定义常量相关方法的语法更高效和简洁,下面是一个有趣的洗车的例子:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值