1.为什么要有枚举

Java中的枚举其实是一种语法糖,在 JDK 1.5之后出现,用来表示固定且有限个的对象。比如一个季节类有春、夏、秋、冬四个对象;一个星期有星期一到星期日七个对象。这些明显都是固定的,且有限个。那jdk5之前如果想表示有限个对象,代码是怎么写的嘞?这样写有什么缺点,最终让jdk官方人员(大部分是美国人)看不下去了呐,我这里给大家举一个2020火热的事件,来体会,话不多说,我wcc直接上代码:

package com.huawei.subtitle.portal.com;

/*
为什么需要Enum
*/
public class AmericanFamily { public static final String bigSon = "特朗普";
public static final String smallSon = "拜登";
public static final String daughter = "奥巴马";
//public static final String daughter = "罗斯福"; public static void main(String[] args) {
String person = "拜登";
ourPresident(person);
System.out.println("--------------------");
String chineseBoyWccNickName = "特朗普";
ourPresident(chineseBoyWccNickName);
System.out.println("你看到了,我是中国小子wcc,可不是AmericanFamily成员");
} public static void ourPresident(String person) {
if (AmericanFamily.bigSon.equals(person)) {
System.out.println(" our president 川建国,2020 我挺你");
}
if (AmericanFamily.smallSon.equals(person)) {
System.out.println(" our president 拜登");
}
if (AmericanFamily.daughter.equals(person)) {
System.out.println(" our president 奥巴马");
} /*
如果上面定义了常量罗斯福,这里就要增加个if'判断',也就是就要改代码哈
if (AmericanFamily.daughter.equals(person)) {
System.out.println(" our president 奥巴马");
}
*/
}
}



大家看到了吧,结果竟然是中国小子wcc因为昵称叫特朗普,当美国人将我进行校验的时候,得到的结果是这是美国总统特朗普哈哈。于是jdk官网人员开始反思,我目前主要是看出了2点:

  • 美国人肯定不干呀最后,jdk官方这些人更是看不下去,这验证方式不对呀,这家伙都不会美国人,这也通过了校验。这不行,我得把类型也得校验住了,最好在编译期就提示这个有问题,冒牌货
  • 这个代码写的不好呀,随着时间的更替,美国总统族谱上的人越来越多,每次增加了常量,ourPresident()这个方法就要增加判断逻辑,这代码水呀,我可是jdk官方,这得改。

为了让编译器能自动检查某个值在枚举的集合内,并且,不同用途的枚举需要不同的类型来标记,不能混用,我们可以使用enum来定义枚举类,于是终于在jdk1.5版本,Enum枚举出来了,接下来我们看下怎么使用,以及它的原理。

2.Enum定义和常用方法

一般的定义形式:

在定义枚举类型时我们使用的关键字是enum,与class关键字类似,enum 枚举名{ 枚举值表 }; 在枚举值表中应罗列出所有可用值。这些值也称为枚举元素。例如:

enum Color {
RED, GREEN, BLUE;
}

枚举类型Color中定义了颜色的值,这里要注意,值一般是大写的字母,多个值之间以逗号分隔。同时我们应该知道的是枚举类型可以像类(class)类型一样,定义为一个单独的文件,当然也可以定义在其他类内部,更重要的是枚举常量在类型安全性和便捷性都很有保证,如果出现类型问题编译器也会提示我们改进,但务必记住枚举表示的类型其取值是必须有限的,也就是说每个值都是可以枚举出来的,比如上述描述的一周共有七天。

使用枚举直接用枚举名.枚举值即可,例如Color.RED

enum Color {
RED, GREEN, BLUE;
} public class Test {
// 执行输出结果
public static void main(String[] args) {
Color c1 = Color.RED;
System.out.println(c1);
}
}

2.1Enum类常见的方法:

我们使用enum关键字(注意是小写的),创建对应的枚举类时,默认是继承了Enum类的,而Enum类里自带了一些方法并且进行了实现,大家可以看下Enum源码。

(1)  ordinal()方法: 返回枚举值在枚举类种的顺序。这个顺序根据枚举值声明的顺序而定。(顺序是以0开始的,即0,1,2......)
Color.RED.ordinal(); //返回结果:0
Color.BLUE.ordinal(); //返回结果:2
(2) compareTo()方法: Enum实现了java.lang.Comparable接口,因此可以比较对象与指定对象的顺序。Enum中的compareTo返回的是两个枚举值的顺序之差。当然,前提是两个枚举值必须属于同一个枚举类,否则会抛出ClassCastException()异常。(具体可见源代码)
Color.RED.compareTo(Color.BLUE); //返回结果 -2,即(0-2)
(3) values()方法: 静态方法,返回一个包含全部枚举值的数组。
Color[] colors=Color.values();
for(Color c:colors){
System.out.print(c+",");
}//返回结果:RED,GREEN,BLUE
(4) toString()方法: 返回枚举常量的名称。和.name一样的效果
Color c=Color.RED;
System.out.println(c);//返回结果: RED
(5) valueOf()方法: 这个方法和toString方法是相对应的,返回带指定名称的指定枚举类型的枚举常量。
Color.valueOf("BLUE"); //返回结果: Color.BLUE
(6) equals()方法: 比较两个枚举类对象的引用是否相同,每一个枚举值其实都是一个类的实例,这个接下来我会讲下。

因为这些api比较简单,就不写例子一一举例了,我们把美国总统这个例子改写下,让大家体会下使用枚举后有么有将我们第一部分的问题给解决:

public enum AmericanFamily {

    teRuPu,baiDeng,obama,wccHaHa;

    public static void ourPresident(AmericanFamily person) {
//大家看这块代码是不是以后都不用管改了哈
for (AmericanFamily human:AmericanFamily.values()){
if (human.equals(person)){
System.out.println("this is our president" +" "+ person);
}
}
} public static void enumMethodTest(){
AmericanFamily teRuPu = AmericanFamily.teRuPu;
int ordinal = teRuPu.ordinal();
int i = teRuPu.compareTo(AmericanFamily.wccHaHa);
boolean same = teRuPu.equals(AmericanFamily.wccHaHa);
System.out.println(teRuPu);
System.out.println(ordinal);
System.out.println(i);
System.out.println(same);
} public static void main(String[] args) {
AmericanFamily person = AmericanFamily.teRuPu;
ourPresident(person);
System.out.println("--------------------");
String chineseBoyWccNickName = "特朗普";
System.out.println("你看到了,我是中国小子wcc,可不是AmericanFamily成员,强行使用,我异常了");
// ourPresident(chineseBoyWccNickName);编译器检查就通不过了
// 不兼容的类型: java.lang.String无法转换为com.huawei.subtitle.portal.com.AmericanFamily
ourPresident(AmericanFamily.wccHaHa);
System.out.println("--------------------");
enumMethodTest();
}
}

3.枚举的原理

我们来看下,我们简单的创建一个枚举类,Java底层为我们做了什么,为了搞反编译,试了很多,因为Java的发展,很多反编译软件可能不支持Java的新的语法特性,或者支持不友好:

这里仅附上我使用的反编译工具下载地址:jad反编译下载


**直接使用jad命令反编译,具体如下:**
- 1. 打开命令,将当前位置指向xjad所在目录
- 2. 使用Jad -sjava xxx.class
即可在同目录下生成xxx.java的反编译后的文件



public enum Day {
MONDAY, TUESDAY, WEDNESDAY,THURSDAY, FRIDAY, SATURDAY, SUNDAY;
}

这里直接展示反编译的结果,具体使用不同工具反编译的差别,请查看我另一篇文章--暂时起名反编译工具使用吧嘻嘻。话不多说,附上反编译后代码,从反编译代码中看这个类的真实样子:

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: Day.java package com.huawei.subtitle.portal; public final class Day extends Enum { public static Day[] values() {
return (Day[])$VALUES.clone();
} public static Day valueOf(String s) {
return (Day)Enum.valueOf(Day, s);
} private Day(String s, int i) {
super(s, i);
} public static final Day MONDAY;
public static final Day TUESDAY;
public static final Day WEDNESDAY;
public static final Day THURSDAY;
public static final Day FRIDAY;
public static final Day SATURDAY;
public static final Day SUNDAY;
private static final Day $VALUES[]; static {
MONDAY = new Day("MONDAY", 0);
TUESDAY = new Day("TUESDAY", 1);
WEDNESDAY = new Day("WEDNESDAY", 2);
THURSDAY = new Day("THURSDAY", 3);
FRIDAY = new Day("FRIDAY", 4);
SATURDAY = new Day("SATURDAY", 5);
SUNDAY = new Day("SUNDAY", 6);
$VALUES = (new Day[] {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
});
}
}

首先我们从第一行

  • 可以知道enum定义的枚举本身也是一个class,和我们常见的类使用上没有差别
  • 这个类默认继承了Enum接口,由于Java的单继承,所以enum不能再继承别的类,同时由于继承,也就有了Enum接口中的已经实现的方法。
  • 这个类被final修饰,也就是太监类,没有子类,不允许继承

接下来我们看成员变量和static代码块这块:

  • static静态代码块的特性大家应该都知道哈,这里会随着类的加载使用而执行,且仅执行一次,为我们创建了Day这个对象的实例,共计7个,每一个枚举值都是一个对象的实例,这点要时刻记住,后面会用到
  • 通过私有构造器,并调用父构造器初始化name和ordinal属性,name值默认就是我们定义的枚举名称的字符串形式,如"Monday"
  • VALUS常量是一个Day类型数组,也是在静态代码块中进行的初始化,数组中的值为对应的枚举类对象

到此我们也就明白了,使用关键字enum定义的枚举类型,在编译期后,也将转换成为一个实实在在的类,而在该类中,会存在每个在枚举类型中定义好变量的对应实例对象,如上述的MONDAY枚举类型对应public static final Day MONDAY;,同时编译器会为该类创建两个方法,分别是values()和valueOf()。ok~,到此相信我们对枚举的实现原理也比较清晰啦。接下来我们来看下枚举的高级用法:

4.枚举的高级用法

向enum类添加方法与自定义属性和构造函数 重新定义一个日期枚举类,带有desc成员变量描述该日期的对于中文描述,同时定义一个getDesc方法,返回中文描述内容,自定义私有构造函数,在声明枚举实例时传入对应的中文描述,代码如下:

public enum DayDesc {
MONDAY("星期一", 1),
TUESDAY("星期二", 2),
WEDNESDAY("星期三", 3),
THURSDAY("星期四", 4),
FRIDAY("星期五", 5),
SATURDAY("星期六", 6),
SUNDAY("星期日", 7); private String desc;//文字描述
private Integer code; //对应的代码 /**
* 私有构造,防止被外部调用,默认就是私有构造,且只允许私有构造
*
* @param desc
*/
private DayDesc(String desc, Integer code) {
this.desc = desc;
this.code = code;
} /**
* 定义方法,返回描述,跟常规类的定义没区别
*
* @return
*/
public String getDesc() {
return desc;
} /**
* 定义方法,返回代码,跟常规类的定义没区别
*
* @return
*/
public int getCode() {
return code;
} public static void main(String[] args) {
for (DayDesc day : DayDesc.values()) {
System.out.println("name:" + day.name() +
",desc:" + day.getDesc());
}
}
}

输出结果:


枚举类中定义抽象方法,由于每个枚举值都是一个对象实例,在枚举类中定义的抽象方法,会由每个对应的枚举对象去实现,在第一个枚举值后面打上{},报红后alt+enter即可实现方法。先上代码,给大家看看

public enum Human {
MEN{
@Override
public void say() {
System.out.println("男:我可以约你出来看月亮么");
}
},WOMEN {
@Override
public void say() {
System.out.println("女:好哒,月亮不睡我们不睡");
}
}; public abstract void say(); public static void main(String[] args) {
Human.MEN.say();
Human.WOMEN.say();
}
}

探索:可能大家还是有疑问哈,这个就简单的使用Idea内嵌的反编译工具叫啥flower给大家看下,大概就知道原理了:

首先执行javac Human.java 对该类进行编译,我们会发现得到了三个class文件:



直接用Idea内置的反编译,默认已经嵌入,大家什么都不用干,直接打开.class文件即可。代码如下:

package com.huawei.subtitle.portal;

public enum Human {
MEN {
public void say() {
System.out.println("男:我可以约你出来看月亮么");
}
},
WOMEN {
public void say() {
System.out.println("女:好哒,月亮不睡我们不睡");
}
}; private Human() {
} public abstract void say(); public static void main(String[] var0) {
MEN.say();
WOMEN.say();
}
}
---
enum Human$1 {
Human$1() {
} public void say() {
System.out.println("男:我可以约你出来看月亮么");
}
} ---
enum Human$2 {
Human$2() {
} public void say() {
System.out.println("女:好哒,月亮不睡我们不睡");
}
}

首先我们看到由于我们定义了抽象方法say(),所以Human这个类是抽象的,要不违背了Java基础有抽象方法的类肯定是抽象类,然后两个枚举对象,分别实现了Human这个类并重写了say()方法,看到这里大家应该懂了吧。好吧,今天就写到这里吧,我有点累了,准备去公司楼下健身房锻炼下。

有什么问题留言即可,基本每天都会登录我的博客。谁让我笨但是勤劳嘞嘻嘻

枚举--让盗版美国总统wcc给你整明白哈哈的更多相关文章

  1. 美国总统大选,黑客组织“匿名者”(Anonymous)也来凑热闹

    美国总统大选,黑客组织"匿名者"(Anonymous)也来凑热闹 黑客组织"匿名者"向美国总统共和党候选人唐纳德•特朗普宣战,发誓将从4月1日开始向其发动大规模 ...

  2. 服务过美国总统竞选的非传统投票UI【demo已放出】

    =============================== 更新:DEMO和分析已经放出,地址在这里   http://www.cnblogs.com/arfeizhang/p/faceoffde ...

  3. 服务过美国总统竞选的非传统投票UI [解析及DEMO]

    上篇文章和大家介绍了需求情况和难点分析,大家可以看这个链接了解详细    服务过美国总统竞选的非传统投票UI      =================正文开始=================== ...

  4. SPSS分析技术:无序多元Logistic回归模型;美国总统大选的预测历史及预测模型

    SPSS分析技术:无序多元Logistic回归模型:美国总统大选的预测历史及预测模型 在介绍有序多元Logistic回归分析的理论基础时,介绍过该模型公式有一个非常重要的假设,就是自变量对因变量多个类 ...

  5. Python计算美国总统的身高并实现数据可视化

    代码如下: import numpy as np import pandas as pd import matplotlib.pyplot as plt data=pd.read_csv('presi ...

  6. 极客”一词,来自于美国俚语“geek”的音译,一般理解为性格古怪的人

    起源 “ 极客”一词,来自于美国俚语“ geek”的音译,一般理解为性格古怪的人.数学“极客”大多是指,并不 一定是数学专业但又对数学等技术有狂热的兴趣并投入大量时间钻研的人.又 译作“ 奇客”.以前 ...

  7. 法国总统放大招,用“分身术”竞选总统 全息3d 网

    编辑:大熊 [摘要]法国总统采用全息技术实现"分身"演讲,可谓是一次演讲,全面覆盖! 全息3d网讯:众所周知,欧美国家的总统是通过公开竞选得到的,所以能更直接.更广泛的近距离接触民 ...

  8. 项目实战利用Python来看美国大选

    一.项目介绍 首先分析美国总统竞选这个项目是一个烂大街的项目,但是他的确是一个适合Python新手入门的数据处理项目. 本人在大二刚刚学习了Python数据处理,学习时间不超过5个小时,但是已经可以完 ...

  9. 罗伯特•盖洛博士(Dr. Robert Charles Gallo)是世界著名的美国生物医学家,他以共同发现了人类免疫缺陷病毒(HIV)――这一导致获得性免疫缺陷综合症(AIDS)的致病源而闻名于世。

    罗伯特•盖洛 开放分类:各国生物学家|生物学家罗伯特•盖洛博士(Dr. Robert Charles Gallo)是世界著名的美国生物医学家,他以共同发现了人类免疫缺陷病毒(HIV)――这一导致获得性 ...

随机推荐

  1. linux系统搭建ftp服务器及创建用户使用

    linux 系统下搭建ftp服务器 ftp是什么 FTP是 File Transfer Protocol 文件传输协议的英文名称,用于在Internet上控制文件的双向传输. 同时它也是一个应用程序. ...

  2. Java微服务 vs Go微服务,究竟谁更强!?

    前言 Java微服务能像Go微服务一样快吗? 这是我最近一直在思索地一个问题. 去年8月份的the Oracle Groundbreakers Tour 2020 LATAM大会上,Mark Nels ...

  3. 细说 js 的7种继承方式

    在这之前,先搞清楚下面这个问题: function Father(){} Father.prototype.name = 'father'; Father.prototype.children = [ ...

  4. 图片质量评估论文 | 无监督SER-FIQ | CVPR2020

    文章转自:同作者微信公主号[机器学习炼丹术].欢迎交流,共同进步. 论文名称:SER-FIQ: Unsupervised Estimation of Face Image Quality Based ...

  5. 【C++】《C++ Primer 》第八章

    第八章 IO库 一.IO类 1. 标准库定义的IO类型 头文件 作用 类型 iostream 从标准流中读写数据 istream, wistream 从流读取数据 ostream, wostream ...

  6. Invalid bound statement (not found): com.xxx.xxx.dao.ShopMapper.insertShop

    mybatis在编写完SQL,进行测试的时候出现了错误,显示 org.apache.ibatis.binding.BindingException: Invalid bound statement ( ...

  7. 四:WEB源码扩展

    前言:WEB源码在安全测试中是非常重要的信息来源,可以用来进行代码审计漏洞也可以用来做信息突破口,其中WEB源码有很多技术需要简明分析,获取某ASP源码后就可以采用默认数据库下载为突破,获取某其他脚本 ...

  8. Loadrunner参数化数据配置与更新方式

    之前遇到过一种情况,对脚本进行并发测试时,脚本没有报错,但是有丢失的事物,与开发配合检查确定不是代码的问题,然后检查脚本,更换参数化数据配置与更新方式,问题解决.现在对参数化数据配置和更新方式进行总结 ...

  9. M8 E147 不可能为条目*确立账户

    今天用BAPI做发票校验, BAPI_INCOMINGINVOICE_CREATE这个函数使用都正常,可是突然就无法做发票检验了 报了个错误,"不可能为条目BOXT TR 确立账户" ...

  10. Kubernetes集群管理工具kubectl命令技巧大全

    一. kubectl概述 Kubectl是用于控制Kubernetes集群的命令行工具,通过kubectl能够对集群本身进行管理,并能够在集群上进行容器化应用的安装部署. kubectl命令的语法如下 ...