枚举的定义

  1. public enum Color {
  2. Red,Blue,Green;
  3. }

枚举的使用

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

原理

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

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

  1. public final class Color extends java.lang.Enum<Color> {
  2. public static final Color Red;
  3. public static final Color Blue;
  4. public static final Color Green;
  5. public static final Color[] $VALUES;
  6. public static Color[] values();
  7. public static Color valueOf(java.lang.String);
  8. static {};
  9. }

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

  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

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

还原后的代码如下:

  1. public final class Color extends java.lang.Enum<Color> {
  2. //定义的枚举成员
  3. public static final Color Red;
  4. public static final Color Blue;
  5. public static final Color Green;
  6. //编译器自动生成的 javap -c 还查不出来,疑惑
  7. public static final /* synthetic */ Color[] $VALUES;//编译器自动生成的
  8. public static Color[] values(){
  9. /**
  10. * 0: getstatic #1 // Field $VALUES:[LColor;
  11. * 3: invokevirtual #2 // Method "[LColor;".clone:()Ljava/lang/Object;
  12. * 6: checkcast #3 // class "[LColor;"
  13. * 9: areturn
  14. */
  15. return $VALUES.clone();
  16. }
  17. public static Color valueOf(java.lang.String s){
  18. /**
  19. * 0: ldc #4 // class Color
  20. * 2: aload_0
  21. * 3: invokestatic #5 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljav
  22. * a/lang/Enum;
  23. * 6: checkcast #4 // class Color
  24. * 9: areturn
  25. */
  26. return Enum.valueOf(Color.class,s);
  27. }
  28. protected Color(String name, int ordinal) {
  29. super(name, ordinal);
  30. }
  31. static {
  32. /**
  33. * 0: new #4 // class Color
  34. * 3: dup
  35. * 4: ldc #7 // String Red
  36. * 6: iconst_0
  37. * 7: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
  38. * 10: putstatic #9 // Field Red:LColor;
  39. */
  40. Red=new Color("Red",0);
  41. /**
  42. * 13: new #4 // class Color
  43. * 16: dup
  44. * 17: ldc #10 // String Blue
  45. * 19: iconst_1
  46. * 20: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
  47. * 23: putstatic #11 // Field Blue:LColor;
  48. */
  49. Blue=new Color("Blue",1);
  50. /**
  51. * 26: new #4 // class Color
  52. * 29: dup
  53. * 30: ldc #12 // String Green
  54. * 32: iconst_2
  55. * 33: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
  56. * 36: putstatic #13 // Field Green:LColor;
  57. */
  58. Green=new Color("Green",2);
  59. /**
  60. * 39: iconst_3
  61. * 40: anewarray #4 // class Color
  62. * 43: dup
  63. * 44: iconst_0
  64. * 45: getstatic #9 // Field Red:LColor;
  65. * 48: aastore
  66. * 49: dup
  67. * 50: iconst_1
  68. * 51: getstatic #11 // Field Blue:LColor;
  69. * 54: aastore
  70. * 55: dup
  71. * 56: iconst_2
  72. * 57: getstatic #13 // Field Green:LColor;
  73. * 60: aastore
  74. * 61: putstatic #1 // Field $VALUES:[LColor;
  75. */
  76. $VALUES=new Color[]{Red,Blue,Green};
  77. };
  78. }

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

枚举的扩展

单例模式

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

  1. public enum Singleton {
  2. INSTANCE;
  3. public void doSometing(){
  4. System.out.println("doing");
  5. }
  6. }

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

避免反射攻击

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

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

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

反序列化

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

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

策略模式

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

  1. public enum Calculator {
  2. //加法运算
  3. ADD("+"){
  4. public int exec(int a,int b){
  5. return a+b;
  6. }
  7. },
  8. //减法运算
  9. SUB("-"){
  10. public int exec(int a,int b){
  11. return a - b;
  12. }
  13. };
  14. String value = "";
  15. //定义成员值类型
  16. Calculator(String _value){
  17. this.value = _value;
  18. }
  19. //获得枚举成员的值
  20. public String getValue(){
  21. return this.value;
  22. }
  23. //声明一个抽象函数
  24. public abstract int exec(int a,int b);
  25. }

总结

  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. CMD批处理(1)——批处理常用命令总结

    echo 打开回显或关闭回显功能,或显示消息.如果没有任何参数,echo命令将显示当前的回显设置. 命令格式1:echo [{on|off}] 命令格式2:echo [message]   例.在命令 ...

  2. [UWP] WinUI 2.6 使用指南

    2021年6月24日,Windows 11 正式对外发布,对于UWP开发者来说,这一天同样值得纪念,因为WinUI 2.6也正式发布了! 相同的时间点意味着一件事,即WinUI 2.6和Windows ...

  3. 『动善时』JMeter基础 — 49、使用JMeter自身代理录制APP测试脚本

    目录 1.测试计划内包含的元件 2.HTTP代理服务器的设置内容 3.设置手机的代理服务器 4.录制脚本 5.查看录制的脚本 6.HTTP代理服务器的排除模式 7.保存脚本 8.注意坑点 录制脚本只不 ...

  4. OO unit3 summary

    Unit3 ​ JML(Java Modeling Language) 是用于对 Java 程序进行规格化设计的一种表示语言,它对于方法内部具体是如何实现的并无要求,只是对方法的接口以及行为进行限制, ...

  5. [源码解析] 深度学习分布式训练框架 horovod (8) --- on spark

    [源码解析] 深度学习分布式训练框架 horovod (8) --- on spark 目录 [源码解析] 深度学习分布式训练框架 horovod (8) --- on spark 0x00 摘要 0 ...

  6. Centos7安装配置jenkins(Tomcat)

    Centos7安装配置jenkins(Tomcat) 一.准备工作 1.1 安装JDK1.8 具体安装过程不在赘述. 1.2 下载jenkins的war包 jenkins官网下载地址:https:// ...

  7. Pytest学习笔记8-参数化

    前言 我们在实际自动化测试中,某些测试用例是无法通过一组测试数据来达到验证效果的,所以需要通过参数化来传递多组数据 在unittest中,我们可以使用第三方库parameterized来对数据进行参数 ...

  8. 虚拟机安装Windows7旗舰版-超详细图文

    虚拟机安装Windows7旗舰版 ----就是想弄一个自己用的CTF+渗透测试的工具集成系统,本来想着用真实机弄就好了,但还是出于安全的考虑,还是再装个虚拟机吧~ 1.先到MSDN找好安装包:http ...

  9. 明明是企业管理软件,CRM系统为何被抵触?

    小编在昨天的文章<CRM系统为什么没有达到预期效果?>中曾说过,CRM客户管理系统没有达到预期效果的其中一个原因是CRM系统的使用率太低,而根本的原因是员工的抵触.明明是企业管理大师,CR ...

  10. Python管道进行数据的吞吐处理

    import multiprocessing import random import time import datetime import struct import os import getF ...