Java-Enum枚举类

Java-Enum枚举类


前言

介绍
enum 的全称为 enumeration, 是 JDK 1.5 中引入的新特性,存放在 java.lang 包中。
使用关键字enum来定义枚举类,枚举类是一个特殊的类,大部分功能和普通类是一样的。
区别为:枚举类继承了java.lang.Enum类,而不是默认的Object类。
而java.lang.Enum类实现了java.lang.Serializable和java.lang.Comparable接口。
非抽象的枚举类默认会使用final修饰,因此不能派生子类


一、枚举的实现原理

枚举类型使用的最常用类型就是枚举常量,下面通过一个简单的Demo来说明枚举的原理。
使用示例:

// 定义
public enum Color {
    BLACK, WHITE
}
 
// 使用
public class Main {
    public static void main(String[] args) {
        System.out.println(Color.BLACK);
    }
}
// 结果
// BLACK

从简单的示例,不能看出枚举的特点和枚举的具体实现。
下面我们通过 jad工具来反编译Color类, 通过jad -sjava Color.class反编译出一份java文件:

// final修饰,无法被继承
public final class Color extends Enum {
 
    // 为了避免 返回的数组修改,而引起内部values值的改变,返回的是原数组的副本
    public static Color[] values() {
        return (Color[]) $VALUES.clone();
    }
 
    // 按名字获取枚举实例
    public static Color valueOf(String name) {
        return (Color) Enum.valueOf(em / Color, name);
    }
 
    // 私有的构造函数
    private Color(String name, int ordinal) {
        super(name, ordinal);
    }
 
    // enum第一行的声明的变量,都对应一个枚举实例对象
    public static final Color BLACK;
    public static final Color WHITE;
    //
    private static final Color $VALUES[];
 
    // 静态域初始化,说明在类加载的cinit阶段就会被实例化,jvm能够保证类加载过程的线程安全
    static {
        BLACK = new Color("BLACK", 0);
        WHITE = new Color("WHITE", 1);
        $VALUES = (new Color[]{
                BLACK, WHITE
        });
    }
}

从反编译的类中,可以看出, 我们使用enum关键字编写的类,在编译阶段编译器会自动帮我们生成一份真正在jvm中运行的代码。
Color 类继承自 Enum类:

public abstract class Enum<E extends Enum<E>>implements Comparable<E>, Serializable

Enum类接受一个继承自Enum的泛型.(在反编译java文件中没有体现泛型是因为,泛型在编译阶段就会被类型类型擦除(参考泛型详解,链接:泛型详解),替换为具体的实现)。
从反编译的Color类中可以看出,在enum关键字的类中,第一行 (准确的说是第一个分号前) 定义的变量,都会生成一个 Color实例,且它是在静态域中进行初始化的, 而静态域在类加载阶段的cinit中进行初始化,所以枚举对象是线程安全的,由JVM来保证.
生成的枚举类有 Color $VALUES[];成员变量,外部可以通过values()方法获取当前枚举类的所有实例对象。

二、使用反射查看枚举类

通过上述查看编译后的枚举类后,我们可以清晰看到枚举类编译后的类内容信息,但是笔者仍旧抱有怀疑的态度,于是就用反射做了验证。

源码:

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Main {
    public static void main(String[] args) {
        Class clazz = Color.class;
        Field[] declaredFields = clazz.getDeclaredFields();
        for(Field field : declaredFields){
            System.out.println(field.toString());
        }
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method method : declaredMethods) {
            System.out.println(method.toString());
        }
        Constructor[] constructors = clazz.getConstructors();
        System.out.println(constructors.length);
    }

    enum Color {
        BLACK, WHITE
    }
}

运行结果:

public static final Main$Color Main$Color.BLACK
public static final Main$Color Main$Color.WHITE
private static final Main$Color[] Main$Color.$VALUES
public static Main$Color[] Main$Color.values()
public static Main$Color Main$Color.valueOf(java.lang.String)
0

可以得知,编译过后的Color类,含三个常量,两个方法,没有可见的构造方法。

三、 枚举的高级使用

先说一个案例,你需要让每一个星期几对应到一个整数,比如星期天对应0。由于枚举类在定义的时候会自动为每个变量添加一个顺序,从0开始。
假如你希望0代表星期天,1代表周一,并且你在定义枚举类的时候,顺序也是这个顺序,那你可以不用定义新的变量,就像这样:

public enum Weekday {
    SUN,MON,TUS,WED,THU,FRI,SAT
}

这个时候,星期天对应的ordinal值就是0,周一对应的就是1,满足你的要求。但是,如果你这么写,那就有问题了:

public enum Weekday {
    MON,TUS,WED,THU,FRI,SAT,SUN
}

我把SUN放到了最后,但是我还是希0代表SUN,1代表MON怎么办呢?默认的ordinal是指望不上了,因为它只会傻傻的给第一个变量0,给第二个1、、、
所以,我们需要自己定义变量!
看代码:

public enum Weekday {
    MON(1),TUS(2),WED(3),THU(4),FRI(5),SAT(6),SUN(0);
 
    private int value;
 
    private Weekday(int value){
        this.value = value;
    }
}

我们对上面的代码做了一些改变:
首先,我们在每个枚举变量的后面加上了一个括号,里面是我们希望它代表的数字。
然后,我们定义了一个int变量,然后通过构造函数初始化这个变量。
你应该也清楚了,括号里的数字,其实就是我们定义的那个int变量。这句叫做自定义变量。

请注意:这里有三点需要注意:
一定要把枚举变量的定义放在第一行,并且以分号结尾。
构造函数必须私有化。事实上,private是多余的,你完全没有必要写,因为它默认并强制是private,如果你要写,也只能写private,写public是不能通过编译的。
自定义变量与默认的ordinal属性并不冲突,ordinal还是按照它的规则给每个枚举变量按顺序赋值。

好了,你已经掌握了上面的知识,你想,既然能自定义一个变量,能不能自定义两个呢?
当然可以:

public enum Weekday {
    MON(1,"mon"),TUS(2,"tus"),WED(3,"wed"),THU(4,"thu"),FRI(5,"fri"),SAT(6,"sat"),SUN(0,"sun");
 
    private int value;
    private String label;
 
    private Weekday(int value,String label){
        this.value = value;
        this.label = label;
    }
}

每一个枚举类型极其定义的枚举变量在JVM中都是唯一的
这句话的意思是枚举类型它拥有的实例在编写的时候,就已经确定下,不能通过其他手段进行创建,且枚举变量在jvm有且只有一个对应的实例.


留言:文章是博主本人遇到问题,通过各种渠道收集资料解决后,并记录在word文档里,最后才整理发博,因此有些相似的内容是借鉴网友的,若有雷同,请谅解!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java2核心技术第I卷.基础知识 目录: 译者序 前言 第1章Java程序设计概述 1.1 Java程序设计平台 1.2 Java“白皮书”的关键术语 1.2.1简单性 1.2.2面向对象 1.2. 3分布式 1. 2.4健壮性 1. 2.5安仝性 1. 2.6体系结构立 1.2.7可移植性 1.2.8解释型 1.2.9高性能 1.2.10多线程 1.2.11动态性 1. 3 Java与Internet 1. 4 Java发展简史 1.5关于Java的常见误解 第2章Java程序设计环境 2.1安装Java开发工具箱 2.1.1下载JDK 2.1.2设置执行路径 2.1.3安装库源代码和文档 2.1.4安装本书的示例 2.1.5导航Java目录 2.2选择开发环境 2.3使用命令行工具 2.4使用集成开发环境 2.5使用文本编辑器编译并运行程序 2.6运行图形化应用程序 2.7建立并运行applet 第3章Java基本的程序设计结构 3.1一个简单的Java应用程序 3.2注释 3.3数据类型 3.3.1整型 3.3.2浮点型 3.3.3 char类型 3.3.4 boolean类型 3.4变量 3.4.1初始化变量 3.4.2常量 3.5运算符 3.5.1自增运算符与自减运算符 3.5.2关系运算符与boolean运算符 3.5.3位运算符 3.5.4数学函数与常量 3.5.5数值类型之间的转换 3.5.6强制类型转换 3.5.7括号与运算符级别 3.5.8枚举类型 3.6字符串 3.6.1代码点与代码单元 3.6.2子串 3.6.3字符串编辑 3.6.4拼接 3.6.5检测字符串是否相等 3.6.6阅读联机API文档 3.7输入输出 3.7.1读取输入 3.7.2格式化输出 3.8控制流程 3.8.1块作用域 3.8.2条件语句 3.8.3循环 3.8.4确定循环 3.8.5多重选择――switch语句 3.8.6断控制流程语句 3.9大数值 3.10数组 3.10.1 “for each”循环 3.10.2数组初始化器以及匿名数组 3.10.3数组拷贝 3.10.4命令行参数 3.10.5数组排序 3.10.6多维数组 3.10.7不规则数组 第4章对象与类 4.1面向对象程序设计概述 4.1.1 OOP词汇表 4.1.2对象 4.1.3类之间的关系 4.1.4 00P与传统的过程化程序设计技术对比 4.2使用现有类 4.2.1对象与对象变量 4.2.2 Java的GregorianCalendar类 4.2.3更改器方法与访问器方法 4.3用户自定义类 4.3.1一个Employee类 4.3.2多个源文件的使用 4.3.3解析Employee类 4.3.4从构造器开始 5.2.2相等测试与继承 5.2.3 hashCode方法 5.2.4 toString方法 5.3泛型数组列表 5.3.1访问数组列表元素 5.3.2类型化与原始数组列表的兼容性 5.4对象包装器与自动打包 5.5反射 5.5.1 Class类 5.5.2使用反射分析类的能力 5.5.3在运行时使用反射分析对象 5.5.4使用反射编写通用的数组代码 5.5.5方法指针 5.6枚举类 5.7继承设计技巧 第6章接口与内部类 6.1接口 6.1.1接口的特性 6.1.2接口与抽象类 6.2对象克隆 6.3接口与回调 6.4内部类 6.4.1使用内部类访问对象状态 6.4.2内部类的特殊语法规则 6.4.3内部类是否实用、必要和安全 6.4.4局部内部类 6.4.5匿名内部类 6.4.6静态内部类 6.5代理 第7章图形程序设计 7.1 Swing概述 7.2创建框架 7.3框架定位 7.4在面板显示信息 7.5 2D图形 7.6颜色 7.7为文本设定特殊字体 7.8图像 第8章事件处理 8.1、事件处理基础 8.1.1实例:处理按钮点击事件 8.1. 2建议使用内部类 8.1. 3将组件变成事件监听器 8.1.4实例:改变观感 8.1.5实例:捕获窗口事件 8.2 AWT事件继承层次 8.3 AWT的语义事件和低级事件 8.4低级事件类型 8.4.1键盘事件 8.4.2鼠标事件 8.4.3焦点事件 8.5动作 8.6多点传送 8.7实现事件源 第9章swing用户界面组件 9.1模型一视图一控制器设计模式 9.2布局管理器慨述 9.2.1边界布局 9.2.2面板 9.2.3网格布局 9.3文本输入 9.3.1文本域 9.3. 2标签与标签组件 9.3 3文本域变化跟踪 9.3.4密码域 9.3.5格式化的输入域 9.3.6文本区 9.4选择组件 9.4.1复选框 9.4.2单选按钮 9.4.3边界 9.4.4组合框 9.4.5滑块 9.4.6 JSpinner组件 9.5菜单 9.5.1菜单创建 9.5.2菜单项的图标 9.5.3复选框和单选按钮菜单项 9.5.4弹出菜单 9.5.5快捷键和加速器 9.5.6启用和禁用菜单项 9.5.7工具栏 9 5.8工具提示 9.6复杂的布局管理 9.6.1箱式布局 9.6.2网格组布局 9.6.3弹簧布局 9.6.4不使用布局管理器 9.6.5定制布局管理器 9 6.6遍历顺序 9.7对话框 9.7.1选项对话框 9.7.2创建对话框 9.7.3数据交换 9.7.4文件对话框 9.7.5颜色选择器 第10章部署applet和应用程序 10.1 applet基础 10. 1. 1一个简单的applet 10.1. 1.2查看applet 10.1.3将应用程序转换为applet lO.1.4 applet的生命周期 10.1.5安全基础 10.1.6 applet的弹出式窗口 10.2 applet的HTML标记和属性 11.5.1启用和禁用断言 11.5.2使用断言的建议 11.6调试技术 11. 6.1调试的常用技巧 11.6.2使用控制台窗口 11.6_3跟踪AWT事件 11.6.4 AWT的Robot类 11.7使用调试器 11.7.1 JDB调试器 11.7.2 Eclipse调试器 第12章流与文件 12.1流 12.2完整的流结构 12.2.1流过滤器的分层 12.2.2数据流 12.2.3随机存取文件流 12.2.4文本流 12.2.5字符集 12.2.6文本输出 12.2.7文本输入 12.3 ZIP文件流 12.4流的使用 12.4.1分隔符输出 12.4.2字符串记号处理器和带分隔符的文本 12.4.3读取带允隔符的输入 12.4.4 StringBuilder类 12.4.5随机存取流 12.5对象流 12.5.1存储可变类型的对象 12.5.2理解对象序列化文件格式 12.5.3保存对象引用问题的解决 12.5.4理解对象引用的输出格式 12.5.5修改默认的序列化机制 12.5.6单元素与类型安全枚举的序列化 12.5.7版本 12.5.8使用序列化进行克隆 12.6文件管理 12.7新的I/O 12.7.1内存映射文件 12.7.2缓冲区数据结构 12.7.3文件锁定 12.8正则表达式 13章泛型程序设计 13.1为什么要使用泛型程序设计 13.2简单泛型类的定义 13.3泛型方法 13.4类型变量的限定 13.5泛型代码和虚拟机 13.5.1翻译泛型表达式 13.5.2翻译泛型方法 13.5.3调用遗留代码 13.6约束与局限性 13.6.1基本类型 13.6.2运行时类型查询 13.6.3异常 13.6.4数组 13.6 5泛型类型的实例化 13.6 6静态上下文 13.6.7擦除后的冲突 13.7泛型类型的继承规则 13.8通配符类型 13.8.1通配符的超类型限定 13.8.2无限定通配符 13.8 3通配符捕获 13.9反射和泛型 13.9.1使用Class参数进行类型匹配 13.9. 2虚拟机的泛型类型信息 附录AJava关键字 附录B更新的JDK 5.0代码 Java2核心技术II卷.高级特性 目录: 译者序 前言 第1章 多线程 1.1 什么是线程 1.2 断线程 1.3 线程状态 1.4 线程属性 1.5 同步 1.6 阻塞队列 1.7 线程安全的集合 1.8 Callable和Future 1.9 执行器 1.10 同步器 1.11 线程和Swing工作器 第2章 集合 2.1 集合接口 2.2 具体的集合 2.3 集合框架 2.4 算法 2.5 遗留下来的集合 第3章 网络 3.1 连接到服务器 3.2 实现服务器 3.3 发送E-Mail 3.4 建立URL连接 3.5 高级套接字编程 第4章 数据库编程 4.1 JDBC的设计 4.2 结构化查询语言 4.3 安装JDBC驱动程序类型 4.4 JDBC的典型用法 4.5 执行查询操作 4.6 可滚动和可更新的结集 4.7 元数据 4.8 行集 4.9 事务 4.10 高级连接管理 4.11 LDAP概述 第5章 分布式对象 5.1 客户与服务器的角色 5.2 远程方法调用 5.3 配置远程方法调用 5.4 远程方法的参数传递 5.5 服务器对象激活 5.6 JavaIDL与CORBA 5.7 远程方法调用与SOAP 第6章 高级Swing 第7章 高级AWT 第8章 JavaBean构件 第9章 安全 第10章 国际化 第11章 本地方法 第12章 XML 第13章 注释

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值