你连简单的枚举类都不知道,还敢说自己会Java???滚出我的公司

枚举类型是Java 5中新增的特性,它是一种特殊的数据类型,之所以特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性。当需要定义一组常量时,强烈建议使用枚举类。

使用枚举类的条件:类的对象是有限个,确定的。例如星期类,它的对象只有星期一…星期日七个,而且是确定的,此时就可以把星期类定义为一个枚举类;又例如性别类,它的对象只有男和女两个,而且是确定的,此时同样可以把性别类定义为一个枚举类;还有诸如季节等这种类的对象是有限个,确定的都可以定义为一个枚举类。

1、枚举类的实现

在JDK1.5之前,还没有枚举类型,如果想要使用枚举类需要我们去自定义。在自定义枚举类时需要注意以下几点:

(1)枚举类对象的属性不应允许被改动,所以应该使用 private final 进行修饰;

(2)枚举类使用 private final 修饰的属性应该在构造器中为其赋值;

(3)枚举类的构造器要私有化,保证不能在类的外部创建其对象,否则就不能确定对象的个数;

(4)在枚举类内部创建的枚举类的实例(枚举)对象,要声明为:public static final。

下面就拿季节举例,来自定义一个枚举类。

public class Season {//1.声明Season对象的属性,又因为枚举类对象的属性不应允许被改动, 所以应该使用 private final修饰//枚举类的使用 private final 修饰的属性应该在构造器中为其赋值private final String seasonName;private final String seasonDesc;//2.私有化构造器,保证不能在类的外部创建其对象,否则就不能确定对象的个数private Season(String seasonName,String seasonDesc){this.seasonName=seasonName;this.seasonDesc=seasonDesc;}//3.提供当前枚举类的多个枚举对象,又因为枚举类是不可变的常量类,所以需要声明为:public static finalpublic static final Season SPRING=new Season("春天","鸟语花香");public static final Season SUMMER=new Season("夏天","夏日炎炎");public static final Season AUTUMN=new Season("秋天","秋高气爽");public static final Season WINNER=new Season("冬天","寒风瑟瑟");//其他需求1:获取枚举类对象的属性//只需要提供属性的get方法即可,但是不能提供set方法,因为枚举类是不可变的常量类,不能被修改public String getSeasonName() {return seasonName;}public String getSeasonDesc() {return seasonDesc;}//其他需求2:打印对象,提供toString方法即可@Overridepublic String toString() {return "Season{" +"seasonName='" + seasonName + '\'' +", seasonDesc='" + seasonDesc + '\'' +'}';}
}
public class SeasonTest {public static void main(String[] args) {Season spring = Season.SPRING;System.out.println(spring); //Season{seasonName='春天', seasonDesc='鸟语花香'}}
}

在JDK 1.5 中新增了enum关键字用于定义枚举类,但是在使用时需要注意以下几点:

(1)使用 enum 定义的枚举类默认继承了 java.lang.Enum类,因此不能再继承其他类;

(2)使用 enum 定义的枚举类默认使用final进行修饰,不可以被继承;(也从侧面说明了它是一个常量类)

(3)枚举类的构造器只能使用 private 权限修饰符;

(4)枚举类的所有实例必须在枚举类中显式列出,多个对象之间使用",“隔开,末尾使用”;"结束。

列出的实例系统会自动添加 public static final 进行修饰;

(5)必须在枚举类的第一行声明枚举类对象;

(6)若枚举类只有一个枚举对象, 则可以作为一种单例模式的实现方式。

下面还是使用季节举例,来自定义一个枚举类。

//使用enum关键字定义枚举类
public enum  Season2 {//1.提供当前枚举类的对象,多个对象之间使用","隔开,末尾使用";"结束//系统默认使用public static final修饰SPRING("春天","鸟语花香"),SUMMER("夏天","夏日炎炎"),AUTUMN("秋天","秋高气爽"),WINNER("冬天","寒风瑟瑟");//2.声明Season对象的属性,又因为枚举类对象的属性不应允许被改动, 所以应该使用 private final修饰private final String seasonName;private final String seasonDesc;//3.枚举类的构造器只能使用 private 权限修饰符// 私有化构造器是为了保证不能在类的外部创建其对象,否则就不能确定对象的个数private Season2(String seasonName, String seasonDesc){this.seasonName=seasonName;this.seasonDesc=seasonDesc;}//其他需求:获取枚举类对象的属性//只需要提供属性的get方法即可,但是不能提供set方法,而且也不允许提供set方法,因为枚举类是不可变的常量类,不能被修改public String getSeasonName() {return seasonName;}public String getSeasonDesc() {return seasonDesc;}
}
public class SeasonTest {public static void main(String[] args) {Season2 spring = Season2.SPRING;System.out.println(spring);//SPRING}
}

2、Enum类中的常用方法

values()方法:返回枚举类型的对象数组,该方法可以很方便地遍历所有的枚举值;

//使用方法如下:

Season2[] seasons = Season2.values();
for (int i = 0; i < seasons.length; i++) {System.out.println(seasons[i]);
}
valueOf(String str):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。
如不是,会报运行时异常:IllegalArgumentException;
//使用方法如下:
Season2 spring = Season2.valueOf("SPRING");
System.out.println(spring);//SPRING
toString():返回当前枚举类对象的名称
//使用方法如下:
Season2 spring = Season2.SPRING;
System.out.println(spring.toString());//SPRING

3、使用enum关键字定义枚举类实现接口

枚举类和普通类一样,可以实现一个或多个接口。枚举类实现接口分为两种情况:

情况一:若枚举类的所有枚举对象在调用实现的接口方法时,呈现相同的行为方式,则只要统一实现该方法即可;此时与普通类实现接口一样,没有任何区别。

public interface Show {void show();
}
//使用enum关键字定义枚举类
public enum  Season2 implements Show{//1.提供当前枚举类的对象,多个对象之间使用","隔开,末尾使用";"结束//系统默认使用public static final修饰SPRING("春天","鸟语花香"),SUMMER("夏天","夏日炎炎"),AUTUMN("秋天","秋高气爽"),WINNER("冬天","寒风瑟瑟");//2.声明Season对象的属性,又因为枚举类对象的属性不应允许被改动, 所以应该使用 private final修饰private final String seasonName;private final String seasonDesc;//3.枚举类的构造器只能使用 private 权限修饰符// 私有化构造器是为了保证不能在类的外部创建其对象,否则就不能确定对象的个数private Season2(String seasonName, String seasonDesc){this.seasonName=seasonName;this.seasonDesc=seasonDesc;}//其他需求:获取枚举类对象的属性//只需要提供属性的get方法即可,但是不能提供set方法,而且也不允许提供set方法,因为枚举类是不可变的常量类,不能被修改public String getSeasonName() {return seasonName;}public String getSeasonDesc() {return seasonDesc;}//重写show()方法,与普通类实现接口一样,没有任何区别@Overridepublic void show() {System.out.println("一年四季:春夏秋冬");}
}
public class SeasonTest {public static void main(String[] args) {Season2 spring = Season2.SPRING;spring.show();Season2 summer = Season2.SUMMER;summer.show();Season2 autumn = Season2.AUTUMN;autumn.show();Season2 winner = Season2.WINNER;winner.show();}
}

运行结果:

情况二:若枚举类的每个枚举对象在调用实现的接口方法时,需要呈现出不同的行为方式,则可以让每个枚举对象分别来实现该方法

public interface Show {void show();
}
//使用enum关键字定义枚举类
public enum  Season2 implements Show{//1.提供当前枚举类的对象,多个对象之间使用","隔开,末尾使用";"结束//系统默认使用public static final修饰SPRING("春天","鸟语花香"){//每个枚举对象分别来实现该方法@Overridepublic void show() {System.out.println("春天是一个鸟语花香的季节!");}},SUMMER("夏天","夏日炎炎"){@Overridepublic void show() {System.out.println("夏天是一个夏日炎炎的季节!");}},AUTUMN("秋天","秋高气爽"){@Overridepublic void show() {System.out.println("秋天是一个秋高气爽的季节!");}},WINNER("冬天","寒风瑟瑟"){@Overridepublic void show() {System.out.println("冬天是一个寒风瑟瑟的季节!");}};//2.声明Season对象的属性,又因为枚举类对象的属性不应允许被改动, 所以应该使用 private final修饰private final String seasonName;private final String seasonDesc;//3.枚举类的构造器只能使用 private 权限修饰符// 私有化构造器是为了保证不能在类的外部创建其对象,否则就不能确定对象的个数private Season2(String seasonName, String seasonDesc){this.seasonName=seasonName;this.seasonDesc=seasonDesc;}//其他需求:获取枚举类对象的属性//只需要提供属性的get方法即可,但是不能提供set方法,而且也不允许提供set方法,因为枚举类是不可变的常量类,不能被修改public String getSeasonName() {return seasonName;}public String getSeasonDesc() {return seasonDesc;}
}
public class SeasonTest {public static void main(String[] args) {Season2 spring = Season2.SPRING;spring.show();Season2 summer = Season2.SUMMER;summer.show();Season2 autumn = Season2.AUTUMN;autumn.show();Season2 winner = Season2.WINNER;winner.show();}
}

运行结果:

image.png

4、枚举类对switch的语句的影响

Java1.5新增enum关键字的同时,也扩大了switch的语句使用范围。Java1.5之前,switch中的值只能是简单数据类型,比如int、byte、short、char, 有了枚举类型之后,就可以使用枚举类的对象了。同时在switch表达式中使用enum定义的枚举类的对象作为表达式时, case子句可以直接使用枚举对象的名字, 无需添加枚举类作为限定。这样一来,程序的控制选择就变得更加的方便,看下面的例子:

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

运行结果:

image.png

对于这些枚举的日期,JVM都会在运行期构造成出一个简单的对象实例一一对应。这些对象都有唯一的identity,类似整型数值一样,switch语句就会根据此来identity进行执行跳转。

5、枚举类的线程安全问题

枚举类天生线程就是安全的,下面我们就来进行验证。

先写一个简单的枚举类,还是以季节类为例:

public enum Season {SPRING,SUMMER,AUTUMN,WINNER;
}

然后我们使用反编译,看看枚举类代码到底是怎么实现的,反编译后的代码内容如下:

枚举类都不知道,还敢说自己会Java?

public final class zzuli.edu.Season extends java.lang.Enum<zzuli.edu.Season> {public static final zzuli.edu.Season SPRING;public static final zzuli.edu.Season SUMMER;public static final zzuli.edu.Season AUTUMN;public static final zzuli.edu.Season WINNER;private static final zzuli.edu.Season[] $VALUES;public static zzuli.edu.Season[] values();public static zzuli.edu.Season valueOf(java.lang.String);private zzuli.edu.Season();static {};
}

由上述代码可知,每一个枚举类的枚举对象都是被public static final 进行修饰的,又因为被static修饰的属性在类加载的时候就会被加载,而且只会被加载一次,所以枚举类天生就是线程安全的。

6、枚举类实现单例模式

实现单例模式的方法有很多种,但是使用枚举类实现单例模式是最好、最安全的一种方式,这种方式也是Effective Java作者Josh Bloch 提倡的方式。因为它天生线程安全,不仅能避免多线程同步问题,而且还能防止使用反射重新创建新的对象。

使用枚举类实现单例模式非常简单,如下所示:

public enum  EnumSingle {INSTANCE;public EnumSingle getInstance(){return INSTANCE;}
}

下面使用代码进行测试,看创建的对象是否是单例:

public class Test {public static void main(String[] args) throws NoSuchMethodException {EnumSingle instance1 = EnumSingle.INSTANCE;EnumSingle instance2 = EnumSingle.INSTANCE;System.out.println(instance1==instance2);}
}

运行结果:

枚举类都不知道,还敢说自己会Java?

由运行结果可知,成功使用了单例模式。接下来测试使用反射能不能创建新的实例对象。

先来看一下使用反射创建实例对象newInstance方法的源码:

[图片上传失败…(image-8ba546-1633679059670)]

由newInstance方法的源码可知,反射在通过newInstance方法创建对象时,会先检查该类是否是枚举类,如果是,则会抛出IllegalArgumentException(“Cannot reflectively create enum objects”)异常,导致使用反射创建对象失败。下面我们就来测试一下:

先看一下枚举类的源码是有参构造函数还是无参构造函数,编译后的源码如下:

枚举类都不知道,还敢说自己会Java?

由源码可知,枚举类的构造函数为无参构造函数,下面就使用反射获取枚举类的无参构造函数,看使用反射是否能创建新的实例对象。

public class Test2 {public static void main(String[] args) throws Exception {EnumSingle instance1 = EnumSingle.INSTANCE;Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor();declaredConstructor.setAccessible(true);EnumSingle instance2 = declaredConstructor.newInstance();System.out.println(instance1);System.out.println(instance2);}
}

运行结果:

枚举类都不知道,还敢说自己会Java?

由运行抛出的异常可知,并不是我们预期的newInstance方法中的IllegalArgumentException(“Cannot reflectively create enum objects”)异常,而是NoSuchMethodException异常,说明枚举类中并没有无参构造函数,编译后的源码欺骗了我们。

接着我们通过javap反编译看一下枚举类的代码

[图片上传失败…(image-182813-1633679059670)]

public final class zzuli.edu.enumTest.EnumSingle extends java.lang.Enum<zzuli.edu.enumTest.EnumSingle> {public static final zzuli.edu.enumTest.EnumSingle INSTANCE;private static final zzuli.edu.enumTest.EnumSingle[] $VALUES;public static zzuli.edu.enumTest.EnumSingle[] values();public static zzuli.edu.enumTest.EnumSingle valueOf(java.lang.String);private zzuli.edu.enumTest.EnumSingle();public zzuli.edu.enumTest.EnumSingle getInstance();static {};
}

由上述反编译后的枚举类代码可知,反编译后的枚举类中存在的仍然是无参构造函数,说明反编译后的代码仍然骗了我们。下面我们就使用更专业的工具jad来进行反编译。

使用jad反编译后的枚举类源码如下所示:

public final class EnumSingle extends Enum
{public static EnumSingle[] values(){return (EnumSingle[])$VALUES.clone();}public static EnumSingle valueOf(String name){return (EnumSingle)Enum.valueOf(zzuli/edu/enumTest/EnumSingle, name);}private EnumSingle(String s, int i){super(s, i);}public EnumSingle getInstance(){return INSTANCE;}public static final EnumSingle INSTANCE;private static final EnumSingle $VALUES[];static {INSTANCE = new EnumSingle("INSTANCE", 0);$VALUES = (new EnumSingle[] {INSTANCE});}
}

由jad反编译后的源码可知,枚举类的构造函数为有参构造函数 EnumSingle(String s, int i),并且有两个参数。

下面我们就使用反射获取枚举类的有参构造函数,看使用反射是否能创建新的实例对象。

public class Test3 {public static void main(String[] args) throws Exception {EnumSingle instance1 = EnumSingle.INSTANCE;Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);declaredConstructor.setAccessible(true);EnumSingle instance2 = declaredConstructor.newInstance();System.out.println(instance1);System.out.println(instance2);}

运行结果:

枚举类都不知道,还敢说自己会Java?

由运行结果可知,与我们预期的异常一样,抛出了IllegalArgumentException(“Cannot reflectively create enum objects”)异常。
此时说明使用枚举类实现单例模式是十分安全的,使用反射进行暴力破解也不能创建新的对象。

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

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

相关文章

爱情第七课,被爱的秘密

我们都想要被爱。 这和懒着&#xff0c;却想有好东西吃时的心态一样。但爱的需要却和生理需要不同&#xff0c;它是一种更高级的精神需要。 在小时候&#xff0c;我们还不懂得什么是爱时&#xff0c;就接收到父母爱的信号&#xff1a;被照顾&#xff0c;被养育&#xff0c;被善…

很好用的ISO制作软件

我一直在用 很小 很实在转载于:https://blog.51cto.com/windows2008/114161

Asp.NET Core 中如何加密 Configuration ?

咨询区 Ovi&#xff1a;web.config 已进入历史垃圾堆&#xff0c;请问现在的 asp.net core 如何更好的做到将敏感信息(password,token) 存储到 configuration 中&#xff1f;换句话说&#xff1a;是否可以自动化解密 appsettings.json 中的 configuration p 节中加密的内容。回…

时隔两年 重新当码农

FIGHTING 转载于:https://www.cnblogs.com/zy691357966/p/5480494.html

早教机器人刷固件_机器人线刷包_机器人刷机包_机器人固件包_机器人救砖包 - 线刷宝ROM中心...

线刷宝用户协议本协议是用户 (自然人、法人或社会团体)与杭州登先网络科技有限公司(以下简称"登先网络")之间关于"线刷宝"软件产品(以下简称"本软件产品")的法律协议。一旦安装、复制或以其他方式使用本软件产品&#xff0c;即表示同意接受协议各…

绝对硬核!万物有“理”,科学原来如此有趣!

▲ 点击查看假如在生活中&#xff0c;你不小心将生鸡蛋和熟鸡蛋混在一起了&#xff0c;那么此时你要如何分辨&#xff0c;哪个鸡蛋是生的&#xff0c;哪个是熟的呢&#xff1f;假若你曾学过力学&#xff0c;那你一定能够轻易的分辨这个生熟问题。我们把这两个鸡蛋放在桌上&…

最近有粉丝向我请教Java泛型,于是重温了一下,希望学弟面试顺利

什么是泛型&#xff1f; 泛型&#xff08;Generic type 或者 generics&#xff09;是对 Java 语言的类型系统的一种扩展&#xff0c;以支持创建可以按类型进行参数化的类。可以把类型参数看作是使用参数化类型时指定的类型的一个占位符&#xff0c;就像方法的形式参数是运行时…

微软输入法2010下载使用-IME2010下载使用

3年前&#xff0c;写过IME2007的安装和使用&#xff0c;在Office 2010 beta开放之后&#xff0c;觉得单独把ime2010单独开放出来比较适合Office 2003/2007的用户群。 1。 依然还是和上次的IME2007提取方式一样&#xff0c;先用7-zip解压Office 2007 beta的exe文件&#xff1a;由…

理论修炼之RabbitMQ,消息队列服务的稳健者

????欢迎点赞 &#xff1a;???? 收藏 ⭐留言 ???? 如有错误敬请指正&#xff0c;赐人玫瑰&#xff0c;手留余香&#xff01;????本文作者&#xff1a;由webmote 原创&#xff0c;首发于 【掘金】????作者格言&#xff1a;生活在于折腾&#xff0c;当你不折…

为什么年龄大了近视还增加_年龄明明一样大,为什么有人长得年轻,有人显老呢?...

台湾不老男神林志颖&#xff0c;始终是十几年前演偶像剧的脸。而德云社郭德纲与他是同龄人却饱经沧桑显得更加老相。这是一件让人哭笑不得的事&#xff0c;也被很多人编成段子。那么为什么有些人看起来年轻有些人却老的很快呢&#xff1f;哪一种更长寿呢&#xff1f;接下来让我…

利用Asp.net中的AJAX制作网页上自动选取开始日期及结束日期的用户自定义控件...

前段时间用过一个酒店入住预约网站&#xff0c;当你点击"入住时间"时会悬浮出一对并列的日历&#xff0c;然后点击左边的日历就能选择入住时间&#xff0c;点击右侧的日历就能自动得到离店时间&#xff0c;当时没有太留意是怎么实现的&#xff0c;现在在做项目时&…

【00】架构型

为什么80%的码农都做不了架构师&#xff1f;>>> 1、架构型&#xff08;archetype&#xff09; 一种形式&#xff0c;所有的东西或多或少地遵守。一种形式&#xff0c;属于同一类型的类都或多或少地遵守&#xff0c;包括属性、链接、方法、插入点、交互。 2、领域无…

SQL进阶提升(疑惑篇order by)-学习sql server2005 step by step(十一)

这篇主要发出两个疑惑&#xff0c;希望有兴趣的人解答&#xff0c;谢谢&#xff01; 1.newid()疑惑 1 create table tb (aa int,bb char(1)) 2 insert tb values(1,A) 3 insert tb values(1,B) 4 insert tb values(1,C) 5 insert tb values(1,D) 6 7 insert tb value…

钟南山团队在患者粪便中检出新冠活病毒,国家卫健委回应!专家:做好这事很重要...

全世界只有3.14 % 的人关注了青少年数学之旅2月13日下午&#xff0c;在广东省人民政府新闻办召开的疫情防控新闻发布会上&#xff0c;钟南山院士团队成员、广州医科大学国家呼吸疾病重点实验室副主任、教授赵金存介绍&#xff0c;该团队在P3实验室中&#xff0c;在中山大学附属…

CSDN《某一程序员竟然吃过shi?让我们走进他的生活,揭露背后的故事》

CSDN《某一程序员竟然吃过屎&#xff1f;我们走进他的生活&#xff0c;揭露背后的故事》 ——————————接下来让我们走进他的故事 到底是什么原因让他吃屎 这是这位程序员的自曝&#xff0c;我很好奇的不是他吃过屎&#xff0c;我在好奇是啥味的~ 接下来我们开始咨询这…

专升本c语言网课听谁的好_都说塑钢泥比玻璃胶好,填缝永不变黑,师傅却说不好用,听谁的?...

新房装修&#xff0c;我一点不想再用玻璃胶来填补缝隙了。像洗手台、淋浴房、厨房水槽这些地方&#xff0c;不管用多贵多好的玻璃胶&#xff0c;最后&#xff0c;它都会变黑发霉。朋友同我说&#xff0c;可以用塑钢泥替代&#xff0c;20年不发霉~他说&#xff0c;现在很多业主家…

NetBeans 6.5 正式发布

NetBeans IDE 是一个荣获奖项的集成开发环境&#xff0c;可用于 Windows、Mac、Linux 和 Solaris。NetBeans 项目由开源 IDE 和应用程序平台组成&#xff1b;通过该 IDE 和平台&#xff0c;开发者可以使用 Java 平台以及 PHP、JavaScript 和 Ajax、Ruby 和 Ruby on Rails、Groo…

技术分享|明源云天际集成开放平台接口中心基于IdentityServer4的鉴权机制

源宝导读&#xff1a;企业数字化生态建设中为解决集成多样性和资源统一管理的痛点引入企业级网关&#xff0c;网关作为资源访问的大门&#xff0c;身份认证鉴权是其业务的重中之重&#xff0c;本文将介绍企业级网关-天际集成开放平台是如何通过IdentityServer4来做到身份认证和…

设计模式--6大原则--单一职责原则

2019独角兽企业重金招聘Python工程师标准>>> 单一职责原则&#xff08;Single Responsibility Principle&#xff09;&#xff0c;简称SRP。 定义&#xff1a; There should never be more than one reason for a class to change. 应该有且仅有一个原因引起类的变更…

法国为何是伟大数学家的摇篮?

全世界只有3.14 % 的人关注了青少年数学之旅笛卡尔、韦达、帕斯卡、费马、拉格朗日、拉普拉斯、达朗贝尔、勒让德、蒙日、彭赛列、柯西、傅里叶、庞加莱、伽罗华、格罗藤迪克…… 这些令无数大学生“闻风丧胆”的数学家&#xff0c;基本上都诞生于十七至二十世纪的法国。解析几…