【Java】【重要特性】详解枚举
一、什么是枚举
枚举在Java中使用Enum表示,它是Java 5中的新增的关键字,枚举类型作为Java 5中新增特性的一部分,它是一种特殊的类型,之所以特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁性、安全性和便携性。
枚举所具有的特性如下:
- 枚举类可以实现一个或多个接口,使用enum定义的枚举类默认继承了java.lang.Enum抽象类,而不是默认继承Object类,因此枚举类就不能继承其他父类了,其中Enum类实现了Serialazable和Comparable接口;使用enum定义、非抽象的枚举类默认会使用final修饰,因此定义的枚举类不能被继承不能被派生出子类了。
- 枚举类的构造器只能使用private访问权限控制,如果省略了构造器的访问权限,会默认使用private修饰。
- 枚举类的所有实例(也就是枚举值)必须在枚举类里面的第一行显式列出来,否则这个枚举类将永远无法产生实例,列出这些实例时,会自动添加public static final修饰符,无需我们显式添加。
- 枚举类提供了一个valus()方法,可以很方便遍历所有的枚举值。
二、为什么出现枚举类型
我们拿常量出来说明,如果使用枚举方式定义常量,可以使代码更具有可读性,允许进行编译时检查,预先记录可接受值的列表,并避免由于传入无效值而引起的意外行为。例如下面定义一周的星期枚举常量,都统一放到一个枚举类型里面,非常方便,简洁。
public enum Week {
//这些都是枚举实例,默认都是public static final修饰的,直接拿来用就可以了
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
STATURDAY,
SUNDAY;
}
三、枚举的基本使用
(1)枚举常量使用
常量一般需要final修饰,程序里实现常量有三种方式:接口常量、类常量和枚举常量,类常量做分类经常在类内部定义静态内部类把不同功能的常量放在一个静态内部类中(这样也很方便使用),前面两种就不说了,很简单普遍,重点说一下枚举常量。
定义一个枚举类:
/**
* @author huahua
* @date 2020/2/20
* @desc 枚举常量使用
*/
public enum StateType {
/**1.以下OK,BAD_REQUEST,UNAUTHORIZED, FORBIDDEN都是该枚举类StateType的实例,需要写在枚举类前面,里面的参数分别对应下面定义构造器的参数,默认是public static final修饰,都是已经实例化好了的,可以直接拿来使用
*/
OK(200,"OK"),
BAD_REQUEST(400,"bad request"),
UNAUTHORIZED(401,"unauthorized"),
FORBIDDEN(403,"forbidden");
private int code;
private String value = null;
private StateType(int code, String value) {
this.code = code;
this.value = value;
}
public String value() {
return this.value;
}
public int getCode() {
return code;
}
//该方法是对枚举类的基本判断,使用到values()方法
public static Boolean isValidateStateType(String... stateType) {
for (int i = 0; i < stateType.length; i++) {
StateType [] value = StateType.values();
boolean falg = false;
for(StateType type : value) {
if(type.value.equals(stateType[i])) {
falg = true;
}
}
if(!falg) {
return falg;
}
}
return true;
}
}
测试:
public class test {
public static void main(String[] args) {
System.out.println(StateType.isValidateStateType("bad request"));
System.out.println(StateType.BAD_REQUEST.value());
}
}
输出结果:
true
bad request
(2)用于switch语句
StateType stateType=StateType.BAD_REQUEST;
switch (stateType){
case BAD_REQUEST:
System.out.println("坏请求");
break;
case OK:
System.out.println("状态可以");
break;
case NOT_ALLOWED:
System.out.println("权限不允许");
break;
default:
System.out.println("状态失败");
}
Switch case分支条件语句,执行时,一定先进行匹配,匹配成功返回当前case值,再根据是否有break,判断是否继续输出,或是跳出判断。
变量类型可以是enum,byte、short、int、char和string(1.7加入的),只能是常量或者是字面常量。可以包含defaul分支,没有匹配到时才会执行,可以放在任何位置。default不需要break语句。
(3)枚举类实现单例
使用枚举实现单例是一种非常好的方式,我们对比一下适合高并发的“双重检验锁”单例和枚举单例
1)Java实现的“双重校验锁”单例:
/**
* @author huahua
* @date 2020/2/20
* @desc 双重校验锁
*/
public class SingleTon2java {
//无参构造器私有化
private SingleTon2java(){
}
//单个实例
private static volatile SingleTon2java singleTon=null;
//提供静态方法创建并获取实例
public static SingleTon2java getInstance(){
//第一次检验
if (singleTon==null){
//同步,只能有一个线程操作SingleTon2java这个类对象
synchronized (SingleTon2java.class){
//第二次校验
if(singleTon==null){
singleTon=new SingleTon2java();
}
}
}
return singleTon;
}
}
2)枚举实现的单例
/**
* @author huahua
* @date 2020/2/20
* @desc 枚举类实现的单例
*/
public enum SingleTonEnum {
//单例的实例,直接使用INSTANCE就可以了
INSTANCE;
}
枚举实现单例简直是简单粗暴,枚举实现的单例可以轻松解决两个问题
- 线程安全问题,因为Java虚拟机在加载枚举类的时候,会使用类加载器的loadClass方法,这个方法使用同步代码块来保证线程安全
- 避免反序列化破坏单例,因为枚举的序列化并不通过反射来实现。
另外使用枚举还可以跟数据库的字段属性交互和实现接口等等,还有很多种用法。
四、枚举的基本实现原理
实际上我们使用enum关键字创建枚举并编译后,编译器会为我们生成一个相应的类,该类继承的Enum类,也就是enum枚举类型实际上也是一个类类型,只不过该类继承了Enum类而已。
我们可以通过反编译Day.class
//Day.class
//枚举类型,使用关键字enum
enum Day {
MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
反编译后得到:
//反编译Day.class
final class Day extends Enum
{
//编译器为我们添加的静态的values()方法
public static Day[] values()
{
return (Day[])$VALUES.clone();
}
//编译器为我们添加的静态的valueOf()方法,注意间接调用了Enum也类的valueOf方法
public static Day valueOf(String s)
{
return (Day)Enum.valueOf(com/huahua/enumdemo/Day, s);
}
//私有构造函数
private Day(String s, int i)
{
super(s, i);
}
//前面定义的7种枚举实例
public static final Day MONDAY;
public static final Day TUESDAY;
public static final Day WEDNESDAY;
public static final Day THURSDAY;
public static final Day FRIDAY;
public static final Day SATURDAY;
public static final Day SUNDAY;
private static final Day $VALUES[];
static
{
//实例化枚举实例
MONDAY = new Day("MONDAY", 0);
TUESDAY = new Day("TUESDAY", 1);
WEDNESDAY = new Day("WEDNESDAY", 2);
THURSDAY = new Day("THURSDAY", 3);
FRIDAY = new Day("FRIDAY", 4);
SATURDAY = new Day("SATURDAY", 5);
SUNDAY = new Day("SUNDAY", 6);
$VALUES = (new Day[] {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
});
}
}
- 从反编译的代码可以看出编译器确实帮助我们生成了一个Day类(注意该类是final类型的,将无法被继承)而且该类继承自java.lang.Enum类,该类是一个抽象类。
- 除此之外,编译器还帮助我们生成了7个Day类型的实例对象分别对应枚举中定义的7个日期,这也充分说明了我们前面使用关键字enum定义的Day类型中的每种日期枚举常量也是实实在在的Day实例对象,只不过代表的内容不一样而已。注意编译器还为我们生成了两个静态方法,分别是values()和 valueOf()。
- 到此我们也就明白了,使用关键字enum定义的枚举类型,在编译期后,也将转换成为一个实实在在的类,而在该类中,会存在每个在枚举类型中定义好变量的对应实例对象,如上述的MONDAY枚举类型对应public static final Day MONDAY;,同时编译器会为该类创建两个方法,分别是values()和valueOf()。到此相信我们对枚举的实现原理也比较清晰。
Java.lang.Enum抽象类的主要方法:
五、枚举的更深入使用
1.使用枚举实现一些设计模式,如单例模式,策略模式等
2.EnumSet、EnumMap等的实现
3.Enum类型的JSON表示形式等
4.Java8在枚举中的应用等
- 点赞
- 收藏
- 关注作者
评论(0)