枚举的定义

public enum Color {
Red,Blue,Green;
}

枚举的使用

Color red = Color.Red;//枚举的其中一个类型
Color[] values = Color.values();//获取所有的枚举类型
String name = red.name();//可以获得枚举值的名称
int ordinal = red.ordinal();//可以获得枚举值的编号

原理

那我们定义枚举类型后,到底发生了什么呢?我们对枚举的实现原理进行探究。

我们来解析下Color.class文件,命令javap Color

public final class Color extends java.lang.Enum<Color> {
public static final Color Red;
public static final Color Blue;
public static final Color Green;
public static final Color[] $VALUES;
public static Color[] values();
public static Color valueOf(java.lang.String);
static {};
}

从解析后的文件中我们可以知道

  1. 枚举类是final的,不能被继承
  2. 枚举类在经过编译后生成一个继承java.lang.Enum的类Color
  3. 编译后的枚举值,是该类的Color类型的成员变量,如Red、Bule、Green,并且是final static修饰
  4. 枚举编译后的类添加了静态的values()valueOf(java.lang.String)方法
  5. 静态代码块static {}

进一步细化Color.class

命令javap -c Color

public final class Color extends java.lang.Enum<Color> {
public static final Color Red;
public static final Color Blue;
public static final Color Green;
public static Color[] values();
Code:
0: getstatic #1 // Field $VALUES:[LColor;
3: invokevirtual #2 // Method "[LColor;".clone:()Ljava/lang/Object;
6: checkcast #3 // class "[LColor;"
9: areturn public static Color valueOf(java.lang.String);
Code:
0: ldc #4 // class Color
2: aload_0
3: invokestatic #5 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljav
a/lang/Enum;
6: checkcast #4 // class Color
9: areturn static {};
Code:
0: new #4 // class Color
3: dup
4: ldc #7 // String Red
6: iconst_0
7: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
10: putstatic #9 // Field Red:LColor;
13: new #4 // class Color
16: dup
17: ldc #10 // String Blue
19: iconst_1
20: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
23: putstatic #11 // Field Blue:LColor;
26: new #4 // class Color
29: dup
30: ldc #12 // String Green
32: iconst_2
33: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
36: putstatic #13 // Field Green:LColor; 39: iconst_3
40: anewarray #4 // class Color
43: dup 44: iconst_0
45: getstatic #9 // Field Red:LColor;
48: aastore
49: dup
50: iconst_1
51: getstatic #11 // Field Blue:LColor;
54: aastore
55: dup
56: iconst_2
57: getstatic #13 // Field Green:LColor;
60: aastore
61: putstatic #1 // Field $VALUES:[LColor;
64: return
}

还原后的代码如下:

public final class Color extends java.lang.Enum<Color> {
//定义的枚举成员
public static final Color Red;
public static final Color Blue;
public static final Color Green;
//编译器自动生成的 javap -c 还查不出来,疑惑
public static final /* synthetic */ Color[] $VALUES;//编译器自动生成的
public static Color[] values(){
/**
* 0: getstatic #1 // Field $VALUES:[LColor;
* 3: invokevirtual #2 // Method "[LColor;".clone:()Ljava/lang/Object;
* 6: checkcast #3 // class "[LColor;"
* 9: areturn
*/
return $VALUES.clone();
}
public static Color valueOf(java.lang.String s){
/**
* 0: ldc #4 // class Color
* 2: aload_0
* 3: invokestatic #5 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljav
* a/lang/Enum;
* 6: checkcast #4 // class Color
* 9: areturn
*/
return Enum.valueOf(Color.class,s);
}
protected Color(String name, int ordinal) {
super(name, ordinal);
}
static {
/**
* 0: new #4 // class Color
* 3: dup
* 4: ldc #7 // String Red
* 6: iconst_0
* 7: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
* 10: putstatic #9 // Field Red:LColor;
*/
Red=new Color("Red",0);
/**
* 13: new #4 // class Color
* 16: dup
* 17: ldc #10 // String Blue
* 19: iconst_1
* 20: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
* 23: putstatic #11 // Field Blue:LColor;
*/
Blue=new Color("Blue",1);
/**
* 26: new #4 // class Color
* 29: dup
* 30: ldc #12 // String Green
* 32: iconst_2
* 33: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
* 36: putstatic #13 // Field Green:LColor;
*/
Green=new Color("Green",2);
/**
* 39: iconst_3
* 40: anewarray #4 // class Color
* 43: dup
* 44: iconst_0
* 45: getstatic #9 // Field Red:LColor;
* 48: aastore
* 49: dup
* 50: iconst_1
* 51: getstatic #11 // Field Blue:LColor;
* 54: aastore
* 55: dup
* 56: iconst_2
* 57: getstatic #13 // Field Green:LColor;
* 60: aastore
* 61: putstatic #1 // Field $VALUES:[LColor;
*/
$VALUES=new Color[]{Red,Blue,Green};
};
}

通过上面还原后的代码可知,在类的static代码块中编译器帮我们生成枚举中的每个成员,实际上就是生成对象并赋值给静态变量。

枚举的扩展

单例模式

枚举其实就是编译帮我们在静态代码块中创建一个或多个枚举成员对象,如果我们只定义一个枚举成员,这样就是一个单例对象

public enum Singleton {
INSTANCE;
public void doSometing(){
System.out.println("doing");
}
}

代码如此简单,不仅如此,这样的方式还可以解决反射攻击反序列化导致的单例失败的问题。

避免反射攻击

在获取实例时进行了枚举判断,如果是枚举类型的则直接抛出异常。

Jdk1.8 Class.clss 文件的416..417

public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException{
....
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
.....
return inst;
}

反序列化

枚举的序列时仅降枚举对象的name属性输出到结果中,在反序列化时则是通过java.lang.EnumvalueOf方法来根据名字在jvm中查找对象的。

同时编译器还禁止对枚举的定制化序列机制,因此禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。

策略模式

一种行为,多种结果,不同结果的实现,名曰策略。

public enum Calculator {
//加法运算
ADD("+"){
public int exec(int a,int b){
return a+b;
}
},
//减法运算
SUB("-"){
public int exec(int a,int b){
return a - b;
}
};
String value = "";
//定义成员值类型
Calculator(String _value){
this.value = _value;
}
//获得枚举成员的值
public String getValue(){
return this.value;
}
//声明一个抽象函数
public abstract int exec(int a,int b);
}

总结

  1. 枚举其实就是编写代码方面的语法糖,仍然是一个完整的类,暴露了几个静态变量,隐藏掉了大部分细节
  2. 枚举的成员变量是在编译后的静态代码块逐个创建实例来实现的
  3. 枚举的values()valueOf()方法都是编译器帮我们创建出来的
  4. 在调用values()方法时,返回的对象是克隆出来的一份新的,修改对原对象没有影响
  5. 枚举可以实现单例模式,能避免反射攻击和反序列化问题
  6. 避免反射攻击是因为枚举类不允许通过反射实例化
  7. 反序列化是通过Enum.valueOf到jvm中根据class,name去查找的
  8. 枚举可以实现策略模式,优化代码,避免大量的if...else的出现

【java基础】枚举的更多相关文章

  1. Java基础—枚举

    定义 枚举(enum)类型是Java 5新增的特性,它是一种新的类型,允许用常量来表示特定的数据片断,而且全部都以类型安全的形式来表示. 为什么要用枚举 在java语言中还没有引入枚举类型之前,表示枚 ...

  2. Java基础——枚举详解

    前言: 在第一次学习面向对象编程时,我记得最深的一句话就是“万物皆对象”.于是我一直秉承着这个思想努力的学习着JAVA,直到学习到枚举(Enum)时,看着它颇为奇怪的语法……我一直在想,这TM是个什么 ...

  3. Java基础--枚举

    1.枚举简介 枚举是由一组固定的常量组成的类型,自定义数据类型. 枚举的常量值一定是可列举的有限值.常量值的类型都是public static final. 下面代码中的Gender 是一种自定义的数 ...

  4. Java基础——枚举

    一.使用枚举类之前是如何实现枚举的  在JDK1.5之前,我们定义常量都是:public static fianl....:定义枚举也可以通过如下的方式: package com.jiangbei.t ...

  5. Java基础--枚举Enum

    Java中的枚举是一种特殊的类,可以将一组固定常量的集合组成一种类型,使用方便且类型安全.使用enum关键字定义. enum类型父类为Enum,通过Enum.class可见Enum为抽象类,实现了Co ...

  6. java基础---枚举类与注解

    一.枚举类 类的对象只有有限个,确定的.我们称此类为枚举类 如果枚举类中只有一个对象,则可以作为单例模式的实现方式. 定义枚举类 方式一:jdk5.0之前,自定义枚举类 public class Se ...

  7. java基础-02数据类型

    基本类型 整数 byte byte 数据类型是8位.有符号的,以二进制补码表示的整数 最小值是 -128(-2^7) 最大值是 127(2^7-1) 默认值是 0 byte 类型用在大型数组中节约空间 ...

  8. 【转】Java基础笔记 – 枚举类型的使用介绍和静态导入--不错

    原文网址:http://www.itzhai.com/java-based-notes-introduction-and-use-of-an-enumeration-type-static-impor ...

  9. 黑马程序员:Java基础总结----枚举

    黑马程序员:Java基础总结 枚举   ASP.Net+Android+IO开发 . .Net培训 .期待与您交流! 枚举 为什么要有枚举 问题:要定义星期几或性别的变量,该怎么定义?假设用1-7分别 ...

  10. 夯实Java基础系列14:深入理解Java枚举类

    目录 初探枚举类 枚举类-语法 枚举类的具体使用 使用枚举类的注意事项 枚举类的实现原理 枚举类实战 实战一无参 实战二有一参 实战三有两参 枚举类总结 枚举 API 总结 参考文章 微信公众号 Ja ...

随机推荐

  1. Electron-Vite2-MacUI桌面管理框架|electron13+vue3.x仿mac桌面UI

    基于vue3.0.11+electron13仿制macOS桌面UI管理系统ElectronVue3MacUI. 前段时间有分享一个vue3结合electron12开发后台管理系统项目.今天要分享的是最 ...

  2. Java并发之ReentrantLock源码解析(三)

    ReentrantLock和BlockingQueue 首先,看到这个标题,不要怀疑自己进错文章,也不要怀疑笔者写错,哈哈.本章笔者会从BlockingQueue(阻塞队列)的角度,看看juc包下的阻 ...

  3. Redmine部署

    Redmine部署文章: 第一篇:Redmine部署 第二篇:Redmine部署中遇到的问题 部门内部需要项目开发维护的网站,这种网站有付费的,也有开源项目.这类项目管理与协作的工具主要的MS Sha ...

  4. 重新整理 .net core 实践篇————polly失败重试[三十四]

    前言 简单整理一下polly 重试. 正文 在开发程序中一般都有一个重试帮助类,那么polly同样有这个功能. polly 组件包: polly 功能包 polly.Extensions.Http 专 ...

  5. jenkins在aws eks中的CI/CD及slave

    本文档不讲解jenkins的基础用法,主要讲解配合k8s的插件,和pipeline的用途和k8s ci/cd的流程. 以及部署在k8s集群内和集群外的注意事项. 1.准备工作 以下在整个CI/CD流程 ...

  6. 9.4、安装zabbix(2)

    8.从节点安装: (1)安装zabbix-agent: 1)下载zabbix-agent并安装: mkdir -p /tools/ cd /tools/ wget https://mirrors.tu ...

  7. 37.qt quick- 高仿微信实现局域网聊天V3版本(添加登录界面、UDP校验登录、皮肤更换、3D旋转)

    1.版本介绍(已上传至群里) 版本说明: 添加登录界面. UDP校验登录. 皮肤更换. 3D旋转(主界面和登录界面之间切换) . 效果图如下所示: 如果效果图加载失败,可以去哔哩哔哩 https:// ...

  8. 【网络编程】HTTP简介&URL

    目录 前言 1. http 简介 1.1 概念 1.2 原理 1.3 特点 2. URL 简介 2.1 概念 2.2 URL 通用格式 2.3 网页地址 实例说明 3. HTTP 消息结构 3.1 客 ...

  9. 暑假自学java第十一天

    1,使用java.util.Arrays类处理数组 (1 ) public static void sort(数值类型 [ ] a):对指定的数值型数组按数字升序进行排序.在数组排序中设计一个简单的冒 ...

  10. 暑假自学java第三天

    1,java中有个c++中没有的数据类型 bite:byte的取值范围为-128~127,占用1个字节(-2的7次方到2的7次方-1) 在通常情况下,如果JAVA中出现了一个整数数字比如35,那么这个 ...