枚举的使用

简介: 枚举的使用

背景以及定义

枚举是在jdk1.5以后引入的.主要用途是:将常量组织起来,在这之前表示一组常量通常使用定义常量的方式:

public static final int RED = 1;

public static final int GREEN = 2;

public static final int BLUE = 3;

但是常量举例有不好的地方,例如:可能碰巧有一个数字1,但是他有可能误会为是RED,现在我们可以直接用枚举来进行组织,这样一来,就拥有了类型,枚举类型.而不是普通的整型1.

public enum TestEnum {

       RED, GREEN, BLUE;

}

优点:将常量组织起来统一进行管理

场景:错误状态码,消息类型,颜色的划分,状态基等...

本质:这个是java.lang.Enum的子类,也就是说,自己写的枚举类,就算没有显示的继承Enum,但是其默认继承了这个类.

使用

switch语句

public enum TestEnum {
    RED, BLACK,GREEN,WHITE;
 
    public static void main(String[] args) {
        TestEnum testEnum = TestEnum.BLACK;
        switch (testEnum) {
            case RED:
                System.out.println("red");
                break;
            case BLACK:
                System.out.println("black");
                break;
            case WHITE:
                System.out.println("white");
                break;
            case GREEN:
                System.out.println("green");
                break;
            default:
                break;
        }
    }
}

执行结果:

2.常用方法:

Enum类常用的方法

方法名称 描述
values() 以数组形式返回枚举类型所有成员
ordinal() 获取枚举成员的索引位置
valueOf() 将普通字符串转换为枚举实例
compareTo() 比较两个枚举类成员在定义时的顺序

示例1:

public enum TestEnum1 {
    RED, BLACK, GREEN, WHITE;
    public static void main(String[] args) {
        TestEnum1[] testEnum1s = TestEnum1.values();
        for(int i = 0; i < testEnum1s.length; i++) {
            System.out.println(testEnum1s[i] + " " + testEnum1s[i].ordinal());
        }
        System.out.println("======================");
        System.out.println(TestEnum1.valueOf("GREEN"));
    }
}

示例2:

public enum TestEnum2 {
    RED, BLACK, GREEN, WHITE;
 
    public static void main(String[] args) {
        //拿到枚举实例BLACK
        TestEnum2 testEnum2 = TestEnum2.BLACK;
        //拿到枚举实例RED
        TestEnum2 testEnum21 = TestEnum2.RED;
        System.out.println(testEnum2.compareTo(testEnum21));
        System.out.println(BLACK.compareTo(RED));
    }
}

刚刚说过,在Java当中枚举实际上就是一个类.所以我们在定义枚举的时候,还可以这样使用定义和枚举.

重要:枚举的构造方法默认是私有的.

public enum TestEnum3 {
    RED("red", 1), BLACK("black", 2), WHITE("white", 3), GREEN("green", 4);
    private String name;
    private int key;
 
    /**
     * 1.当枚举对象有参数后,需要提供相应的构造函数
     * 2.枚举的构造函数默认是私有的,这个一定要记住
     * @param name
     * @param key
     */
 
    private TestEnum3(String name, int key) {
        this.name = name;
        this.key = key;
    }
 
    public static TestEnum3 getEnumKey(int key) {
        for(TestEnum3 t : TestEnum3.values()) {
            if(t.key == key) {
                return t;
            }
        }
        return null;
    }
 
    public static void main(String[] args) {
        System.out.println(getEnumKey(2));
    }
}

枚举优点缺点

优点:

1.枚举常量更简单更安全

2.枚举具有内置方法,代码更优雅

缺点:

1.不可继承,无法扩展

枚举和反射

枚举是否能通过反射,拿到实例对象呢?

我们知道,在反射中,任何的一个类,哪怕是私有的,我们也可以通过反射拿到它的实例对象,那么枚举的构造方法也是私有的,我们是否可以拿到呢?接下来,我们来试验一下.

同样利用上述提供的枚举类来进行举例:

import java.lang.reflect.Constructor;
 
public enum TestEnum3 {
    RED("red", 1), BLACK("black", 2), WHITE("white", 3), GREEN("green", 4);
    private String name;
    private int key;
 
    /**
     * 1.当枚举对象有参数后,需要提供相应的构造函数
     * 2.枚举的构造函数默认是私有的,这个一定要记住
     * @param name
     * @param key
     */
 
    private TestEnum3(String name, int key) {
        this.name = name;
        this.key = key;
    }
 
    public static TestEnum3 getEnumKey(int key) {
        for(TestEnum3 t : TestEnum3.values()) {
            if(t.key == key) {
                return t;
            }
        }
        return null;
    }
 
    public static void reflectPrivateConstructor() {
        try {
            Class<?> classStudent = Class.forName("demo5.TestEnum3");
            //注意传入对应的参数,获得对应的构造方法来构造对象,当前枚举类时提供了两个参数分别是String和int
            Constructor<?> declaredConstructorStudent = classStudent.getDeclaredConstructor(String.class, int.class);
            //设置为true后可以修改访问权限
            declaredConstructorStudent.setAccessible(true);
            Object objectStudent = declaredConstructorStudent.newInstance("绿色", 666);
            TestEnum3 testEnum3 = (TestEnum3) objectStudent;
            System.out.println("获取枚举的私有构造函数" + testEnum3);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
 
    public static void main(String[] args) {
        reflectPrivateConstructor();
    }
}

输出结果:

上述异常信息是:java.lang.NoSuchMethodException:TestEnum.<init>(java.lang.String, int).

意思就是没有对应的构造方法.但是我们提供的枚举类的构造方法就是两个参数分别为String和int阿.问题出现在哪里?我们知道,所有的枚举类都是默认继承java.lang.Enum,说到继承,继承了什么? 继承了父类除构造函数外的所有东西,并且子类要帮助父类进行构造!而我们写的类,并没有帮助父类进行构造!那意思是,我们要在枚举类里面,提供super吗?不是的,枚举比较特殊,虽然我们写的是两个,但是默认他还添加了两个参数,哪两个参数呢?我们看一下Enum的源码:

protected Enum(String name, int ordinal) {
    this.name = name;
    this.ordinal = ordinal;
}

也就是说,我们自己的构造函数有两个参数一个是String一个是int,同时他默认后边还会给两个参数,一个是String, 一个是int.也就是说:我们不仅要写子类新增的两个,还应包含父类提供的两个.

这里我们正确给的是4个参数 :

public static void reflectPrivateConstructor() {
        try {
            Class<?> classStudent = Class.forName("demo5.TestEnum3");
            //注意传入对应的参数,获得对应的构造方法来构造对象,当前枚举类时提供了两个参数分别是String和int
            Constructor<?> declaredConstructorStudent = classStudent.getDeclaredConstructor(String.class, int.class, String.class, int.class);
            //设置为true后可以修改访问权限
            declaredConstructorStudent.setAccessible(true);
            //前两个是父类参数,后面两个是子类参数
            Object objectStudent = declaredConstructorStudent.newInstance("父类参数", 666, "子类参数", 888);
            TestEnum3 testEnum3 = (TestEnum3) objectStudent;
            System.out.println("获取枚举的私有构造函数" + testEnum3);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

此时的运行结果是:

他还是报错了.不过这正是我们想要的结果!此时的异常信息显示,是我的一个方法,这个方法是:

newInstance()报错了!没错,问题就是在这里,我们来看一下这个方法的源码,为什么会抛出

java.lang.IllegalArgumentException异常呢>?

源码显示:

是的,枚举在这里被过滤了,你不能通过反射获取枚举类的实例!

因此,枚举对象是非常安全的,就算通过反射,也是不可以创建一个枚举对象的.

总结

1.枚举本身就是一个类,其构造方法默认是私有的,且都是继承于java.lang.Enum

2.枚举可以避免反射和序列化问题

3.枚举的优点(简单安全,有内置方法,代码更优雅)与缺点(无法拓展)

面试问题

1.写一个单例模式

class Singleton {
    private static Object locker = new Object();
    private static volatile Singleton instance = null;
    private Singleton() {}
    public static Singleton getInstance() {
        if(instance == null) {
            synchronized (locker) {
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

2.用枚举实现一个单例模式

public enum TestEnum4 {
    INSTANCE;
    public TestEnum4 getInstance() {
        return INSTANCE;
    }
 
    public static void main(String[] args) {
        TestEnum4 singleton1 = TestEnum4.INSTANCE;
        TestEnum4 singleton2 = TestEnum4.INSTANCE;
        System.out.println("两个实例是否相同: " + (singleton1 == singleton2));
    }
}

 

相关文章
|
SQL 关系型数据库 MySQL
mysql使用default给列设置默认值的问题
mysql使用default给列设置默认值的问题
278 0
【笔记】PCIe LTSSM 状态转移
【笔记】PCIe LTSSM 状态转移
2029 0
【笔记】PCIe LTSSM 状态转移
|
12月前
|
JSON JavaScript 数据可视化
开发 CNode 技术社区智能体
CNode 社区是国内最大的 Node.js 开源技术社区,致力于 Node.js 技术研究。本文基于 Botnow 平台,通过创建 Bot、插件及工作流,详细介绍了如何利用 CNode 社区的开放 API 构建智能体,并最终发布上线,实现智能化交互功能。
|
11月前
|
存储 算法 Java
数据结构与算法学习八:前缀(波兰)表达式、中缀表达式、后缀(逆波兰)表达式的学习,中缀转后缀的两个方法,逆波兰计算器的实现
前缀(波兰)表达式、中缀表达式和后缀(逆波兰)表达式的基本概念、计算机求值方法,以及如何将中缀表达式转换为后缀表达式,并提供了相应的Java代码实现和测试结果。
1092 0
数据结构与算法学习八:前缀(波兰)表达式、中缀表达式、后缀(逆波兰)表达式的学习,中缀转后缀的两个方法,逆波兰计算器的实现
|
JavaScript
Vue2图片懒加载(vue-lazyload)
这篇文章介绍了如何在Vue 2项目中使用`vue-lazyload`插件来实现图片的懒加载功能,包括安装插件、注册配置以及在页面中的具体使用方法。
520 0
Vue2图片懒加载(vue-lazyload)
|
Java Linux Android开发
理解Android进程创建流程
理解Android进程创建流程
200 0
|
12月前
|
SQL 存储 关系型数据库
深入 MySQL 的执行计划与性能优化
深入 MySQL 的执行计划与性能优化
161 0
|
SQL 数据库 Python
SqlAlchemy 2.0 中文文档(十一)(3)
SqlAlchemy 2.0 中文文档(十一)
158 11
|
SQL 人工智能 JSON
JARVIS 变为现实:使用 Python、React 和 GPT-3 构建个人 AI 助理
JARVIS 变为现实:使用 Python、React 和 GPT-3 构建个人 AI 助理
328 0
|
网络协议 Java Linux
CentOS上安装运行XWiKi
CentOS上安装运行XWiKi
195 0