文章目录

  • 枚举类型
    • 一. 枚举常量
    • 二.枚举主体声明
      • 对枚举常数自我引用的限制:
    • 三.枚举成员

这不是一顿快餐,希望你沉淀下来,细细品尝

写在前面

枚举类型可以考虑用来替换接口中的常量声明。并且 《Effective Java》建议不要用“常量接口模式”,而是在类中声定义常量。可实际上在接口中定义常量往往更加简洁。

枚举类默认继承了 java.lang.Enum 类。

枚举类型


枚举声明指定一个新的枚举类型, 一种特殊的类的类型。

EnumDeclaration:
{ClassModifier} enum Identifier [Superinterfaces] EnumBody

如果枚举声明的类修饰符有`abstract` 或者 `final` 将产生编译时错误。

灵魂拷问:为什么不能显式声明为abstract 或者 final 的?参考推荐博文中的第一篇,思考其所说的反编译结果

枚举声明是隐式final,除非它包含至少一个具有类主体的枚举常量。

public enum Student {GOOD(){@Overridepublic void action() {}};public abstract void action();
}// 反编译如下 (javap -p命令)
Compiled from "Student.java"
public abstract class com.gysoft.utils.Student extends java.lang.Enum<com.gysoft.utils.Student> {public static final com.gysoft.utils.Student GOOD;
private static final com.gysoft.utils.Student[] $VALUES;
public static com.gysoft.utils.Student[] values();
public static com.gysoft.utils.Student valueOf(java.lang.String);
private com.gysoft.utils.Student();
public abstract void action();
com.gysoft.utils.Student(java.lang.String, int, com.gysoft.utils.Student$1);
static {};
}

哪怕反编译出来的类是抽象的,我们依然不能显示的将其声明为abstract的,当然,此时它肯定不是final的了。

嵌套枚举类型是隐式静态的。嵌套枚举类型的声明允许冗余地指定静态修饰符。

灵魂拷问:为什么是静态的?因为非静态的内部类对象需要依赖外部类对象而存在,通过反编译的结果知道,编译器创建了Student 对象,如果是非静态内部类,它无法限制外部类的创建条件,最终也就无法构造 Student 对象。其实从存在的意义上来看,那并不是枚举类该有的

这意味着不可能在内部类(这里的内部类指的是我们常说的非静态的内部类)的主体中声明枚举类型,因为内部类除了常量变量外不能有静态成员。


枚举类型除了由其枚举常量定义的实例外,没有其他实例。试图显式实例化枚举类型(§15.9.1)是编译时错误。

枚举类型的构造方法只能是私有的

除了编译时错误之外,还有三种机制确保枚举类型的实例不存在于枚举常量定义的实例之外:

  1. Enum中的最后一个克隆方法确保永远不能克隆Enum常量。
protected final Object clone() throws CloneNotSupportedException {throw new CloneNotSupportedException();}
  1. 禁止枚举类型的反射实例化。
public enum  Student {GOOD;public static void main(String[] args) {try {Student.GOOD.getDeclaringClass().newInstance();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}
}//异常最终会从 Class 类的 如下方法抛出
/*
** parameterTypes 是一个{},但constructor(private
** com.gysoft.utils.Student(java.lang.String,int))能够获取到两个参数类型,无法与传入** 的parameterTypes 匹配上。导致无法返
** 回,最终抛出异常。
*/
private Constructor<T> getConstructor0(Class<?>[] parameterTypes,int which) throws NoSuchMethodException{Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));for (Constructor<T> constructor : constructors) {if (arrayContentsEq(parameterTypes,constructor.getParameterTypes())) {return getReflectionFactory().copyConstructor(constructor);}}throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));}
  1. 序列化机制的特殊处理确保不会因为反序列化而创建重复的实例
    /*** prevent default deserialization*/private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException {throw new InvalidObjectException("can't deserialize enum");}private void readObjectNoData() throws ObjectStreamException {throw new InvalidObjectException("can't deserialize enum");}
一. 枚举常量

枚举声明的主体能够包含枚举常量,一个枚举常量定义为一个枚举类型的实例。(参考上文反编译后的结果)

EnumBody:
{ [EnumConstantList] [,] [EnumBodyDeclarations] }EnumConstantList:
EnumConstant {, EnumConstant}EnumConstant:
{EnumConstantModifier} Identifier [( [ArgumentList] )] [ClassBody]EnumConstantModifier:
Annotation

需要注意的是枚举常量的修饰符只能是注解类型,以下是一个稍微复杂的枚举类:

public enum Student {@Annotation GOOD("zhangsan",180){@Overridevoid say() {System.out.println("我是"+name+",身高"+height);}};String name;int height;Student(String name,int height){this.name = name;this.height = height;}abstract void say();
}// 下面这种写法也是合法的,但是你无法调用 say 方法
public enum Student {@Annotation GOOD("zhangsan",180){void say() {System.out.println("我是"+name+",身高"+height);}};String name;int height;Student(String name,int height){this.name = name;this.height = height;}}

在枚举常量的类主体隐式定义了一个匿名类声明,它拓展了立即封闭的枚举类型。类体受匿名类的一般规则控制;特别是它不能包含任何构造函数。想要调用类主体中声明的实例方法,前提是该方法是覆盖的枚举类型中的可访问方法。

枚举常量的类主体声明一个abstract 方法将产生编译时错误。

这是因为每个枚举常量仅仅只有一个实例,所以当比较两个对象引用时,如果知道其中至少有一个是引用的枚举常量,则允许使用 == 操作符代替equals方法。

Enum中的equals方法是一个final方法:

 public final boolean equals(Object other) {return this==other;}
二.枚举主体声明

除了枚举常量,枚举声明的主体能够包含构造器和成员声明 以及实例和静态初始化器。

EnumBodyDeclarations:
; {ClassBodyDeclaration}ClassBodyDeclaration:ClassMemberDeclaration InstanceInitializer StaticInitializer ConstructorDeclarationClassMemberDeclaration:FieldDeclaration MethodDeclaration ClassDeclaration InterfaceDeclaration ;

一个符合以上规格的类如下:

public enum Student {/****/@Annotation GOOD("zhangsan",180){void say() {System.out.println("我是"+name+",身高"+height);}},BAD("lisi",178);String name;int height;{System.out.println("实例初始化器执行!");}static{System.out.println("静态初始化器执行!");}Student(String name,int height){this.name = name;this.height = height;System.out.println("构造函数执行!");}public static void main(String[] args) {System.out.println("main 方法开始");}}// 执行 main 方法 输出如下:
实例初始化器执行!
构造函数执行!
实例初始化器执行!
构造函数执行!
静态初始化器执行!
main 方法开始

枚举声明的主体中的任何构造函数或成员声明都适用于枚举类型,就像它们曾经出现在普通类声明主体中一样,除非另有明确说明。

  • 在枚举声明里的构造函数声明为 public 或者 protected 会产生编译时错误。

  • 在枚举声明里的构造函数声明包含了父类构造函数调用 会产生编译时错误。

// 尽管Enum 中的构造函数访问控制修饰符为 protected
protected Enum(String name, int ordinal) {this.name = name;this.ordinal = ordinal;
}
// 只是不能调用 super(), 像super.hashcode() 等方法可以正常调用
  • 从枚举类型的构造函数、实例初始化器或实例变量初始化器表达式引用枚举类型的静态字段是编译时错误,除非该字段是常量变量。结合前文运行结果,以及后面的对枚举常数自我引用的限制可以理解。
public static final String ss = new String("ss"); // 编译时错误
// public static final String ss = "ss" 编译正常
String name;
int height;{System.out.println("实例初始化器执行!"+ss);}
  • 如果枚举声明有一个抽象方法作为成员,除非该枚举类至少有一个枚举常量,并且该枚举类的所有枚举常量都有提供抽象方法的具体实现的类体。
  • 枚举声明如果 声明finalizer是编译时的错误(§12.6)。枚举类型的实例可能永远不会被终结。

在枚举声明中,没有访问修饰符的构造函数声明是私有的。

在没有构造函数声明的枚举声明中,隐式声明默认构造函数。默认构造函数是私有的,没有正式的参数,也没有throws子句。

对枚举常数自我引用的限制:

如果没有静态字段访问规则,显然合理的代码在运行时才会失败,这是由于枚举类型固有的初始化循环性造成的。(循环性存在于任何具有“自类型”静态字段的类中。)下面是一个会失败的代码的例子:

import java.util.Map;
import java.util.HashMap;enum Color {RED, GREEN, BLUE;Color() { colorMap.put(toString(), this); } // 实例初始化器 编译无法通过static final Map<String,Color> colorMap =new HashMap<String,Color>();
}

此枚举的静态初始化将引发NullPointerException,因为在枚举常量的构造函数运行时,静态变量colorMap未初始化。上面的限制确保不能编译这样的代码。然而,代码可以很容易地重构,以正常工作:

import java.util.Map;
import java.util.HashMap;enum Color {RED, GREEN, BLUE;static final Map<String,Color> colorMap =new HashMap<String,Color>();static {for (Color c : Color.values())colorMap.put(c.toString(), c);}
}

重构版本显然是正确的,因为静态初始化是从上到下进行的。

三.枚举成员

枚举类型 E 的成员都是以下类型:

  • 在 E 的声明主体中声明的成员;

  • Enum<E>继承的成员;

  • E 中声明的每个枚举常量 c , E 都具有类型E 隐式声明的公共静态final字段,该字段具有与 c 相同的名称(参考前文反编译后的结果进行理解)。字段有一个由 c 组成的变量初始化器,并被与 c 相同的注解所注解。

在声明E的主体中显式声明任何静态字段之前,这些字段以与相应枚举常量相同的顺序隐式声明。

枚举常量是在初始化相应的隐式声明字段时创建的。

官网最后列出了几个创建枚举类的例子,可以在最后的参考博文中找到官网地址

推荐博文


java基础加强–实现带有抽象方法的枚举

参考博文


Java8语言规范-枚举

如果你觉得我的文章对你有所帮助的话,欢迎关注我的公众号。赞!
认认真真学习,做思想的产出者,而不是文字的搬运工。错误之处,还望指出!

谈Java语言规范之枚举类型相关推荐

  1. Java语言中的枚举类型

    枚举类型(Enumerated Type)在编程语言中常用,程序员必备食粮,下面随着我的思路来认识一下枚举类型. 是什么 枚举类型在java中是一种基本数据类型.它用于声明一组命名的常数,当一个变量有 ...

  2. JAVA语言规范 JAVA SE 8 - 类型、值和变量

    JAVA语言规范 JAVA SE 8 - 类型.值和变量 类型和值的种类 简单类型和值 整数类型和值 整数操作 浮点数类型.格式和值 浮点数操作 boolean类型和布尔值 引用类型和值 对象 Obj ...

  3. Java快速入门学习笔记2 | Java语言中的基本类型

    有人相爱,有人夜里开车看海,有人却连LeetCode第一题都解不出来!虽然之前系统地学习过java课程,但是到现在一年多没有碰过Java的代码,遇到LeetCode不知是喜是悲,思来想去,然后清空自己 ...

  4. C语言枚举类型通常用来干嘛,C语言学习:枚举类型是什么?

    前言 枚举(enum)类型是计算机编程语言中的一种数据类型.枚举类型:在实际问题中,有些变量的取值被限定在一个有限的范围内.例如,一个星期内只有七天,一年只有十二个月,一个班每周有六门课程等等.如果把 ...

  5. 浅谈Java编码规范,养成良好习惯!

    编程最害怕的是出现 bug,满屏的报错让我们的大脑嗡嗡直响,不好的编码习惯也会让其他人无从下手,所以良好的编码习惯不仅可以让我们尽量少的出错,也可以让他人清明的看懂自己写的代码.本篇主要分为三个部分: ...

  6. java变量命名规则_浅谈JAVA开发规范与开发细节(上)

    开发团队在开发过程中,由于每个人的开发习惯,以及对于技术的理解深浅程度不一,往往一个项目在开发过程中,代码的质量,代码的风格都不尽相似,所以有一份适合团队的代码规范是非常有必要的,而一个团队的代码规范 ...

  7. Java基础教程(15)--枚举类型

      枚举类型定义了一个枚举值的列表,每个值是一个标识符.例如,下面的语句声明了一个枚举类型,用来表示星期的可能情况: public enum Day {SUNDAY, MONDAY, TUESDAY, ...

  8. Java魔法堂:枚举类型详解

    一.前言 Java的枚举类型相对C#来说具有更灵活可配置性,Java的枚举类型可以携带更多的信息. // C# enum MyColor{RED = 0,BLUE = 1 } Console.Writ ...

  9. MyBatis对于Java对象里的枚举类型处理

    平时咱们写程序实体类内或多或少都会有枚举类型属性,方便嘛.但是mybatis里怎么处理他们的增删改查呢? 要求: 插入的时候,会用枚举的定义插入数据库,我们希望在数据库中看到的是数字或者其他东西: 查 ...

最新文章

  1. 解决:No configuration found. Configuring ehcache from ehcache-failsafe.xml 问题
  2. css 全局 兼容性问题
  3. Linux 内核Coding Style整理
  4. tensors used as indices must be long or byte tensors
  5. 每天一道LeetCode-----分糖果问题
  6. [Leetcode] Flatten Binary Tree to Linked List 整平二叉树
  7. 每日两SQL(10),欢迎交流~
  8. System.Drawing.Color转System.Windows.Media.Color
  9. 某微型计算机指令格式如图,组成原理考试试卷
  10. 梦想还是要有的 万一实现了呢
  11. batchplot放到哪个文件夹_如何整理文件夹?来看腾讯设计师的方法
  12. 苹果AirPods Pro将提供8种配色选择 或成最贵真无线耳机
  13. Ajax 1.0 中使用web控件调用后台方法的用法.
  14. python2.7 matplotlib_Python 2.7中的Numpy、SciPy、MatPlotLib安装与配置
  15. (转)全球最神秘的高频交易巨头Jump Trading
  16. 黑马程序员——三天快速入门python机器学习(总结篇)
  17. Proteus ISIS仿真软件中英文元件名称对照
  18. 浙江省计算机二级办公软件高级应用技术,浙江省计算机二级办公软件高级应用技术考试大纲.doc...
  19. 中国股市:史上最新A股龙头股票
  20. dataframe两个表合并_DataFrame踩坑整理(一)

热门文章

  1. EBS-PO接收控制状态的处理
  2. PHP_基础学习(10)
  3. 用matlab实现任意点图片的旋转_Matlab实现图像旋转
  4. 使用Keras进行深度学习:(一)Keras 入门
  5. c类事业编计算机岗面试题,2017广西事业单位分类统考(C类)笔试试题答案
  6. 霸榜知乎,谴责豆瓣,数据分析告诉你《流浪地球》到底好看么?
  7. iOS 界面流畅度研究
  8. 出国留学,转计算机编程学习入门以及面试经验之一家之言
  9. qt飞扬青云 / Qt开发经验
  10. 鄙人从事IT业已经有9年时间,少壮不努力,长大搞IT