原文:https://blog.csdn.net/zhoufanyang_china/article/details/86707727
项目中如果要定义组变量,你可能会这样定义:
//redis的key常量定义
public static final String KEY_PRE = "api-key"; //前缀
public static final String KEY_PRE_VERIFY_IMG = "api-key-verify-img"; //图片验证码
public static final String KEY_PRE_TOKEN = "api-key-token"; //token码
public static final String KEY_PRE_SMS = "api-key-sms"; //短信码
//红橙黄绿青蓝紫七种颜色的常量定义
public static final int RED = 0;
public static final int ORANGE = 1;
public static final int YELLOW = 2;
public static final int GREEN = 3;
public static final int CYAN = 4;
public static final int BLUE = 5;
public static final int PURPLE = 6;
这个写法是我工作以来见过最常见的写法, 当然我也经常这样定义
那既然是写枚举,如果要提上面的写法呢,这是因为其实枚举提供了另外一种定义变量的方式:
Oracle官网的例子:
// int Enum Pattern - has severe problems!
public static final int SEASON_WINTER = 0;
public static final int SEASON_SPRING = 1;
public static final int SEASON_SUMMER = 2;
public static final int SEASON_FALL = 3;
.
This pattern has many problems, such as:
Not typesafe - Since a season is just an int you can pass in any other int value where a season is required, or add two seasons
together (which makes no sense).
No namespace - You must prefix constants of an int enum with a string (in this case SEASON_) to avoid collisions with other int enum
types.
Brittleness - Because int enums are compile-time constants, they are compiled into clients that use them. If a new constant is added
between two existing constants or the order is changed, clients must
be recompiled. If they are not, they will still run, but their
behavior will be undefined. Printed values are uninformative - Because
they are just ints, if you print one out all you get is a number,
which tells you nothing about what it represents, or even what type it
is.
上面是ORACLE官网针对上面写法总结的缺点, 翻译一下:
最常见的枚举写法:
public enum Color{
RED, BLACK, GREEN, BLUE, YELLOW, WHITE;
}
1
2
3
如果枚举值是int类型或String类型,会这样写:
/**
* 订单的状态
*/
public enum Status{
NEW(1), PAY(2), SUCCESS(3), FAIL(-1), REFUND(-2);
private int status;
Status(int status) {
this.status = status;
}
public int status() { return status; }
}
/**
* 订单的类型
*/
public enum Type{
CAKE("cake"), MOVIE("movie"), SHOW("show");
private String type;
Type(String type) {
this.type = type;
}
public String type() { return type; }
}
需要特别注意的是:enum 类型不支持 public 和 protected 修饰符的构造方法, 它是 私有的构造函数(private),所以你无法通过new要创建一个枚举对象
package com.zhoufy.base.enum0;
import java.util.Arrays;
/**
*
* @author zhoufy
* @date 2019年1月29日 下午1:48:35
*/
public class EnumTest {
public enum Color{
RED, BLACK, GREEN, BLUE, YELLOW, WHITE;
}
public static void main(String[] args){
for(Color c:Color.values()){
System.out.println(c);
}
//jdk8 可以这样循环遍历
Arrays.stream(Color.values()).forEach(c -> System.out.println(c));
//jdk8 可以这样循环遍历
Arrays.stream(ConstantOrder.Type.values()).forEach(c -> System.out.println(c.type()));
}
}
现在有Status.java代码如下:
/**
*
* @author zhoufy
* @date 2019年1月29日 下午1:48:35
*/
public enum Status {
NEW(1), PAY(2), SUCCESS(3), FAIL(-1), REFUND(-2);
private int status;
Status(int status) {
this.status = status;
}
public int status() { return status; }
}
通过javap命令,分析class文件的字节码信息:
"C:\Program Files\Java\jdk1.8.0_121\bin\javap.exe" -c Status
Compiled from "Status.java"
public final class Status extends java.lang.Enum<Status> {
public static final Status NEW;
public static final Status PAY;
public static final Status SUCCESS;
public static final Status FAIL;
public static final Status REFUND;
static {};
Code:
0: new #1 // class Status
3: dup
4: ldc #18 // String NEW
6: iconst_0
7: iconst_1
8: invokespecial #19 // Method "":(Ljava/lang/String;II)V
11: putstatic #23 // Field NEW:LStatus;
14: new #1 // class Status
17: dup
18: ldc #25 // String PAY
20: iconst_1
21: iconst_2
22: invokespecial #19 // Method "":(Ljava/lang/String;II)V
25: putstatic #26 // Field PAY:LStatus;
28: new #1 // class Status
31: dup
32: ldc #28 // String SUCCESS
34: iconst_2
35: iconst_3
36: invokespecial #19 // Method "":(Ljava/lang/String;II)V
39: putstatic #29 // Field SUCCESS:LStatus;
42: new #1 // class Status
45: dup
46: ldc #31 // String FAIL
48: iconst_3
49: iconst_m1
50: invokespecial #19 // Method "":(Ljava/lang/String;II)V
53: putstatic #32 // Field FAIL:LStatus;
56: new #1 // class Status
59: dup
60: ldc #34 // String REFUND
62: iconst_4
63: bipush -2
65: invokespecial #19 // Method "":(Ljava/lang/String;II)V
68: putstatic #35 // Field REFUND:LStatus;
71: iconst_5
72: anewarray #1 // class Status
75: dup
76: iconst_0
77: getstatic #23 // Field NEW:LStatus;
80: aastore
81: dup
82: iconst_1
83: getstatic #26 // Field PAY:LStatus;
86: aastore
87: dup
88: iconst_2
89: getstatic #29 // Field SUCCESS:LStatus;
92: aastore
93: dup
94: iconst_3
95: getstatic #32 // Field FAIL:LStatus;
98: aastore
99: dup
100: iconst_4
101: getstatic #35 // Field REFUND:LStatus;
104: aastore
105: putstatic #37 // Field ENUM$VALUES:[LStatus;
108: return
public int status();
Code:
0: aload_0
1: getfield #44 // Field status:I
4: ireturn
public static Status[] values();
Code:
0: getstatic #37 // Field ENUM$VALUES:[LStatus;
3: dup
4: astore_0
5: iconst_0
6: aload_0
7: arraylength
8: dup
9: istore_1
10: anewarray #1 // class Status
13: dup
14: astore_2
15: iconst_0
16: iload_1
17: invokestatic #50 // Method java/lang/System.arraycopy:(Ljava/lang/Object;ILjava/lang/Object;II)V
20: aload_2
21: areturn
public static Status valueOf(java.lang.String);
Code:
0: ldc #1 // class Status
2: aload_0
3: invokestatic #58 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
6: checkcast #1 // class Status
9: areturn
}
Process finished with exit code 0
通过分析字节码信息总结:
Status类是被final修饰,不可继承
Status类继承自java.lang.Enum类
其中NEW、PAY、SUCCESS、FAIL、REFUND以为是常量,其实是类的实例(且被static和final修饰)
运行以下Java代码会更清晰:
public class EnumTest1 {
public static void main(String[] args){
System.out.println("枚举里NEW实例的类名:"+Status.NEW.getClass().getName());
System.out.println("枚举里NEW实例的父类:"+Status.NEW.getClass().getSuperclass().getName());
}
}
输出:
枚举里NEW实例的类名:Status
枚举里NEW实例的父类:java.lang.Enum
其源码为:
package java.lang;
import java.io.Serializable;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
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修饰,出了这个包,在别的包里面是不可以new这个对象的
*/
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);
}
/**
* enum classes cannot have finalize methods.
*/
protected final void finalize() { }
/**
* prevent default deserialization
*/
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");
}
}
java.lang.Enum类是abstract类型
java.lang.Enum类的构造函数是protected类型的,受保护,所以非同包目录下的类不可以实例化该类
java.lang.Enum类提供了一系列方法:values(), ordinal() and valueOf() 等方法,这些方法都可以被Enum类的字类使用(所以在上面枚举的使用与遍历里,遍历时用到了values()方法)
在enums里顺序是很重要的(这里对应到了文章开头常量的写法一致性差,所以枚举加了序号,解决该问题),所以提供了ordinal变量以及ordinal()方法来获取每一个enum常量的index(索引值,像数组的index值一样)
如果枚举类有一个abstract方法,则它的每一个枚举类必须实现它的方法,下面是使用用例
package com.zhoufy.base.enum0.e3;
public enum Level {
HIGH {
@Override
public String asLowerCase() {
return HIGH.toString().toLowerCase();
}
},
MEDIUM {
@Override
public String asLowerCase() {
return MEDIUM.toString().toLowerCase();
}
},
LOW {
@Override
public String asLowerCase() {
return LOW.toString().toLowerCase();
}
};
public abstract String asLowerCase();
}
注意这种情况下:HIGH、MEDIUM、LOW都内部自己实现了asLowerCase()方法,那说明它们都是Level类的子类?如果是子类那它们的类名又叫什么?
到工程target目录看一下类文件:
发现除了Level.class 多了三个class文件分别是:Level$1.class,Level$2.class,Level$3.class 那这三个类文件就是对应的
就是HIGH、MEDIUM、LOW(可以反编译三个类文件查看)
也可以这样测试:
public class EnumTest1 {
public static void main(String[] args){
System.out.println(Level.HIGH.getClass().getName());
System.out.println(Level.LOW.getClass().getName());
System.out.println(Level.MEDIUM.getClass().getName());
}
}
输出:
com.zhoufy.base.enum0.e3.Level$1
com.zhoufy.base.enum0.e3.Level$3
com.zhoufy.base.enum0.e3.Level$2
这里要特别说明的是:Level$1、Level$2、Level$3匿名内部类类名的写法
有了Enum 类型,JAVA 编程更便利,定义常量的方式选择性更多,当然使用Enum会让程序常量更加容易控制,不容易出现错误,所以某些场景推荐使用Enum