原理】
写一个枚举,相信大家都会,如:
1public enum City {Guangzhou, Shenzhen, Dongguan}这是个城市的枚举类,包含广州、深圳、东莞三个城市枚举值,但是它怎么用呢?它的背后的真面目是怎样的呢?
下面我们来反编译一下这个枚举,结果得出:
1public final class City extends Enum<City> { 2 public static final City[] values() { 3 return (City[]) $VALUES.clone(); 4 } 5 public static City valueOf(String name) { 6 return (City) Enum.valueOf(City, name); 7 } 8 // 构造函数 9 private City(String s, int i) {10 super();11 }12 public static final City Guangzhou;13 public static final City Shenzhen;14 public static final City Dongguan;1516 private static final City $VALUES[];17 // 实例化枚举18 static {19 Guangzhou = new City("Guangzhou", 0);20 Shenzhen = new City("Shenzhen", 1);21 Dongguan = new City("Dongguan", 2);22 $VALUES = (new City[] { Guangzhou, Shenzhen, Dongguan });23 }24}从这个类中我们可以看出几点:
1)是个类 并且是final类,继承与java.lang.Enum类 不可继承。
2)用static块初始化。
3)定义了几个final类型的City对象,并且在static块中初始化。
由此可以知道,enum本质是一个final类,而JavaAPI的设计出的这个Enum,是一个被封装之后的类,旨在提供一种代码简洁而高效的类型管理组件,简化开发者编码。这当然是废话啦,各位看看,同样是功能的两种写法哪一种更简洁?哪一种更值得用?
我们再来看看enum接口类的源码:
1public abstract class Enum<E extends Enum<E>> 2 implements Comparable<E>, Serializable { 3 //枚举常量名称 4 private final String name; 5 //返回枚举常量名称 6 public final String name() { 7 return name; 8 } 9 //枚举常量的序数 初始值为0 根据位置而得出10 private final int ordinal;11// 返回枚举常量的序数12 public final int ordinal() {13 return ordinal;14 }15 //私有构造函数,程序无法直接调用改函数进行初始化16 //它用于由响应枚举类型声明的编译器发出的代码17 //@param name:枚举常量的名称18 //@param ordinal: 枚举常量的序数19 protected Enum(String name, int ordinal) {20 this.name = name;21 this.ordinal = ordinal;22 }23 //和name()是一个意思24 public String toString() {25 return name;26 }27 //判断是否相同对象的函数28 public final boolean equals(Object other) {29 return this==other;30 }31 //为枚举返回一个hashCode32 public final int hashCode() {33 return super.hashCode();34 }35 //克隆函数36 protected final Object clone() throws CloneNotSupportedException {37 throw new CloneNotSupportedException();38 }39 //比较此枚举与指定对象的顺序(编号)。40 //在该对象小于、等于或大于指定对象时,分别返回负整数、零或正整数。41 //枚举常量只能与相同枚举类型的其他枚举常量进行比较。42 //该方法实现的自然顺序就是声明常量的顺序。 43 public final int compareTo(E o) {44 Enum<?> other = (Enum<?>)o;45 Enum<E> self = this;46 if (self.getClass() != other.getClass() && // optimization47 self.getDeclaringClass() != other.getDeclaringClass())48 throw new ClassCastException();49 return self.ordinal - other.ordinal;50 }51// 以下方法略过....52}有了这个抽象类Enum的源码,我们基本上知道枚举是怎么使用的了,下面是一些常用的方法:
方法名 | 返回值 | 说明 |
name() | String | 返回枚举的名称 |
ordinal() | int | 返回枚举常量的序数 |
compareTo(E o) | int | 比较此枚举与指定对象的顺序(编号)。 在该对象小于、等于或大于指定对象时,分别返回负整数、零或正整数。 枚举常量只能与相同枚举类型的其他枚举常量进行比较。 该方法实现的自然顺序就是声明常量的顺序 |
【使用方式】
Enum类实现三种枚举:
1)最常用:
1public enum Num {2 ONE,TWO,THREE;3}2)定义带参构造函数:
1public enum EnumTest{ 2 ONE("1","YI"); 3 private String name; 4 private String value; 5 //一个参 6 private EnumTest(String name){ 7 this.name=name; 8 } 9 //两个参数10 private EnumTest(String name,String value){11 this.name=name;12 this.value=value;13 }14 //...多个参数15 public static void main(String[] args) {16 System.out.println(EnumTest.ONE.name);17 System.out.println(EnumTest.ONE.value);18 }19}
3)抽象方法实现:
1public enum EnumTest{ 2 ONE("1") { 3 @Override 4 public void handle() { 5 // TODO Auto-generated method stub 6 } 7 }; 8 private String name; 9 private EnumTest(String name){10 this.name=name;11 }12 //定义抽象方法13 public abstract void handle();14 public static void main(String[] args) {15 //调用16 EnumTest.ONE.handle();17 }18}【注意事项】
1)不能继承,因为枚举类本身已经继承了Enum抽象类,而Java是单继承
2)枚举的第一行必须是枚举项,所有其它的变量和方法都必须在枚举项后面
3) 枚举类可以定义抽象方法,但没枚举项必须重写所有抽象方法
4)可以在switch中使用枚举,它的使用方法如下:
1switch(EnumTest.ONE){ 2 3case ONE: 4 5break; 6 7case TWO: 8 9break;1011}补充:
一、包含抽象方法的枚举类:
定义一个 Calculator 枚举类,有4个枚举值PLUS、MINUS、TIMES、DIVIDE,分别代表加、减、乘、除,该枚举类有一个 calculate() 抽象方法,用于完成计算。
包含抽象方法的枚举类Calculator:
public enum Calculator { // 加法运算 PLUS { // 花括号部分其实是一个匿名内部子类 @Override public int calculate(int x, int y) { return x + y; } }, // 减法运算 MINUS { // 花括号部分其实是一个匿名内部子类 @Override public int calculate(int x, int y) { // TODO Auto-generated method stub return x - y; } }, // 乘法运算 TIMES { // 花括号部分其实是一个匿名内部子类 @Override public int calculate(int x, int y) { return x * y; } }, // 除法运算 DIVIDE { // 花括号部分其实是一个匿名内部子类 @Override public int calculate(int x, int y) { return x / y; } }; //为该枚举类定义一个抽象方法,枚举类中所有的枚举值都必须实现这个方法 public abstract int calculate(int x, int y);}加、减、乘、除计算:
public class TestDemo { public static void main(String[] args) { //加、减、乘、除计算 System.out.println("2 + 2 = " + Calculator.PLUS.calculate(2, 2)); System.out.println("5 - 2 = " + Calculator.MINUS.calculate(5, 2)); System.out.println("2 * 2 = " + Calculator.TIMES.calculate(2, 2)); System.out.println("4 / 2 = " + Calculator.DIVIDE.calculate(4, 2)); }}/** 使用枚举实现SqlSessionFactory单例 @Author Winston @date 2018年10月4日 * */publicenum SingletonSqlSessionFactoryEnum { /** * 定义一个成员变量,如果变量后面有方法要加分号 */ INSTACE; /** *SqlSessionFactory对象 */ private SqlSessionFactory sqlSessionFactory; /** * JVM会值调用构造方法一次 * @throws IOException */ private SingletonSqlSessionFactoryEnum(){ System.out.println("枚举构造方法初始化"); try { //加载配置文件 Reader read = Resources.getResourceAsReader("config.xml"); sqlSessionFactory = new SqlSessionFactoryBuilder().build(read); } catch (IOException e) { e.printStackTrace(); } } /** 获取sqlSessionFactory对象实例 @Author Winston @date 2018年10月4日 @email 940945444@qq.com @return @param */ publicstatic SqlSessionFactory getInstance(){ return SingletonSqlSessionFactoryEnum.INSTACE.sqlSessionFactory; }}使用publicclass TestSingletonSqlSessionFactoryEnum { publicstaticvoid main(String[] args) { //获取对象,构造方法只会初始化一次 System.out.println(SingletonSqlSessionFactoryEnum.getInstance()); System.out.println(SingletonSqlSessionFactoryEnum.getInstance()); System.out.println(SingletonSqlSessionFactoryEnum.getInstance()); System.out.println(SingletonSqlSessionFactoryEnum.getInstance()); }}旁白:枚举类的构造方法JVM只会初始化一次。通常使用枚举来实现单例都是该枚举类只有一个成员属性的时候