类的对象只有有限个且确定的,这种类称之为枚举类;因为在jdk1.5之前没有enum关键字来定义枚举类,只能采用class定义一个类然后将类做一些修改满足对象个数有限且确定,那么这种类就是枚举类;而在jdk1.5及之后就可以直接使用enum关键字来直接定义枚举类,这种方式比之前的方式更简单方便的定义枚举类且代码更明显分辨枚举类;同时是最安全的单例模式,可以避免new、反射、反序列化等方式创建对象破解单例。
由于枚举类的对象个数有限且确定的因此对于某些类似于订单状态、登录/支付/物流方式、审批状态、异常状态码与异常说明等等情况就很适合使用枚举类;这些场景如果直接使用常量等定义值不太好表示每种值的含义,只能通过常量名或注释等方式加以标注;而定义为枚举类则可以使用枚举类属性来表示这些值的含义,使代码更能方便解读;因此当 需要定义一组常量时,强烈建议使用枚举类进行定义 。
注意:enum关键字定义的枚举类主要方法
方法名 | 作用说明 |
---|---|
valueOf(String name) | 传递枚举类对象名称name参数给静态方法valueOf,会得到与name参数匹配的枚举类对象(如:EnumAfter.valueOf(“DEVELOP”),如果name参数名称的枚举类对象不存在,则抛出 IllegalArgumentException 异常) |
public String toString() | 返回当前枚举类对象的名称,可以通过重写这个方法来使得到的结果更易读,比如重写为返回这个枚举类对象的具体属性信息(如:develop.toString()) |
public final boolean equals(Object other) | equals0方法是直接使用“==”实现的,并不是比较枚举类对象的值是否一致,它的存在是为了在Set、List 和 Map 中使用;由于equals方法是final修饰的因此该方法是不可变的,不能像toString()方法一样被重写 |
public final int hashCode() | Enum实现了hashCode()方法来和equals()保持一致;也是final修饰的也是不可变的,不能被重写 |
public final Class getDeclaringClass() | 获取枚举类对象的Class对象,和getClass()获取的结果类似 |
public final String name() | 获取当前枚举类对象在枚举类中声明的对象名称,建议优先使用未重写的toString()方法 |
public final int ordinal() | 获取枚举类对象在枚举类定义时枚举类中列举当前对象的序号(从0开始,0表示第一个列举的枚举类对象) |
public final int compareTo(E o) | 比较枚举类对象的大小,根据在定义枚举类时枚举类中声明的对象的先后顺序比较,比较结果是两个对象的 ordinal() 的值的差值(前一个对象的 ordinal() 的值减去后一个对象的 ordinal() 的值),需要比较的两个对象属于同一个枚举类的对象否则报错 |
values() | 枚举类的静态方法,返回当前枚举类的所有枚举类对象,返回值为当前枚举类对象数组 |
一般常用的是加粗的三个方法。
格式:
修饰符(如public) enum 枚举类名称{
枚举类对象列表(一定要在枚举类最前面定义),属性,构造器,方法等等
}
如:
public enum Test {
}
示例:
package com.database.pool.testpool.enumtest;
/**
* 自定义枚举类及测试
*/
public class EnumTest {
public static void main(String[] args) {
EnumBefor develop = EnumBefor.DEVELOP;
//调用toString()方法的结果
System.out.println("DEVELOP对象:"+develop);
develop.common();
System.out.println(develop.oneself());
EnumAfter finance = EnumAfter.FINANCE;
System.out.println("FINANCE对象:"+finance);
//获取名称为DEVELOP的枚举类对象
EnumAfter develop1 = EnumAfter.valueOf("DEVELOP");
EnumAfter develop2 = EnumAfter.valueOf("DEVELOP");
boolean equals = develop1.equals(develop2);
//这里的比较结果是true,但枚举类的equals是采用的==作比较,那么比较的就是Java虚拟机栈的数据,也就是develop2和develop1这两个变量的引用地址做比较,由于他们指向的堆内存中的对象是同一个,所以地址一样
System.out.println("equals比较结果:"+equals);
//获取枚举类对象的hashCode值
int hashCode = develop1.hashCode();
System.out.println("hashCode结果:"+hashCode);
//获取枚举类对象的Class对象
Class<EnumAfter> declaringClass = develop1.getDeclaringClass();
System.out.println("develop1的DeclaringClass:"+declaringClass);
Class<? extends EnumAfter> aClass = develop1.getClass();
System.out.println("develop1的Class:"+aClass);
//获取枚举类对象名称(结果是枚举类中列举的枚举类对象的名称)
System.out.println("枚举类对象名称name:"+develop1.name());
//获取枚举类对象在枚举类定义时枚举类中列举当前对象的序号(从0开始,0表示第一个列举的枚举类对象)
System.out.println("develop1枚举类对象在枚举类中声明时的序号:"+develop1.ordinal());
//比较枚举类对象的大小,根据在定义枚举类时枚举类中声明的对象的先后顺序比较,比较结果是两个对象的 ordinal() 的值的差值(前一个对象的 ordinal() 的值减去后一个对象的 ordinal() 的值)
System.out.println("compareTo比较结果:"+finance.compareTo(develop1));
//获取某个枚举类的所有枚举类对象
EnumAfter[] values = EnumAfter.values();
for (EnumAfter value : values) {
System.out.println("打印所有的枚举类对象:"+value);
value.common();
System.out.println(value.oneself());
}
/**
* 如果enum类的属性不是final修饰的,并且提供set相关属性的方法,那么就可以对这个枚举类对象设置相关值,后续的finance1拿到的对象就是修改后的值
* 此种情况对于enum来说一般是不允许的,因此enum类的属性需要定义为常量,并提供设置相关属性的构造器
*/
/*finance.setName("xwcewc");
System.out.println(finance);
EnumAfter finance1 = EnumAfter.FINANCE;
System.out.println(finance1);*/
}
}
/**
* jdk1.5之前没有enum关键字定义枚举方式
* 枚举类:类的对象只有有限个且确定的,这种类称之为枚举类
* 那么就需要做以下修改:
* 1、对象数量有限意味着不能让使用者随意创建枚举类对象,那么需要私有化枚举类构造器
* 2、对象数量有限且确定的,意味着需要列举出所有的枚举类对象并提供外部访问这些对象的入口(比如通过获取某个静态变量就是某个枚举类对象),并且由于对象是确定的意味着对象的属性不能修改,那么意味着枚举类的属性是private final修饰
*/
class EnumBefor implements EnumInterface{
/**
* 属性是确定的不能做更改,所以需要final修饰定义为常量,那么就需要进行属性赋值但不能在此处声明时赋值,否则所有对象都一样,采用private final修饰属性
* 此处不能赋值就必须拥有可以对该属性赋值的构造器否则报错
*/
private final String name;
private final String desc;
/**
* 私有化构造器,由于属性是final,那么就需要对属性进行一次赋值,如果直接在属性声明时进行赋值那么所有的对象都一样了,所以作为构造器参数传入,每个对象一旦创建就不能更改
* @param name
* @param desc
*/
private EnumBefor(String name,String desc){
this.name = name;
this.desc = desc;
}
/**
* 例举出枚举类的所有对象,并提供访问入口,使用public static final修饰
*/
public static final EnumBefor DEVELOP = new EnumBefor("开发部","程序开发"){
/**
* jdk1.5之前的方式对每个枚举类对象单独实现接口抽象方法,需要在枚举类中对所有抽象方法实现,然后每个枚举类对象单独实现自己逻辑的抽象方法,如果枚举类不对所有抽象方法实现,则会报错编译不通过
* 如果枚举类对象不单独实现则调用该方法时使用枚举类对该抽象方法的实现逻辑
* @return
*/
@Override
public String oneself() {
return "1.5之前开发部单独实现!";
}
};
public static final EnumBefor PERSONNEL = new EnumBefor("人事部","人力资源"){
@Override
public String oneself() {
return "1.5之前人事部单独实现!";
}
};
public static final EnumBefor FINANCE = new EnumBefor("财务部","资金管理"){
@Override
public String oneself() {
return "1.5之前财务部单独实现!";
}
};
public static final EnumBefor SELL = new EnumBefor("销售部","对外合作"){
@Override
public String oneself() {
return "1.5之前销售部单独实现!";
}
};
/**
* 提供访问属性的方法
* @return
*/
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
/**
* 重写toString方法
* @return
*/
@Override
public String toString() {
return "EnumBefor{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
/**
* 如果每个枚举类对象实现都是相同的逻辑,那么统一实现接口抽象方法即可
*/
@Override
public void common() {
System.out.println("1.5之前公共的抽象方法实现!");
}
/**
* 接口的所有抽象方法都必须实现,如果每个枚举类对象有单独实现抽象方法则生效单独实现的抽象方法
* @return
*/
@Override
public String oneself() {
return "1.5之前独占方法公共实现!";
}
}
/**
* 定义接口及抽象方法
*/
interface EnumInterface{
void common();
String oneself();
}
/**
* jdk1.5及之后直接采用enum关键字定义枚举类,这种方式是目前推荐和应该使用的方式
* enum关键字定义的枚举类的父类是java.lang.Enum,因此枚举类对象的toString方法的结果不再是Object类的返回地址,而是返回当前对象的名称(比如这里的:DEVELOP、PERSONNEL、FINANCE、SELL)
*/
enum EnumAfter implements EnumInterface{
/**
* enum要求将列举的枚举类对象放在类中最前面
* 相比于1.5之前定义枚举类对象做以下修改由于所有对象都是public static final修饰的,因此修饰枚举类对象的public static final需要去掉
* 并且对象类型都是当前枚举类所以EnumAfter需要去掉,且都是定义枚举类对象那么new的动作也需要去掉,并且多个对象之间用,隔开而不是;隔开且末尾用;
* 这里虽然没有new这个代码,但实际上就是调用的拥有name和desc属性入参的构造器,因此下面需要定义一个这样的构造器,否则会报错
* 本质是new创建一个对象,所以可以对每个枚举类对象单独实现方法
*/
DEVELOP("开发部","程序开发"){
/**
* 如果实现的抽象方法每个枚举类对象不一样,则在每个枚举类对象中单独实现接口抽象方法,如果抽象方法没有在枚举类中实现那么需要该抽象方法在每个枚举类对象中都单独实现否则报错编译不通过
* 如果枚举类有实现抽象方法则枚举类对象可以不实现该抽象方法
* 如果枚举类有实现且枚举类对象也有实现则枚举类对象的实现生效
* @return
*/
@Override
public String oneself() {
return "1.5之后开发部单独实现!";
}
},
PERSONNEL("人事部","人力资源"){
@Override
public String oneself() {
return "1.5之后人事部单独实现!";
}
},
FINANCE("财务部","资金管理"){
@Override
public String oneself() {
return "1.5之后财务部单独实现!";
}
},
SELL("销售部","对外合作"){
@Override
public String oneself() {
return "1.5之后销售部单独实现!";
}
};
/**
* 枚举类的所有构造器都是private的,所以这个private可以省略
* @param name
* @param desc
*/
EnumAfter(String name,String desc){
this.name = name;
this.desc = desc;
}
/**
* 属性是确定的不能做更改,所以需要final修饰定义为常量,那么就需要进行属性赋值但不能在此处声明时赋值,否则所有对象都一样,采用private final修饰属性
* 此处不能赋值就必须拥有可以对该属性赋值的构造器否则报错
* 如果不用final修饰,那么这个属性就是可变的,如果提供了set属性的方法且有人使用了set相关属性的方法那么会导致对象的值发生改变
*
*/
private final String name;
private final String desc;
/**
* 获取相关属性的方法
* @return
*/
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
/**
* 重写toString方法,否则toString方法是返回当前枚举类对象的名称
* @return
*/
@Override
public String toString() {
return "EnumAfter{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
/**
* 根据name属性获取对应的枚举类对象,一般来说应当保证枚举类对象中name属性值的唯一性,否则获取的对象可能会有问题,所以name一般应当是状态码等的值,desc可以是状态值的说明
* @param name
* @return
*/
public static EnumAfter getInstanceByName(String name){
if (null == name || name.trim().length() == 0){
return null;
}
EnumAfter[] values = EnumAfter.values();
for (EnumAfter value : values) {
if (name.equals(value.name)){
return value;
}
}
return null;
}
/**
* 如果每个枚举类对象实现都是相同的逻辑,那么统一实现接口抽象方法即可
*/
@Override
public void common() {
System.out.println("1.5之后公共的抽象方法实现!");
}
/**
* 如果枚举类对象有单独实现抽象方法,则采用单独实现的抽象方法的逻辑,枚举类没有实现的抽象方法必须每个枚举类对象单独实现否则报错编译不通过
* 和1.5之前的枚举类定义不同的是1.5之前必须枚举类实现所有抽象方法,每个枚举类对象根据需要是否单独实现抽象方法,如果枚举类和枚举类对象都实现抽象方法枚举类对象的抽象方法生效
* 1.5及之后则是要么枚举类实现要么枚举类对象实现二选一,如果两者都实现则枚举类对象实现的抽象方法逻辑生效
*
* @return
*/
@Override
public String oneself() {
return "1.5之后独占方法公共实现!";
}
}