枚举类

定义:

枚举是一个表示一组常量特殊的类;
所有的枚举类型隐性地继承自 java.lang.Enum。枚举实质上还是类,每个被枚举的成员是一个枚举类型的实例,他们默认都是public static final的。可以直接通过枚举类型名直接使用它们。

枚举类是一个特殊的常量类,且构造方法被默认强制是私有;
所有的枚举类型隐性地继承自 java.lang.Enum,实现接口是使其子类化的唯一办法;
二方库里可以定义枚举类型,参数可以使用枚举类型,但是接口返回值不允许使用枚举类型或者包含枚举类型的 POJO 对象。 最好出入参都不用枚举;

public enum 枚举名 {枚举元素1,枚举元素2,……};

枚举的好处:

枚举是一组固定的常量组成的类型;
在JDK1.5 之前,我们定义常量都是:public static final.... 。1.5之后,有了枚举类enum,可以**把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法,代码更加清晰简洁,使用起来很方便。**

枚举类的特点好处:

  1. 类型安全(枚举定义的都是常量,不可变对象);
  2. 易于输入(通过类名就可使用枚举对象);
  3. 代码清晰,可读性更强(将一组相关常量分组,比如运单状态、订单状态、错误响应码等等);
1.5之前自定义枚举类:
public class Season {
    //属性:
    private final String seasonName ;//季节名字
    private final String seasonDesc ;//季节描述
    //利用构造器对属性进行赋值操作:
    //构造器私有化,外界不能调用这个构造器,只能Season内部自己调用
    private Season(String seasonName,String seasonDesc){
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }
    //提供枚举类的有限的  确定的对象:
    public static final Season SPRING = new Season("春天","春暖花开");
    public static final Season SUMMER = new Season("夏天","烈日炎炎");
    public static final Season AUTUMN = new Season("秋天","硕果累累");
    public static final Season WINTER = new Season("冬天","冰天雪地");
	
	
1.5之后使用枚举关键字enum定义枚举类
public enum Season {
    //提供枚举类的有限的  确定的对象:--->enum枚举类要求对象(常量)必须放在最开始位置
    //多个对象之间用,进行连接,最后一个对象后面用;结束
    SPRING("春天","春暖花开"),
    SUMMER("夏天","烈日炎炎"),
    AUTUMN("秋天","硕果累累"),
    WINTER("冬天","冰天雪地");
    //属性:
    private final String seasonName ;//季节名字
    private final String seasonDesc ;//季节描述
    //利用构造器对属性进行赋值操作:
    //构造器私有化,外界不能调用这个构造器,只能Season内部自己调用
    private Season(String seasonName, String seasonDesc){
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }

常用方法:

public class TestSeason {
    //这是一个main方法,是程序的入口:
    public static void main(String[] args) {
        //用enum关键字创建的Season枚举类上面的父类是:java.lang.Enum,常用方法子类Season可以直接拿过来使用:
        //toString();--->获取对象的名字
        Season autumn = Season.AUTUMN;
        System.out.println(autumn/*.toString()*/);//AUTUMN
        System.out.println("--------------------");
        //values:返回枚举类对象的数组
        Season[] values = Season.values();
        for(Season s:values){
            System.out.println(s/*.toString()*/);
        }
        System.out.println("--------------------");
        //valueOf:通过对象名字获取这个枚举对象
        //注意:对象的名字必须传正确,否则抛出异常
        Season autumn1 = Season.valueOf("AUTUMN");
        System.out.println(autumn1);
    }
}

枚举使用的规范:

【参考】枚举类名带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。
说明:枚举其实就是特殊的常量类,且构造方法被默认强制是私有。
正例:枚举名字为 ProcessStatusEnum 的成员名称:SUCCESS / UNKNOWN_REASON

【推荐】使用枚举值来指代月份。如果使用数字,注意 Date,Calendar 等日期相关类的月份 month 取值范围从 0 到 11 之间。
说明:参考 JDK 原生注释,Month value is 0-based. e.g., 0 for January.
正例:Calendar.JANUARY,Calendar.FEBRUARY,Calendar.MARCH 等来指代相应月份来进行传参或比较。

【强制】所有的枚举类型字段必须要有注释,说明每个数据项的用途。

【强制】枚举 enum(括号内)的属性字段必须是私有且不可变。

**【强制】二方库里可以定义枚举类型,参数可以使用枚举类型,但是接口返回值不允许使用枚举类型或者包含枚举类型的 POJO 对象。**
出入参最好不用枚举:

  • 避免反序列化失败;序列化的时候Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过java.lang.Enum的valueOf方法来根据名字查找枚举对象。找不到序列化失败
  • 避免对外接口上下游枚举类型不一致会报非法参数异常;

RPC框架大多数会采用JSON的格式进行数据传输,也就是客户端会将返回值序列化成JSON字符串,而服务端会再将JSON字符串反序列化成一个Java对象。
而JSON在反序列化的过程中,对于一个枚举类型,会尝试调用对应的枚举类的valueOf方法来获取到对应的枚举。
从valueOf方法的实现来看,如果从枚举类中找不到对应的枚举项的时候,就会抛出IllegalArgumentException:

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);
}

**在RPC的接口中入参和出参都不要使用枚举:**
一般,我们要使用枚举都是有几个考虑:
1、枚举严格控制下游系统的传入内容,避免非法字符。
2、方便下游系统知道都可以传哪些值,不容易出错。

不可否认,使用枚举确实有一些好处,但是我不建议使用主要有以下原因:
1、如果二方库升级,并且删除了一个枚举中的部分枚举项,那么入参中使用枚举也会出现问题,调用方将无法识别该枚举项。
2、有的时候,上下游系统有多个,如C系统通过B系统间接调用A系统,A系统的参数是由C系统传过来的,B系统只是做了一个参数的转换与组装。这种情况下,一旦A系统的二方库升级,那么B和C都要同时升级,任何一个不升级都将无法兼容。会报异常非法传参异常IllegalArgumentException

建议在接口中使用字符串代替枚举,相比较于枚举这种强类型,字符串算是一种弱类型。
如果使用字符串代替RPC接口中的枚举,那么就可以避免上面我们提到的两个问题,上游系统只需要传递字符串就行了,而具体的值的合法性,只需要在A系统内自己进行校验就可以了。
为了方便调用者使用,可以使用javadoc的@see注解表明这个字符串字段的取值从那个枚举中获取。

public Class AResponse{
    private Boolean success;
    /**
    *  @see AType 
    */
    private String aType;
}

对于像阿里这种比较庞大的互联网公司,随便提供出去的一个接口,可能有上百个调用方,而接口升级也是常态,我们根本做不到每次二方库升级之后要求所有调用者跟着一起升级,这是完全不现实的,并且对于有些调用者来说,他用不到新特性,完全没必要做升级。
还有一种看起来比较特殊,但是实际上比较常见的情况,就是有的时候一个接口的声明在A包中,而一些枚举常量定义在B包中,比较常见的就是阿里的交易相关的信息,订单分很多层次,每次引入一个包的同时都需要引入几十个包。
对于调用者来说,我肯定是不希望我的系统引入太多的依赖的,一方面依赖多了会导致应用的编译过程很慢,并且很容易出现依赖冲突问题。
所以,在调用下游接口的时候,如果参数中字段的类型是枚举的话,那我没办法,必须得依赖他的二方库。但是如果不是枚举,只是一个字符串,那我就可以选择不依赖。
所以,我们在定义接口的时候,会尽量避免使用枚举这种强类型。规范中规定在返回值中不允许使用,而我自己要求更高,就是即使在接口的入参中我也很少使用。

【参考】为避免应用二方库的依赖冲突问题,二方库发布者应当遵循以下原则:
1) 。移除一切不必要的 API 和依赖,只包含 Service API、必要的领域模型对象、Utils 类、常量、枚举等。如果依赖其它二方库,尽量是 provided 引入,让二方库使用者去依赖具体版本号;无 log 具体实现,只依赖日志框架。
2) 。每个版本的变化应该被记录,二方库由谁维护,源码在哪里,都需要能方便查到。除非用户主动升级版本,否则公共二方库的行为不应该发生变化。

常见用法:

常量

在JDK1.5 之前,我们定义常量都是: public static final.... 。现在好了,有了枚举,可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法。

public enum Color {  
  RED, GREEN, BLANK, YELLOW  
} 

switch

使用枚举,能让我们的代码可读性更强。

switch语句的数据类型:
基本数据类型:byte, short, char, int
包装数据类型:Byte, Short, Character, Integer
枚举类型:Enum
字符串类型:String(Jdk 7+ 开始支持)

public enum  WeekDay {
    // 定义一周七天的枚举类型
    Monday,Tuesday, Wednesday ,Thursday,Friday,Saturday,Sunday;
}
 
class Test{
    public static void getDay(WeekDay weekDay){
        switch (weekDay){
            case Monday:
                System.out.println("Today is Monday");
                break;
            case Tuesday:
                System.out.println("Today is Tuesday");
                break;
            case Wednesday:
                System.out.println("Today is Wednesday");
                break;
            case Thursday:
                System.out.println("Today is Thursday");
                break;
            case Friday:
                System.out.println("Today is Friday");
                break;
            case Saturday:
                System.out.println("Today is Saturday");
                break;
            case Sunday:
                System.out.println("Today is Sunday");
                break;
            default:
                System.out.println("data error");
        }
    }
 
    public static void main(String[] args) {
        WeekDay sunday = WeekDay.Sunday;
        getDay(sunday);
        WeekDay friday = WeekDay.Friday;
        getDay(friday);
    }
}

向枚举中添加新方法

如果打算自定义自己的方法,那么必须在enum实例序列的最后添加一个分号。

public enum Color {  
    RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);  
    // 成员变量  
    private final String name;  
    private final 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;  
    }  

覆盖枚举的方法

下面给出一个toString()方法覆盖的例子。

public enum Color {  
    RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);  
    // 成员变量  
    private final String name;  
    private final int index;  
    // 构造方法  
    private Color(String name, int index) {  
        this.name = name;  
        this.index = index;  
    }  
    //覆盖方法  
    @Override  
    public String toString() {  
        return this.index+"_"+this.name;  
    }  
}

实现接口

所有的枚举都继承自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 final String name;  
    private final 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);  
    }  
}  

使用接口组织枚举

  • 所有的 enum 都继承自 Java.lang.Enum 类,也可以实现其他接口
  • 再接口中实现枚举,再利用向上转型
  • 不推荐,让代码更复杂了
    enum不能继承其他类,有时我们需要扩展原enum中的元素,有时我们希望使用子类将一个enum中的元素进行分组。在一个接口的内部,创建实现该接口的枚举,以此将元素进行分组,可以达到将枚举元素分类组织的目的。举例来说,假设你想用enum来表示不同类别的食物,同时还希望每个enum元素仍然保持Food类型。那么可以这样实现:
package com.zy.test;
 
import com.zy.test.Food.Appetizer;
import com.zy.test.Food.MainCourse;
 
interface Food {
    
    enum Appetizer implements Food {
        SALAD, SOUP, SPRING_ROLLS;
    }
    
    enum MainCourse implements Food {
        LASAGNE, BURRITO, PAD_THAI,
        LENTILS, HUMMOUS, VINDALOO;
    }
    
    enum Dessert implements Food {
        TIRAMISU, GELATO, BLACK_FOREST_CAKE,
        FRUIT, CREME_CARAMEL;
    }
}
 
public class InterfaceOrganizeEnum {
    public static void main(String[] args) {
        Food food = Appetizer.SALAD;
        food = MainCourse.LASAGNE;
    }
}

对于enum而言,实现接口是使其子类化的唯一办法,所以嵌入在Food中的每个enum都实现了Food接口。现在,在main方法中,可以使用实现了Food接口的enum类型,那么我们就可以将其实例向上转型为Food,所以上例中的所有东西都是Food。
当需要与一大堆类型打交道时,接口就不如enum好用了。例如,如果你想创建一个“枚举的枚举”,那么可以创建一个新的enum,然后用其实例包装Food中的每一个enum类:

package com.zy.test;
 
import java.util.Random;
 
import com.zy.test.Food.Appetizer;
import com.zy.test.Food.MainCourse;
 
interface Food {
    
    enum Appetizer implements Food {
        SALAD, SOUP, SPRING_ROLLS;
    }
    
    enum MainCourse implements Food {
        LASAGNE, BURRITO, PAD_THAI,
        LENTILS, HUMMOUS, VINDALOO;
    }
    
    enum Dessert implements Food {
        TIRAMISU, GELATO, BLACK_FOREST_CAKE,
        FRUIT, CREME_CARAMEL;
    }
}
 
public class InterfaceOrganizeEnum {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            for (Course course : Course.values()) {//先获取枚举
                Food f = course.randSelection();//获取枚举的枚举,这也是一种管理枚举的方式
                System.out.println(f);
            }
        }
    }
}
 
enum Course {
    APPETIZER(Food.Appetizer.class),
    MAINCOURSE(Food.MainCourse.class);
    
    private Food[] values;
    private Course(Class<? extends Food> kind) {
        values = kind.getEnumConstants();// 返回此枚举类的元素
    }
    public Food randSelection() {
        Random random = new Random(47);
        return values[random.nextInt(values.length)];
    }
}

在这个例子中,我们通过遍历每一个Course实例来获得“枚举的枚举”的值。下面我们还将看到另一种枚举实例的方式。此外,还有一种更简洁的管理枚举的办法,就是将一个enum嵌套在另一个enum内,就像以下代码:

package com.zy.test;
 
import java.util.Random;
 
import com.zy.test.Food.Appetizer;
import com.zy.test.Food.MainCourse;
import com.zy.test.SecurityCategory.Security;
 
public class InterfaceOrganizeEnum {
    public static void main(String[] args) {
        Random random = new Random(47);
        SecurityCategory[] valueTemp = SecurityCategory.class.getEnumConstants();
        for (int i = 0; i < 10; i++) {
            SecurityCategory category = valueTemp[random.nextInt(valueTemp.length)];
            System.out.println(category + ":" + category.randomSelection());
        }
    }
}
 
enum SecurityCategory {
    STOCK(Security.Stock.class), BOND(Security.Bond.class);
    Security[] values;
    SecurityCategory(Class<? extends Security> kind) {
        values = kind.getEnumConstants();
    }
    interface Security {
        enum Stock implements Security { SHORT, LONG, MARGIN}
        enum Bond implements Security {MUNICIPAL, JUNK}
    }
    
    public Security randomSelection() {
        Random random = new Random(50);
        return values[random.nextInt(values.length)];
    }
}

输出结果:

BOND:JUNK
STOCK:LONG
BOND:JUNK
STOCK:LONG
STOCK:LONG
BOND:JUNK
STOCK:LONG
STOCK:LONG
BOND:JUNK
BOND:JUNK

解析:Security接口的作用是将其所包含的enum组合成一个公共类型,这一点是很有必要的,然后,SecurityCategory才能将Security中的enum作为其构造器的参数使用,以便起到组织的效果。
当枚举嵌套,枚举类型比较多时怎么使用各种方式将其整齐规范的组织起来。

关于枚举集合的使用

java.util.EnumSet和java.util.EnumMap是两个枚举集合。EnumSet保证集合中的元素不重复;EnumMap中的 key是enum类型,而value则可以是任意类型。关于这个两个集合的使用,可以参考JDK文档。
EnumMap
使用EnumMap是一种非常常见的技术。EnumMap是一个高度优化和高效的map实现,它使用枚举作为key,枚举常量的数量是固定的,所以EnumMap不会像普通HashMap一样有多余的开销,从而可以提供比HashMap更快的执行效率。接下来谈一下在Java中使用EnumMap。
EnumMap是一个非常有用的类,在Java编程中经常被使用。它被设计用于必须使用enum类型作为键的情况,因为它利用enum类型本身的特殊性质,从而提供了性能优于HashMap的实现方式。
特点

EnumMap是一个非常高效的Map实现方式。它与HashMap相比,具有以下一些特点:

  1. EnumMap使用的内存开销非常小,因为它只记录枚举常量的信息;
  2. EnumMap中的元素按照键的自然顺序排序,枚举常量的顺序是其声明的顺序,这样可以进行高效的枚举遍历;
  3. 枚举常量的数量会影响EnumMap创建和使用时使用的内存,枚举常量越多,使用的内存就越大;
  4. EnumMap不能为null。

创建EnumMap
在Java中创建EnumMap时,必须指定枚举类型作为泛型类型。以下是一段示例代码:

public enum Color {
RED, GREEN, BLUE
}
EnumMap<Color, String> colorMap = new EnumMap<Color, String>(Color.class);

使用EnumMap
为了向EnumMap中添加元素,我们可以使用put()方法,如下所示:

EnumMap<Color, String> colorMap = new EnumMap<Color, String>(Color.class);
colorMap.put(Color.RED, "红色");
colorMap.put(Color.GREEN, "绿色");
colorMap.put(Color.BLUE, "蓝色");

for (Map.Entry<Color, String> entry : colorMap.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}

在Java编程中,EnumMap是一个高效和可靠的映射实现方式。它具有内存占用小、按自然顺序排序以及高效的枚举遍历等特点。当需要使用枚举类型作为键时,请尽可能地使用EnumMap,它可以为提供更快的执行速度和更好的性能。希望上述介绍能够帮助Java初学者更好地理解EnumMap的使用方式。

使用场景:

状态和中文描述映射:

工作中某些字段在数据库中存储的可能是数字代表具体中文描述,比如订单状态。
枚举的用途有建立数字标识与中文描述的映射关系,比如API调用异常枚举,支付方法枚举等映射关系;
枚举适合的场景就是定义一组有限的集合,像颜色、状态、优惠券类型、快递运输状态等等都适合使用枚举。

点击查看代码
public enum  BenlaiRespCodeEnum {
    A1000("调用后端服务成功","A1000"),
    A1001("必传参数不可为空","A1001"),
    A1002("请求时效已过期","A1002"),
    A1003("IP无效","A1003"),
    A1004("无对应服务权限","A1004"),
    A1005("流量受控","A1005"),
    A1006("数字签名无效","A1006"),
    A1007("重复请求","A1007"),
    A1008("数据解密失败","A1008"),
    A1009("目标服务异常或不可达","A1009"),
    A1099("系统异常","A1099")
    ;
    @Getter
    private final String desc;

    @Getter
    private final String code;

    private BenlaiRespCodeEnum(String desc, String code) {
        this.desc = desc;
        this.code = code;
    }

    public static BenlaiRespCodeEnum getEnumByCode(String code) {
        if (StrUtil.isBlank(code)) {
            return null;
        }
        BenlaiRespCodeEnum[] enums = BenlaiRespCodeEnum.values();
        for (BenlaiRespCodeEnum e : enums) {
            if (StrUtil.equalsIgnoreCase(e.getCode(), code)) {
                return e;
            }
        }
        return null;
    }
}

封装工具类:

 public interface GenericEnum {
        Integer getCode();
        String getDesc();
    }

 public final class EnumUtil {
        /***
         * 根据code获取枚举
         */
        public static <T extends GenericEnum> T getEnumByCode(Integer code, Class<T> enumClass) {
            return Arrays.stream(enumClass.getEnumConstants())
            .filter(t -> t.getCode().equals(code))
            .findFirst().orElse(null);
        }

        /***
         * 根据code获取描述
         */
        public static <T extends GenericEnum> String getEnumDescByCode(Integer code, Class<T> enumClass) {
            return Arrays.stream(enumClass.getEnumConstants())
                    .filter(t -> t.getCode().equals(code))
                    .map(T::getDesc).findFirst()
                    .orElse(null);
        }
    }

利用枚举对集合排序:

如下图,想根据 checkLineLocation 字段值排序这个集合,首先肯定要问排序规则是什么
image
按照这个图从左到右的顺序,左边墙、左拱腰、拱顶、右拱腰、右边墙。现在我们知道了排序规则,但是 checkLineLocation 是 String 类型的,有自己默认的排序规则,所以我们需要给这个字段建立一个映射关系,然后写另一个排序规则。那么我首先想到的就是使用枚举。

public enum LineLocationEnum {

        LOCATION_LEFT_WALL( "左边墙",1),
        LOCATION_LEFT_WAIST("左拱腰",2),
        LOCATION_TOP("拱顶",3),
        LOCATION_RIGHT_WAIST("右拱腰",4),
        LOCATION_RIGHT_WALL("右边墙",5);

        LineLocationEnum(String location,int sort){
          this.location = location;
          this.sort = sort;
        }

        @Getter
        private final String location;
        @Getter
        private final int sort;

        //根据位置获取排序值
        public static int getSortByLocation(String location) {
            return Arrays.stream(LineLocationEnum.values())
                    .filter(orderStatusEnum -> orderStatusEnum.getLocation().equals(location))
                    .map(LineLocationEnum::getSort)
                    .findFirst()
                    .orElse(0);
        }
    }

这样我们就给 checkLineLocation 关联上了排序规则 sort,并且提供了根据 location 获取排序值 sort 的方法 getSortByLocation。然后写代码测试

 public static void main(String[] args) {
        List<CheckDataReport> list = new ArrayList<CheckDataReport>(){{
                add(new CheckDataReport("拱顶"));
                add(new CheckDataReport("右边墙"));
                add(new CheckDataReport("左拱腰"));
                add(new CheckDataReport("左边墙"));
                add(new CheckDataReport("右拱腰"));
            }
        };
        //排序
        list.sort(Comparator.comparingInt(o -> LineLocationEnum.getSortByLocation(o.getCheckLineLocation())));
        System.out.println(list);
    }

这样排序问题就解决了,先使字段可以按照我们的规则排序,然后调用集合的 sort 方法,在实现的 comparator 接口匿名类中,使用我们的排序规则即可。
枚举的好处在于它可以使一些数字符号化,然后增强程序的可读性。 比如上述问题,我们也可以在程序中 new 一个 HashMap 存储对应的 sort 排序值,然后排序,但是代码繁多,可读性差。使用枚举代码量很少,而且定义好枚举,下次类似场景还可以复用。

问题:

1.枚举允许继承类吗?
枚举类使用enum定义后在编译后默认继承了java.lang.Enum类,而不是普通的继承Object类。enum声明类继承了Serializable和Comparable两个接口。且采用enum声明后,该类会被编译器加上final声明(同String),故该类是无法继承的。

所有枚举类都默认是Enum类的子类,无需我们使用extends来继承。

2.枚举允许实现接口吗?
枚举允许实现接口。因为枚举本身就是一个类,类是可以实现多个接口的。

3.枚举可以用等号比较吗?
肯定,因为在Enum类里面,已经重写了equals方法,而方法里面比较就是直接使用,来比较2个对象的。所以,你在外边直接使用也是可以的。

4.可以继承枚举吗?
当然不能呀,枚举类默认继承了java.lang.Enum类,一个类怎么能继承两个类呢?

5.枚举可以实现单例模式吗?
枚举本身就是一种对单例设计模式友好的形式,它是实现单例模式的一种很好的方式。

点击查看代码
public class InstanceDemo {

    /**
     * 构造方法私有化
     */
    private InstanceDemo(){

    }

    /**
     * 返回实例
     * @return
     */
    public static InstanceDemo getInstance() {
        return Singleton.INSTANCE.getInstance();
    }

    /**
     * 使用枚举方法实现单利模式
     */
    private enum Singleton {
        INSTANCE;

        private InstanceDemo instance;

        /**
         * JVM保证这个方法绝对只调用一次
         */
        Singleton() {
            instance = new InstanceDemo();
        }

        public InstanceDemo getInstance() {
            return instance;
        }
    }

//测试一下
    public static void main(String[] args) {
        InstanceDemo one =  InstanceDemo.getInstance();
        InstanceDemo two =  InstanceDemo.getInstance();
        System.out.println(one);
        System.out.println(two);
        System.out.println(one == two);
    }
}


创建的对象相同

csdn.test.recursion.demo.InstanceDemo@74a14482
csdn.test.recursion.demo.InstanceDemo@74a14482
true

6.当使用compareTo()比较枚举时,比较的是什么?
int compareTo(E e):比较两个枚举常量谁大谁小,其实比较的就是枚举常量在枚举类中声明的顺序;返回值可以仔细看看源码;

  1. 当使用equals()比较枚举的时候,比较的是什么?
    枚举类型的equals()方法比较的是枚举类对象的内存地址,作用与等号等价。
posted @ 2023-04-22 13:30  Jimmyhus  阅读(78)  评论(0编辑  收藏  举报