Java枚举
1 枚举的用法
1.1 常量
在JDK1.5 之前,定义常量的方法:public static fianl…
而使用枚举,可以把相关的常量分组到一个枚举类型里,并且枚举提供了比常量更多的方法。
public enum Color {
RED, GREEN, BLANK, YELLOW
}
1.2 switch
使用枚举,增强代码可读性。
enum Signal {
GREEN, YELLOW, RED
}
public class TrafficLight {
Signal color = Signal.RED;
public void change() {
switch (color) {
case RED:
color = Signal.GREEN;
break;
case YELLOW:
color = Signal.RED;
break;
case GREEN:
color = Signal.YELLOW;
break;
}
}
}
1.3 向枚举中添加新方法
在enum实例序列的最后添加一个分号。而且 Java 要求必须先定义 enum 实例。
public enum Color {
RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
// 成员变量
private String name;
private int index;
// 构造方法
private Color(String name, int index) {
this.name = name;
this.index = index;
}
// 普通方法
public static String getName(int index) {
for (Color c : Color.values()) {
if (c.getIndex() == index) {
return c.name;
}
}
return null;
}
// get set 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}
1.4 覆盖枚举的方法
public enum Color {
RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
// 成员变量
private String name;
private int index;
// 构造方法
private Color(String name, int index) {
this.name = name;
this.index = index;
}
//覆盖方法
@Override
public String toString() {
return this.index+"_"+this.name;
}
}
1.5 实现接口
所有的枚举都继承自java.lang.Enum类。由于Java 不支持多继承,所以枚举对象不能再继承其他类。
public interface Behaviour {
void print();
String getInfo();
}
public enum Color implements Behaviour{
RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
// 成员变量
private String name;
private int index;
// 构造方法
private Color(String name, int index) {
this.name = name;
this.index = index;
}
//接口方法
@Override
public String getInfo() {
return this.name;
}
//接口方法
@Override
public void print() {
System.out.println(this.index+":"+this.name);
}
}
1.6 使用接口组织枚举
public interface Food {
enum Coffee implements Food{
BLACK_COFFEE,DECAF_COFFEE,LATTE,CAPPUCCINO
}
enum Dessert implements Food{
FRUIT, CAKE, GELATO
}
}
1.7 枚举集合
2 枚举的实现
示例代码
public enum ApplicationInterfaceTypeEnum {
dubbo("dubbo", 1), webapp("webapp", 2), custom("custom", 3);
private String name;
private int index;
//私有构造,防止被外部调用
private ApplicationInterfaceTypeEnum(String name, int index) {
this.name = name;
this.index = index;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}
通过javap进行反汇编
Compiled from "ApplicationInterfaceTypeEnum.java"
public final class cn.zzypiper.test.enumearn.ApplicationInterfaceTypeEnum extends java.lang.Enum<cn.zzypiper.test.enumearn.ApplicationInterfaceTypeEnum> {
public static final cn.zzypiper.test.enumearn.ApplicationInterfaceTypeEnum dubbo;
public static final cn.zzypiper.test.enumearn.ApplicationInterfaceTypeEnum webapp;
public static final cn.zzypiper.test.enumearn.ApplicationInterfaceTypeEnum custom;
public static cn.zzypiper.test.enumearn.ApplicationInterfaceTypeEnum[] values();
Code:
0: getstatic #1 // Field $VALUES:[Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum;
3: invokevirtual #2 // Method "[Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum;".clone:()Ljava/lang/Object;
6: checkcast #3 // class "[Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum;"
9: areturn
LineNumberTable:
line 3: 0
public static cn.zzypiper.test.enumearn.ApplicationInterfaceTypeEnum valueOf(java.lang.String);
Code:
0: ldc #4 // class cn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum
2: aload_0
3: invokestatic #5 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
6: checkcast #4 // class cn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum
9: areturn
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 name Ljava/lang/String;
public java.lang.String getName();
Code:
0: aload_0
1: getfield #7 // Field name:Ljava/lang/String;
4: areturn
LineNumberTable:
line 16: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum;
public void setName(java.lang.String);
Code:
0: aload_0
1: aload_1
2: putfield #7 // Field name:Ljava/lang/String;
5: return
LineNumberTable:
line 20: 0
line 21: 5
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum;
0 6 1 name Ljava/lang/String;
public int getIndex();
Code:
0: aload_0
1: getfield #8 // Field index:I
4: ireturn
LineNumberTable:
line 24: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum;
public void setIndex(int);
Code:
0: aload_0
1: iload_1
2: putfield #8 // Field index:I
5: return
LineNumberTable:
line 28: 0
line 29: 5
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum;
0 6 1 index I
static {};
Code:
0: new #4 // class cn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum
3: dup
4: ldc #9 // String dubbo
6: iconst_0
7: ldc #9 // String dubbo
9: iconst_1
10: invokespecial #10 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;I)V
13: putstatic #11 // Field dubbo:Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum;
16: new #4 // class cn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum
19: dup
20: ldc #12 // String webapp
22: iconst_1
23: ldc #12 // String webapp
25: iconst_2
26: invokespecial #10 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;I)V
29: putstatic #13 // Field webapp:Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum;
32: new #4 // class cn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum
35: dup
36: ldc #14 // String custom
38: iconst_2
39: ldc #14 // String custom
41: iconst_3
42: invokespecial #10 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;I)V
45: putstatic #15 // Field custom:Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum;
48: iconst_3
49: anewarray #4 // class cn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum
52: dup
53: iconst_0
54: getstatic #11 // Field dubbo:Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum;
57: aastore
58: dup
59: iconst_1
60: getstatic #13 // Field webapp:Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum;
63: aastore
64: dup
65: iconst_2
66: getstatic #15 // Field custom:Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum;
69: aastore
70: putstatic #1 // Field $VALUES:[Lcn/zzypiper/test/enumearn/ApplicationInterfaceTypeEnum;
73: return
LineNumberTable:
line 5: 0
line 3: 48
}
可以把 enum 看成是一个普通的 class,它们都可以定义一些属性和方法,不同之处是:enum 不能使用 extends 关键字继承其他类,因为 enum 已经继承了 java.lang.Enum(java是单一继承)。
3 Enum类
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable {
private final String name;
public final String name() {
return name;
}
private final int ordinal;
public final int ordinal() {
return ordinal;
}
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
public String toString() {
return name;
}
public final boolean equals(Object other) {
return this==other;
}
public final int hashCode() {
return super.hashCode();
}
protected final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
public final int compareTo(E o) {
Enum<?> other = (Enum<?>)o;
Enum<E> self = this;
if (self.getClass() != other.getClass() && // optimization
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
}
@SuppressWarnings("unchecked")
public final Class<E> getDeclaringClass() {
Class<?> clazz = getClass();
Class<?> zuper = clazz.getSuperclass();
return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
}
public static <T extends Enum<T>> T valueOf(Class<T> enumType,
String name) {
T result = enumType.enumConstantDirectory().get(name);
if (result != null)
return result;
if (name == null)
throw new NullPointerException("Name is null");
throw new IllegalArgumentException(
"No enum constant " + enumType.getCanonicalName() + "." + name);
}
protected final void finalize() { }
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
throw new InvalidObjectException("can't deserialize enum");
}
private void readObjectNoData() throws ObjectStreamException {
throw new InvalidObjectException("can't deserialize enum");
}
}
4 枚举与单例
以下是枚举实现的单例模式
public enum Singleton {
INSTANCE;
public void doSomething() {
System.out.println("doSomething");
}
}
模拟反序列化攻击
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
String path ="EnumSingleton.ser";
//Write Obj to file
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));
oos.writeObject(Singleton.INSTANCE);
//Read Obj from file
File file = new File(path);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
Singleton newInstance = (Singleton) ois.readObject();
//判断是否是同一个对象
System.out.println(Singleton.INSTANCE == newInstance); // true
newInstance.doSomething();
}
}
5 Java枚举如何比较
java 枚举类比较是用==还是equals?
测试代码如下:
public enum GameEnum{
BIG,
SMALL,
FATTER
}
public static void main(String[] args) {
GameEnum s1 = GameEnum.BIG;
GameEnum s2 = GameEnum.BIG;
GameEnum ss1 = GameEnum.SMALL;
System.out.println("s1 == s2:" + (s1 == s2));
System.out.println("s1.equals(s2):" + (s1.equals(s2)));
System.out.println("s1 == ss1:" + (s1 == ss1));
System.out.println("s1.equals(ss1):" + (s1.equals(ss1)));
}
out:
s1 == s2:true
s1.equals(s2):true
s1 == ss1:false
s1.equals(ss1):false
可以看到,使用== 和使用equals方法的执行结果是一样的。
Enum中实现了equals方法,即使用==
/**
* Returns true if the specified object is equal to this
* enum constant.
*
* @param other the object to be compared for equality with this object.
* @return true if the specified object is equal to this
* enum constant.
*/
public final boolean equals(Object other) {
return this==other;
}
6 switch对枚举的支持
见 1.2
7 枚举的序列化
常见的单例模式都有一个比较大的问题:就是一旦实现了Serializable接口之后,序列化会破坏单例。有一种解决办法就是使用readResolve()方法来避免此事发生。
对于枚举,Java规范中定义枚举变量在JVM中是唯一的。因此在枚举类型的序列化和反序列化上,Java做了特殊的规定。
深度分析Java的枚举类型—-枚举的线程安全性及序列化问题
8 枚举的线程安全
当一个Java类第一次被真正使用到的时候静态资源被初始化、Java类的加载和初始化过程都是线程安全的。所以,创建一个enum类型是线程安全的。