Java枚举

news2024/11/19 18:39:48

Java枚举

  • 🍺1 背景及定义🍺
  • 🧃2 使用🧃
  • 🥤3 枚举优点缺点🥤
  • 🍵4 枚举和反射🍵
    • 🍷4.1 枚举是否可以通过反射,拿到实例对象呢?🍷
  • ☕️5 总结☕️

大家好,我是晓星航。今天为大家带来的是 Java枚举 相关的讲解!😀

🍺1 背景及定义🍺

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

public static final int RED = 1;
public static final int GREEN = 2;
public static final int BLACK = 3;

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

public enum TestEnum {
    RED,BLACK,GREEN;
}

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

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

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

🧃2 使用🧃

1、switch语句

public enum TestEnum {
    RED,BLACK,GREEN,WHITE;
    public static void main(String[] args) {
        TestEnum testEnum2 = TestEnum.BLACK;
        switch (testEnum2) {
            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("black");
                break;
            default:
                break;
        }
    }
}

2、常用方法

Enum 类的常用方法

示例一:

public enum TestEnum {
    RED, BLACK, GREEN, WHITE;

    public static void main(String[] args) {
        TestEnum[] testEnum2 = TestEnum.values();
        for (int i = 0; i < testEnum2.length; i++) {
            System.out.println(testEnum2[i] + " " + testEnum2[i].ordinal());
        }
        System.out.println("=========================");
        System.out.println(TestEnum.valueOf("GREEN"));
    }
}

示例二:

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

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

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

public enum TestEnum {
    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 TestEnum (String name,int key) {
        this.name = name;
        this.key = key;
    }
    public static TestEnum getEnumKey (int key) {for (TestEnum t: TestEnum.values()) {
        if(t.key == key) {
            return t;
        }
    }
        return null;
    }
    public static void main(String[] args) {
        System.out.println(getEnumKey(2));
    }
}

🥤3 枚举优点缺点🥤

优点:

  1. 枚举常量更简单安全 。
  2. 枚举具有内置方法 ,代码更优雅

缺点:

  1. 不可继承,无法扩展

🍵4 枚举和反射🍵

🍷4.1 枚举是否可以通过反射,拿到实例对象呢?🍷

我们刚刚在反射里边看到了,任何一个类,哪怕其构造方法是私有的,我们也可以通过反射拿到他的实例对象,那么枚举的构造方法也是私有的,我们是否可以拿到呢?接下来,我们来实验一下:

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

    public enum TestEnum {
        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 TestEnum (String name,int key) {
            this.name = name;
            this.key = key;
        }
        public static TestEnum getEnumKey (int key) {
            for (TestEnum t: TestEnum.values()) {
                if(t.key == key) {
                    return t;
                }
            }
            return null;
        }
        public static void reflectPrivateConstructor() {
            try {
                Class<?> classStudent = Class.forName("TestEnum");
//注意传入对应的参数,获得对应的构造方法来构造对象,当前枚举类是提供了两个参数分别是String和int。
                Constructor<?> declaredConstructorStudent = classStudent.getDeclaredConstructor(String.class,int.class);
//设置为true后可修改访问权限
                declaredConstructorStudent.setAccessible(true);
                Object objectStudent = declaredConstructorStudent.newInstance("绿色",666);
                TestEnum testEnum = (TestEnum) objectStudent;
                System.out.println("获得枚举的私有构造函数:"+testEnum);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        public static void main(String[] args) {
            reflectPrivateConstructor();
        }
    }

输出结果:

老铁们啊,看到没有哇!异常信息是: java.lang.NoSuchMethodException: TestEnum.(java.lang.String, int) ,什么意思是:就是没有对应的构造方法,我的天呐!我们提供的枚举的构造方法就是两个参数分别是 Stringint啊!!!!问题出现在哪里呢?还记不记得我们说过的,我们所有的枚举类,都是默认继承与 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("TestEnum");
//注意传入对应的参数,获得对应的构造方法来构造对象,当前枚举类是提供了两个参数分别是String和int。
            Constructor<?> declaredConstructorStudent =
                    classStudent.getDeclaredConstructor(String.class,int.class,String.class,int.class);
//设置为true后可修改访问权限
            declaredConstructorStudent.setAccessible(true);
//后两个为子类参数,大家可以将当前枚举类的key类型改为double验证
            Object objectStudent = declaredConstructorStudent.newInstance("父类参数",666,"子类参数",888);
            TestEnum testEnum = (TestEnum) objectStudent;
            System.out.println("获得枚举的私有构造函数:"+testEnum);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

此时运行程序结果是:

java.lang.IllegalArgumentException: Cannot reflectively create enum objects
    at java.lang.reflect.Constructor.newInstance(Constructor.java:416)
    at TestEnum.reflectPrivateConstructor(TestEnum.java:46)
    at TestEnum.main(TestEnum.java:55)

嗯!没错,他还报错了,不过这次就是我想要的结果!此时的异常信息显示,是我的一个方法这个方法是:newInstance() 报错了!没错,问题就是这里,我们来看一下这个方法的源码,为什么会抛出java.lang.IllegalArgumentException: 异常呢?

源码显示:

是的,枚举在这里被过滤了,你不能通过反射获取枚举类的实例!这道题是2017年阿里巴巴曾经问到的一个问题,不看不知道,一看吓一跳!同学们记住这个坑。原版问题是:为什么枚举实现单例模式是安全的?希望大家记住这个问题!

☕️5 总结☕️

  1. 枚举本身就是一个类,其构造方法默认为私有的,且都是默认继承与 java.lang.Enum
  2. 枚举可以避免反射和序列化问题
  3. 枚举的优点和缺点

面试问题(单例模式学完后可以回顾):

1、写一个单例模式。

public class Singleton {
    private volatile static Singleton uniqueInstance;
    private Singleton() {}
    public static Singleton getInstance() {
        if (uniqueInstance == null) {
            synchronized (Singleton.class){
                if(uniqueInstance == null){//进入区域后,再检查一次,如果仍是null,才创建实例
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

2、用静态内部类实现一个单例模式

class Singleton {
    /** 私有化构造器 */
    private Singleton() {
    }
    /** 对外提供公共的访问方法 */
    public static Singleton getInstance() {
        return UserSingletonHolder.INSTANCE;
    }
    /** 写一个静态内部类,里面实例化外部类 */
    private static class UserSingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}
public class Main {
    public static void main(String[] args) {
        Singleton u1 = Singleton.getInstance();
        Singleton u2 = Singleton.getInstance();
        System.out.println("两个实例是否相同:"+ (u1==u2));
    }
}

3、用枚举实现一个单例模式

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

感谢各位读者的阅读,本文章有任何错误都可以在评论区发表你们的意见,我会对文章进行改正的。如果本文章对你有帮助请动一动你们敏捷的小手点一点赞,你的每一次鼓励都是作者创作的动力哦!😘

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/543021.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

正负压力精密控制在隐形牙齿矫正器成型机中的应用

摘要&#xff1a;真空压力热成型技术作为一种精密成型工艺在诸如隐形牙套等制作领域得到越来越多的重视&#xff0c;其主要特点是要求采用高精度的正负压力控制手段来抵消重力对软化膜变形的影响以及精密控制成型膜厚度。本文提出了相应的改进解决方案&#xff0c;通过可编程的…

Greenplum数据库系统架构

Greenplum数据库系统架构 Greenplum是一个纯软件的MPP数据库服务器&#xff0c;其系统架构专门用于管理大规模分析型数据仓库或商业智能工作负载。从技术上讲&#xff0c;MPP无共享架构是指具有多个节点的系统&#xff0c;每个节点都有自己的内存、操作系统和磁盘&#xff0c;…

【天线专题】天线(Antenna)的理解

天线,英文名称是(Antenna),本义是指像蚂蚁、蜜蜂这样的小动物头顶上的触角。天线的作用就是将调制到射频频率的数字信号或模拟信号发射到空间无线信道,或从空间无线信道接收调制在射频频率上的数字或模拟信号。简单来说就是有发射和接收电磁波的功能。 所以两只蚂…

chatgpt赋能Python-python3_5怎么用

Python 3.5是什么? Python是一种高级编程语言&#xff0c;非常流行并广泛使用。Python3.5是Python编程语言的一个较新的版本&#xff0c;它具有许多新的功能和改进。Python 3.5已包含许多流行的Python框架。它可以用于各种任务&#xff0c;例如web开发&#xff0c;数据科学&a…

StableDiffusion模型发展历史

参考资料&#xff1a; 相应的github和huggingface LDM [github] StableDiffusion v1.1 ~ v1.4 [github] [huggingface] StableDiffusion v1.5 [huggingface] [github] StableDiffusion v2 v2.1 [github] [huggingface]   首先说一下&#xff0c;这篇文章的目的是让你清晰地了…

Django SQL注入漏洞 CVE-2022-28347

漏洞简介 在Django 2.2 的 2.2.28 之前版本、3.2 的 3.2.13 之前版本和 4.0 的 4.0.4 之前版本中的 QuerySet.deexplain() 中发现了SQL注入问题。这是通过传递一个精心编制的字典&#xff08;带有字典扩展&#xff09;作为**options参数来实现的&#xff0c;并将注入负载放置在…

[golang gin框架] 36.Gin 商城项目-RESTful API 设计指南,允许Cros跨域 ,提供api接口实现前后端分离,以及JWT的使用

一.RESTful API 设计指南 请看:Restful API 的接口规范 二.Gin 中配置服务器端允许跨域 github官方地址: https://github.com/gin-contrib/cors 在 main.go文件中配置跨域请求 代码如下: 在使用cors时,需要 引入该插件,先: import ( "github.com/gin-contrib/cors" )…

Cocos CreatorXR 1.2.0 今日发布,正式支持 WebXR ,并开启 MR 之路

去年九月&#xff0c;Cocos CreatorXR v1.0.1 版本支持了 VR 内容创作&#xff0c;成为率先支持 XR 的国产引擎&#xff0c;今年三月&#xff0c;Cocos CreatorXR v1.1.0 版本实现了对 AR 内容开发的支持。在完成基本功能的建设后&#xff0c;更多开发者开始尝试使用 Cocos Cre…

Linux内核oops panic简析

源码基于&#xff1a;Linux 5.4 0. 前言 内核异常的级别大致分为三个&#xff1a;BUG、oops、panic。 BUG 是指那些不符合内核的正常设计&#xff0c;但内核能够检测出来并且对系统运行不会产生影响的问题&#xff0c;比如在原子上下文中休眠&#xff0c;在内核中用 BUG 标识。…

case

[rootes3 data]# cat case11.sh #!/bin/bash. /etc/rc.d/init.d/functionsdisable_selinux(){sed -i s/SELNUXenforcing/SELINUXdisabled/ /etc/selinux/config action "SELINUX 已经禁用&#xff0c;重启生效" }disable_firewalld(){ systemctl disable --now firew…

chatgpt赋能Python-python3_9_1怎么打开

Python 3.9.1 使用指南&#xff1a;如何打开 Python 3.9.1 Python 3.9.1 是 Python 最新版本的一个分支&#xff0c;包含各种新特性和改进&#xff0c;能够让开发者快速、简单地创建并运行 Python 程序。如果您想要使用 Python 3.9.1&#xff0c;下面是一个简单的指南&#xf…

菜单选择shell

[rootes3 data]# vi action.sh #!/bin/bash . /etc/init.d/functionsecho -en "\E[$[RANDOM%731];1m"cat <<EOF请选择&#xff1a;1) 备份数据库2)清理日志3)软件升级4)软件回滚5)删库跑路EOFecho -en \E[0mread -p "请选择上面的项对应的数字1-5&#xf…

Spring Boot-如何让你的 bean 在其他 bean 之前完成加载

今天有个小伙伴给我出了一个难题&#xff1a;在 SpringBoot 中如何让自己的某个指定的 Bean 在其他 Bean 前完成被 Spring 加载&#xff1f;我听到这个问题的第一反应是&#xff0c;为什么会有这样奇怪的需求&#xff1f;Talk is cheap&#xff0c;show me the code&#xff0c…

「读书感悟系列」原则:应对变化中的世界秩序(达利欧)

作者 | gongyouliu 编辑 | gongyouliu 最近2个月读完了达利欧的『原则2&#xff1a;应对变化中的世界秩序』&#xff0c;收获非常大。几年之前读他的『原则1&#xff1a;工作与生活』就非常喜欢&#xff0c;很有启发&#xff0c;这次读起来一如既往的喜欢。这本书利用周期的思路…

Qt推流程序自动生成网页远程查看实时视频流(视频文件/视频流/摄像头/桌面转成流媒体rtmp+hls+webrtc)

一、前言说明 推流程序将视频流推送到流媒体服务器后&#xff0c;此时就等待验证拉流播放&#xff0c;一般可以选择ffplay命令行播放或者vlc等播放器打开播放&#xff0c;也可以选择网页直接打开拉流地址播放&#xff0c;一般主流的浏览器都支持网页直接播放hls/m3u8/webrtc类…

流批一体的近实时数仓的思考与设计

摘要&#xff1a;基于对数据时间旅行的思考&#xff0c;引出了对目前三种数仓形态和两种数仓架构的思考。结合数据湖在 Flink 的应用和数据湖元数据类型的思考&#xff0c;探索了基于数据湖的 Flink SQL 流批一体的实践&#xff0c;在流批一体 SQL 表达一致、结果一致性、流批任…

【JS】1686- 重学 JavaScript API - Clipboard API

&#x1f3dd; 1. 什么是 Clipboard API 1.1 概念介绍 Clipboard API[1] 是一组 JavaScript API&#xff0c;用于在浏览器中操作剪贴板。通过 Clipboard API&#xff0c;开发者可以将文本、图片和其他数据复制到剪贴板&#xff0c;也可以从剪贴板中读取数据&#xff0c;实现复制…

OPPO解散芯片团队的真相,真的不缺钱?

OPPO解散芯片研发团队&#xff0c;各方都喜欢说OPPO不缺钱&#xff0c;解散芯片研发团队应该不是因为资金问题&#xff0c;然而仔细看看当下全球智能手机市场的表现&#xff0c;就未必会如此想了。 全球手机市场的出货量在2022年下跌了12%&#xff0c;跌穿了12亿部&#xff1b;…

【JVM】4. 虚拟机栈

文章目录 4.1. 虚拟机栈概述4.1.2. 初步印象4.1.3. 内存中的栈与堆4.1.4. 虚拟机栈基本内容Java虚拟机栈是什么&#xff1f;生命周期作用栈的特点面试题&#xff1a;开发中遇到哪些异常&#xff1f; 4.2. 栈的存储单位4.2.1. 栈中存储什么&#xff1f;4.2.2. 栈运行原理4.2.3. …

GPT理解的CV:基于Yolov5的半监督目标检测

关注并星标 从此不迷路 计算机视觉研究院 公众号ID&#xff5c;ComputerVisionGzq 学习群&#xff5c;扫码在主页获取加入方式 计算机视觉研究院专栏 作者&#xff1a;Edison_G 主要贡献是提出了一种名为“Efficient Teacher”的半监督目标检测算法。与传统的监督学习算法不同&…