Java核心 --- 枚举
枚举把显示的变量与逻辑的数字绑定在一起在编译的时候,就会发现数据不合法也起到了使程序更加易读,规范代码的作用
一、用普通类的方式实现枚举
新建一个终态类Season,把构造方法设为私有,因为枚举值不能随意增加因为不能new出这个类的对象,所以需要定义成员变量,把new写在类的内部这样,就可以在类的外部通过访问类的静态成员变量的方式访问到枚举值通过这样的方式,只能在类的外部使用在枚举类的内部定义的枚举值
类Season里面是可以有方法的,我们设置地球又公转了四分之一方法的返回值是Season,对应的这个类里面的所有枚举值将转动四分之一重写方法toString(),打印枚举的值测试一下,打印一个枚举值的next后的值,再经过toString后输出
/Enumeration/src/yuki/core/enumeration/imitation/SeasonTest.java
package yuki.core.enumeration.imitation;public class SeasonTest { public static void main(String[] args) { Season season = Season.AUTUMN; System.out.PRintln(season.nextQuarter()); }}
/Enumeration/src/yuki/core/enumeration/imitation/Season.java
package yuki.core.enumeration.test;public final class Season { //私有构造方法 private Season(){} //定义静态成员常量 public final static Season SPRING = new Season(); public final static Season SUMMER = new Season(); public final static Season AUTUMN = new Season(); public final static Season WINTER = new Season(); //定义地球又公转了四分之一 public Season nextQuarter(){ Season ret = null; if(this == WINTER) ret = SPRING; else if(this == SPRING) ret = SUMMER; else if(this == SUMMER) ret = AUTUMN; else if(this == AUTUMN) ret = WINTER; return ret; } //打印枚举的值 @Override public String toString(){ return this == WINTER ? "Winter" : ( this == SPRING ? "Spring" : ( this == SUMMER ? "Summer" : "Autumn")); } }
可以看到程序中出现了大量的if-else,这样似乎不太好可以把每一个元素的nextQuarter写成自己独立的方法这需要定义成抽象的nextQuarter来使要新建对象就必须先实现这个方法拥有抽象方法的类也必须是抽象的这时,直接使用new来新建对象的方式就会报红,使用它的子类创建实例对象定义为抽象的类需要子类来实现它的抽象方法,这是需要去掉final修饰符在每个枚举值得类中实现抽象方法nextQuarter(),再次测试在这里,把统一的if-else语句转化为了一个个独立的类
/Enumeration/src/yuki/core/enumeration/imitation/Season.java
package yuki.core.enumeration.imitation;public abstract class Season { //私有构造方法 private Season(){} //定义静态成员常量 public final static Season SPRING = new Season(){ @Override public Season nextQuarter() { return SUMMER; } }; public final static Season SUMMER = new Season(){ @Override public Season nextQuarter() { return AUTUMN; } }; public final static Season AUTUMN = new Season(){ @Override public Season nextQuarter() { return WINTER; } }; public final static Season WINTER = new Season(){ @Override public Season nextQuarter() { return SPRING; } }; public abstract Season nextQuarter(); //打印枚举的值 @Override public String toString(){ return this == WINTER ? "Winter" : ( this == SPRING ? "Spring" : ( this == SUMMER ? "Summer" : "Autumn")); } }
运行结果如下:
Winter
二、一个基本的枚举类这里把枚举放在类里定义,作为测试类的一个成员内部类枚举里的一个元素就相当于类里的一个对象对于枚举这种类型的对象,它自动的帮我们实现了toString方法打印出的是枚举值的接收引用的名称
方法name()获得接收引用的名称方法ordinal()获得枚举值在枚举中所在的位置,下表从0开始静态方法valueOf(String)可以通过枚举值的名称得到枚举值从浏览器获得的请求参数值就可以通过这种方式编程枚举值对象静态方法values()可以获得枚举里的所有枚举值
/Enumeration/src/yuki/core/enumeration/construction/SquareVertexTest.java
package yuki.core.enumeration.construction;public class SquareVertexTest { public static void main(String[] args) { SquareVertex vertex = SquareVertex.A; System.out.println(vertex); System.out.println(vertex.name()); System.out.println(vertex.ordinal()); System.out.println(SquareVertex.valueOf("D")); System.out.println(SquareVertex.values().length); } /** * 正方形的四个顶点 */ public enum SquareVertex{ A, B, C, D }}
运行结果如下:
AA0D4
三、带有构造方法的枚举枚举是一个类,所以应该可以定义自己的构造方法构造方法必须放在元素列表之后,并在元素列表之后加上分号构造方法必须是私有的,默认调用的是无参构造所有的枚举值在第一次用到枚举类时全部初始化
在枚举元素后加圆括号指定参数列表,可以调用对应的构造方法在枚举元素后加上没有内容的圆括号,调用的方法也是无参构造方法
/Enumeration/src/yuki/core/enumeration/construction/DirectionTest.java
package yuki.core.enumeration.construction;public class DirectionTest { public static void main(String[] args) { @SuppressWarnings("unused") Direction direction = Direction.W; } public enum Direction{ E(1), S(), W, N; private Direction(){ System.out.println("无参构造方法"); } private Direction(int direct){ System.out.println("Direction(int direct)"); } }}
运行结果如下:
Direction(int direct)无参构造方法无参构造方法无参构造方法
四、实现带有抽象方法的枚举添加一个抽象方法返回类型是枚举类本身在枚举元素后添加花括号实现这个抽象方法
/Enumeration/src/yuki/core/enumeration/abstraction/ColorTest.java
package yuki.core.enumeration.abstraction;public class ColorTest { public static void main(String[] args) { } public enum TrafficLamp{ RED{ @Override public TrafficLamp nextLamp() { return GREEN; } }, GREEN{ @Override public TrafficLamp nextLamp() { return YELLOW; } }, YELLOW{ @Override public TrafficLamp nextLamp() { return RED; } }; public abstract TrafficLamp nextLamp(); }}
在bin目录下可以看到枚举的每个元素都生成了class文件D:/Workspaces/Eclipse/Enumeration/bin/yuki/core/enumeration/abstraction
为灯指定一个时间,这时需要在枚举元素的后面加上构造参数
/Enumeration/src/yuki/core/enumeration/abstraction/LampTest.java
package yuki.core.enumeration.abstraction;public class LampTest { public static void main(String[] args) { } public enum TrafficLamp{ RED(30){ @Override public TrafficLamp nextLamp() { return GREEN; } }, GREEN(45){ @Override public TrafficLamp nextLamp() { return YELLOW; } }, YELLOW(5){ @Override public TrafficLamp nextLamp() { return RED; } }; public abstract TrafficLamp nextLamp(); @SuppressWarnings("unused") private int time; private TrafficLamp(int time){ this.time = time; } }}
在新建父类的匿名子类对象时,可以指定调用父类的构造方法
/Enumeration/src/yuki/core/enumeration/abstraction/ConstructorTest.java
package yuki.core.enumeration.abstraction;public class ConstructorTest { public static void main(String[] args) { Supper supper = new Supper(1234){ @Override public String toString(){ return "子类的toString方法"; } }; System.out.println(supper); } public static class Supper{ public Supper() { System.out.println("无参构造被调用"); } public Supper(int i){ System.out.println("有参构造被调用"); } }}
运行结果如下:
有参构造被调用子类的toString方法
如果枚举只有一个成员,可以作为一种单例模式的实现方式更多内容请参考:[张孝祥Java高新技术_枚举]
五、为枚举提供的功能和演示1. 枚举与switch-caseswitch中匹配枚举值时,只需要写出不带枚举类型的枚举值就可以了
/Enumeration/src/yuki/core/enumeration/switch_case/QuarkTest.java
package yuki.core.enumeration.switch_case;public class QuarkTest { public static void main(String[] args) { Quark quark = Quark.C; String name = null; switch(quark){ case U: name = "上夸克"; break; case D: name = "下夸克"; break; case C: name = "粲夸克"; break; case S: name = "奇夸克"; break; case T: name = "顶夸克"; break; case B: name = "底夸克"; break; } System.out.println(name); } private enum Quark{ U, D, C, S, T, B; }}
运行结果如下:
粲夸克
2. 定制自己的枚举字符串这样就可以不用public static final String variable_name = string_value;
/Enumeration/src/yuki/core/enumeration/const_string/WebsiteTest.java
package yuki.core.enumeration.const_string;public class WebsiteTest { private enum Website{ GOOGLE("https://www.google.com.hk/?gws_rd=ssl"), AMAZON("http://www.amazon.cn/"); //网址 private String address; //构造函数 private Website(String address){ this.address = address; } //获取地址的方法 public String address() { return address; } } public static void main(String[] args) { Website amazon = Website.AMAZON; System.out.println(amazon); System.out.println(amazon.address()); }}
运行结果如下:
AMAZONhttp://www.amazon.cn/
3. EnumSet和EnumMap
JDK5.0 中在增加 Enum 类的同时,也增加了两个工具类 EnumSet 和 EnumMap,这两个类都放在 java.util 包中。
/Enumeration/src/yuki/core/enumeration/enum_util/EnumSetTest.java
package yuki.core.enumeration.enum_util;import java.util.EnumSet;/** * JDK5.0 中在增加 Enum 类的同时,也增加了两个工具类 EnumSet 和 EnumMap, * 这两个类都放在 java.util 包中。 * * EnumSet 是一个针对枚举类型的高性能的 Set 接口实现。 * EnumSet 中装入的所有枚举对象都必须是同一种类型, * 在其内部,是通过bit-vector 来实现,也就是通过一个 long 型数。 * EnumSet 支持在枚举类型的所有值的某个范围中进行迭代。 * * @author yuki * */public class EnumSetTest { private enum WeekDay{ Mon, Tue, Wed, Thu, Fri, Sat, Sun; } public static void main(String[] args) {// EnumSet<WeekDay> weekDays = EnumSet.of(WeekDay.Fri, WeekDay.Tue); EnumSet<WeekDay> weekDays = EnumSet.range(WeekDay.Tue, WeekDay.Fri); for(WeekDay day : weekDays){ System.out.println(day); } } }
运行结果如下:
TueWedThuFri
/Enumeration/src/yuki/core/enumeration/enum_util/EnumMapTest.java
package yuki.core.enumeration.enum_util;import java.util.EnumMap;import java.util.Map;/** * 与EnumSet 类似,EnumMap 也是一个高性能的 Map 接口实现, * 用来管理使用枚举类型作为 keys 的映射表,内部是通过数组方式来实现。 * EnumMap 将丰富的和安全的 Map 接口与数组快速访问结合到一起, * 如果你希望要将一个枚举类型映射到一个值,你应该使用 EnumMap。 * * @author yuki * */public class EnumMapTest { //卦值 private enum Divination{ KAN, XUN, QIAN, DUI, LI, ZHENG, KUN, GEN } //自然值 private enum Nature{ SHUI, FENG, TIAN, ZE, HUO, LEI, DI, SHAN } public static void main(String[] args) { //将卦值与自然值对应起来 Map<Divination, Nature> schema = new EnumMap<>(Divination.class); for(int i = 0; i < Divination.values().length; ++i){ schema.put(Divination.values()[i], Nature.values()[i]); } for(Map.Entry<Divination, Nature> entry : schema.entrySet()){ System.out.println(entry.getKey() + "/t->/t" + entry.getValue()); } }}
运行结果如下:
KAN -> SHUIXUN -> FENGQIAN -> TIANDUI -> ZELI -> HUOZHENG -> LEIKUN -> DIGEN -> SHAN
更多疑问请参考:[Java 语言中 Enum 类型的使用介绍]:http://www.ibm.com/developerworks/cn/java/j-lo-enum/index.html
六、反编译工具jdecompiler下载地址:http://jd.benow.ca/ ,点击下载JD-Eclipse>Offline installation
按照提示的步骤安装,把下载文件解压到以压缩包名为文件名的文件夹Help>InstallNewSoftware...>Add>AddRepository>选择文件夹jdeclipse_update_siteName=jdeclipse; Location=C:/Users/yuki/Downloads/jdeclipse_update_site点击OK,列表中出现Java Decompiler Eclipse Plug-in>勾选它>Next这时鼠标的指针变为等待,显示Calculating requirements and dependencies额,这好像要等好久,我去吃饭了,~~,哦,只是前面慢而已,点击Next选择I accept the terms of the license agreement>Finish显示InstallSoftware的弹出窗,这里又好慢再按照提示添加EmmanuelDupuy>选中>Finish>安装完成>restart Eclipse
打开Perferences>Java>Decompile,表明已经安装成功,当然下载的svn也是上面格式的文件,所以应该也可以用上面的方式安装General>Editor>FileAssociation>点击*.class without source看到默认是用ClassFileEditor打开的,从它的图标不难看出,它就是把class文件反编译为java文件格式打开的编辑器
在很多情况下我们是有源码的,当源码在maven库中对应的位置时maven工具就会自动下载源码,并用ClassFileViewer打开在设为默认后,重新打开Eclipse会发现默认的class打开方式还是反编译打开点击General>StartupAndShutdown,这里有一个检查项JD-Eclipse Plug-in取消它的勾选就可以了
七、反编译枚举类的类文件bin目录下的内容按照Eclipse的设定是不显示的在src下的class文件也是默认不显示的写一个枚举类PlatonicSolid,里面有五种元素
package yuki.core.enumeration.decompiler;public enum PlatonicSolid { FOUR, EIGHT, TWENTY, SIX, TWELVE}
打开/Enumeration/bin/yuki/core/enumeration/decompiler/复制PlatonicSolid.class到根目录下打开后,文件以为自己是有源码的,所以这里反编译不起作用
访问http://jd.benow.ca/ 下载JD-GUI,证明这个反编译器打不开编译后的枚举类文件额,不是要下载右边那个,左边那个是稳定版
打开后反编译的结果就仅仅是源码去掉了注释看起来软件升级了,看不到想要看的内容了
反编译网站:http://www.findmaven.net/decompile ,同样的结果反编译网站:http://www.showmycode.com/ 反编译结果如下
经过我的排板后的文件如下:因为编译不通过,所以用Ctrl+Shift+F排版是不行的
/Enumeration/src/yuki/core/enumeration/decompiler/platonic_solid/PlatonicSolid.java
package yuki.core.enumeration.decompiler.platonic_solid;public final class PlatonicSolid extends Enum<Enum<E>> { private PlatonicSolid(String s, int i) { super(s, i); } public static PlatonicSolid[] values() { PlatonicSolid aplatonicsolid[]; int i; PlatonicSolid aplatonicsolid1[]; System.arraycopy(aplatonicsolid = enum$VALUES, 0, aplatonicsolid1 = new PlatonicSolid[i = aplatonicsolid.length], 0, i); return aplatonicsolid1; } public static PlatonicSolid valueOf(String s) { return (PlatonicSolid)enum.valueOf(yuki/core/enumeration/decompiler/PlatonicSolid, s); } public static final PlatonicSolid FOUR; public static final PlatonicSolid EIGHT; public static final PlatonicSolid TWENTY; public static final PlatonicSolid SIX; public static final PlatonicSolid TWELVE; private static final PlatonicSolid enum$VALUES[]; static { FOUR = new PlatonicSolid("FOUR", 0); EIGHT = new PlatonicSolid("EIGHT", 1); TWENTY = new PlatonicSolid("TWENTY", 2); SIX = new PlatonicSolid("SIX", 3); TWELVE = new PlatonicSolid("TWELVE", 4); enum$VALUES = (new PlatonicSolid[] { FOUR, EIGHT, TWENTY, SIX, TWELVE }); }}
但是Enum是无法被继承的,具体请见:http://www.zhihu.com/question/19908744http://pf-miles.VEvb.com/blog/187155后一篇文章所说的内容就是,枚举的多态是由属性的类继承枚举类实现的
枚举类的声明如下:
public abstract class Enum<E extends Enum<E>>implements Comparable<E>, Serializable
这里的E继承了Enum<E>, 而枚举E又是枚举类的泛型参数,如果可以继承public final class PlatonicSolid extends Enum<Enum<E>>那么就必须要拥有一个继承了枚举的枚举类,这又回到了刚才的问题,所以问题无解
八、构造方法,方法,变量枚举类型的每一个值都将映射到protected Enum(String name, int ordinal)构造函数中在这里,每个值的名称都被转换成一个字符串,并且序数设置表示了每个设置的优先值。换句话说,enum Size {Small, Medium, Large}将映射到清单 2 中所示的构造函数调用中:new Enum<Size>("Small", 0);new Enum<Size>("Medium", 1);new Enum<Size>("Large", 2);
更多疑问请参考:[驯服 Tiger: 深入研究枚举类型]:http://www.ibm.com/developerworks/cn/java/j-tiger04195/
九、实现接口与使用接口组织枚举所有的枚举都继承自java.lang.Enum类。由于Java 不支持多继承,所以枚举对象不能再继承其他类。
/Enumeration/src/yuki/core/enumeration/interfaces/PlanetTest.java
package yuki.core.enumeration.interfaces;import java.util.Arrays;import java.util.List;public class PlanetTest { private interface Move{ void move(); }; private enum Planet implements Move{ SOLAR, EARTH{ @Override public void move() { System.out.println("rounding...center:" + SOLAR); } }, MOON{ @Override public void move() { System.out.println("rounding...center:" + EARTH); } }; @Override public void move() { System.out.println("rounding..."); } } public static void main(String[] args) { List<Planet> planets = Arrays.asList(Planet.values()); for(Planet planet : planets){ System.out.print(planet + " : "); planet.move(); } }}
运行结果如下:
SOLAR : rounding...EARTH : rounding...center:SOLARMOON : rounding...center:EARTH
使用接口也可以管理枚举,并且可以在接口内部再定义接口
/Enumeration/src/yuki/core/enumeration/interfaces/Shape.java
package yuki.core.enumeration.interfaces;public interface Shape { enum Circle implements Shape { Center, Radius } interface RegularPolygon extends Shape { enum Triangle implements RegularPolygon { Angel, Length } enum Square implements RegularPolygon { Point, Area } }}
/Enumeration/src/yuki/core/enumeration/interfaces/ShapeTest.java
package yuki.core.enumeration.interfaces;public class ShapeTest { public static void main(String[] args) { Shape circle = Shape.Circle.Radius; System.out.println(circle); Shape squre = Shape.RegularPolygon.Square.Area; System.out.println(squre); }}
运行结果如下:
RadiusArea
更多疑问请参考:[Java 枚举7常见种用法]:http://softbeta.VEvb.com/blog/1185573http://www.VEVb.com/happyPawpaw/archive/2013/04/09/3009553.html
更多好文请查看:http://www.VEVb.com/kodoyang/
点击下方的红色按钮“ 关注我 ”吧!
孔东阳
2014/10/2
新闻热点
疑难解答