枚举--让盗版美国总统wcc给你整明白哈哈
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给你整明白哈哈的更多相关文章
- 美国总统大选,黑客组织“匿名者”(Anonymous)也来凑热闹
美国总统大选,黑客组织"匿名者"(Anonymous)也来凑热闹 黑客组织"匿名者"向美国总统共和党候选人唐纳德•特朗普宣战,发誓将从4月1日开始向其发动大规模 ...
- 服务过美国总统竞选的非传统投票UI【demo已放出】
=============================== 更新:DEMO和分析已经放出,地址在这里 http://www.cnblogs.com/arfeizhang/p/faceoffde ...
- 服务过美国总统竞选的非传统投票UI [解析及DEMO]
上篇文章和大家介绍了需求情况和难点分析,大家可以看这个链接了解详细 服务过美国总统竞选的非传统投票UI =================正文开始=================== ...
- SPSS分析技术:无序多元Logistic回归模型;美国总统大选的预测历史及预测模型
SPSS分析技术:无序多元Logistic回归模型:美国总统大选的预测历史及预测模型 在介绍有序多元Logistic回归分析的理论基础时,介绍过该模型公式有一个非常重要的假设,就是自变量对因变量多个类 ...
- Python计算美国总统的身高并实现数据可视化
代码如下: import numpy as np import pandas as pd import matplotlib.pyplot as plt data=pd.read_csv('presi ...
- 极客”一词,来自于美国俚语“geek”的音译,一般理解为性格古怪的人
起源 “ 极客”一词,来自于美国俚语“ geek”的音译,一般理解为性格古怪的人.数学“极客”大多是指,并不 一定是数学专业但又对数学等技术有狂热的兴趣并投入大量时间钻研的人.又 译作“ 奇客”.以前 ...
- 法国总统放大招,用“分身术”竞选总统 全息3d 网
编辑:大熊 [摘要]法国总统采用全息技术实现"分身"演讲,可谓是一次演讲,全面覆盖! 全息3d网讯:众所周知,欧美国家的总统是通过公开竞选得到的,所以能更直接.更广泛的近距离接触民 ...
- 项目实战利用Python来看美国大选
一.项目介绍 首先分析美国总统竞选这个项目是一个烂大街的项目,但是他的确是一个适合Python新手入门的数据处理项目. 本人在大二刚刚学习了Python数据处理,学习时间不超过5个小时,但是已经可以完 ...
- 罗伯特•盖洛博士(Dr. Robert Charles Gallo)是世界著名的美国生物医学家,他以共同发现了人类免疫缺陷病毒(HIV)――这一导致获得性免疫缺陷综合症(AIDS)的致病源而闻名于世。
罗伯特•盖洛 开放分类:各国生物学家|生物学家罗伯特•盖洛博士(Dr. Robert Charles Gallo)是世界著名的美国生物医学家,他以共同发现了人类免疫缺陷病毒(HIV)――这一导致获得性 ...
随机推荐
- 阿里云centos7安装mysql8数据库
一.安装mysql 1. mysql官网查找仓库源镜像,选择downloads https://www.mysql.com/downloads/ 2. 找到社区版 3. 选择yum仓库 4. 选择对应 ...
- 【SpringBoot1.x】SpringBoot1.x 任务
SpringBoot1.x 任务 文章源码 异步任务 在 Java 应用中,绝大多数情况下都是通过同步的方式来实现交互处理的.但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使 ...
- 第一章计算机网络概述---OSI七层网络模型
局域网和广域网 局域网的简写是LAN,广域网用WAL表示.其实家庭的网络就是一个小型的局域网,一个光猫,一根网线,但是光猫无线信号不太好的话,需要在搞一个路由器. 这时候你的电脑连接到路由器上,路由器 ...
- DHCP最佳实践(三)
这是Windows DHCP最佳实践和技巧的最终指南. 如果您有任何最佳做法或技巧,请在下面的评论中发布它们. 在本指南(三)中,我将分享以下DHCP最佳实践和技巧. 仅在需要时才使用IP冲突检测 运 ...
- 彻底搞懂MySQL为什么要使用B+树索引
目录 MySQL的存储结构 表存储结构 B+树索引结构 B+树页节点结构 为什么要用B+树索引 二叉树 多叉树 B树 B+树 搞懂这个问题之前,我们首先来看一下,MySQL表的存储结构 MySQL的存 ...
- Linux Shell 编程基础详解——吐血整理,墙裂推荐!
第一部分:Linux Shell 简介 Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁.Shell 既是一种命令语言,又是一种程序设计语言. Shell 是指一种应用程序, ...
- django使用缓存之drf-extensions
使用方法:1.直接添加装饰器@cache_response该装饰器装饰的方法有两个要求: 它必须是继承了rest_framework.views.APIView的类的方法 它必须返回rest_fram ...
- LuoguP5488 差分与前缀和
题意 给定一个长为\(n\)的序列\(a\),求出其\(k\)阶差分或前缀和.结果的每一项都需要对\(1004535809\)取模. 打表找规律 先看前缀和,设\(n=5\),\(k=4\),按照阶从 ...
- Maven + springboot + mybatis 构建多模块工程
废话不说先上最终效果:创建一个空项目,再创建一个父项目用来管理各模块并维护各模块关系,简要说明如下: parent模块:主要用来管理以下各模块,和各模块涉及的jar包版本和boot项目入口级的的依赖管 ...
- Ubuntu18.04完全卸载mysql5.7并安装mysql8.0的安装方法
Ubuntu18.04版本下,如果直接输入: sudo apt install mysql-server 命令,会默认安装mysql5.7版本,安装过程并没有提示输入密码,安装完成后也无法正常登录,这 ...