IT教程 ·

Java:枚举类也就这么回事

MySQL8.0 InnoDB并行执行

目次

一、媒介

本篇博客是对JDK1.5的新特征罗列的一波小小的总结,主假如昨天在看一部分面试题的时刻,碰到了罗列范例的题目,发明本身有很多细节还须要加强,做起来都含糊其词,是时刻总结一波了。

二、源自一道面试题

不多bb,直接直言不讳,我碰到如许一道或许很简单的题目:

enum AccountType
{
    SAVING, FIXED, CURRENT;
    private AccountType()
    {
        System.out.println(“It is a account type”);
    }
}
class EnumOne
{
    public static void main(String[]args)
    {
        System.out.println(AccountType.FIXED);
    }
}

问打印的结果是啥?准确答案以下:

It is a account type
It is a account type
It is a account type
FIXED

至于结果为啥是这个,且看我逐步总结。

三、罗列的由来

存期近合理。

我贼喜好这句圣经,每次我一诠释不了它为何涌现的时刻,就不自发地用上这句话。

罗列肯定有他存在的代价,在一些时刻,我们须要定义一个类,这个类中的对象是有限且牢固的,比方我们一年有四个时节,春夏秋冬。

在罗列被支撑之前,我们该怎样定义这个Season类呢?可能会像下面如许:

public class Season {
    //private润饰组织器,没法随意建立对象
    private Season(){}
    //final润饰供应的对象在类外不能转变
    public static final Season SPRING = new Season();
    public static final Season SUMMER = new Season();
    public static final Season AUTUMN = new Season();
    public static final Season WINTER = new Season();
}

在定义上,这个Season类能够完成我们的预期,它们各自代表一个实例,且不能被转变,外部也不能随意建立实例。

但,经由历程自定义类完成罗列的结果有个明显的问题:代码量异常大。

因而,JDK1.5,罗列类应运而生。

四、罗列的定义情势

enum关键字用以定义罗列类,这是一个和classinterface关键字职位相称的关键字。也就是说,罗列类和我们之前运用的类差不太多,且enum和class润饰的类假如同名,会失足。

有一部分划定规矩,类须要遵照的,罗列类也遵照:

  • 罗列类也能够定义成员变量、组织器、平常和笼统要领等。
  • 一个Java源文件最多只能定义一个public的罗列类,且类名与文件名雷同。
  • 罗列类能够完成一个或多个接口。

也有一部分划定规矩,罗列类显得异乎寻常:

  • 罗列类的实例必须在罗列类的第一行显式列出,以逗号分开,列出的实例体系默许增加public static final润饰。
  • 罗列类的组织器默许私有,且只能是私有,能够重载。
  • 罗列类默许final润饰,没法被继续。
  • 罗列类都继续了java.lang.Enum类,所以没法继续其他的类。
  • 平常状况下,罗列常量须要用罗列类.罗列常量的体式格局挪用。

晓得这些今后,我们能够用enum关键字从新定义罗列类:

public enum Season{
    //定义四个实例
    SPRING,SUMMER,AUTUMN,WINTER;
}

须要注重的是,在JDK1.5罗列类到场今后,switch-case语句举行了扩大,其掌握表达式能够是恣意罗列范例,且能够直接运用罗列值的称号,无需增加罗列类作为限制。

五、Enum类里有啥?

Enum类是一切enum关键字润饰的罗列类的顶级父类,里头定义的要领默许状况下,是通用的,我们来瞅它一瞅:

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

我们能够发明,Enum实际上是一个继续自Object类的笼统类(Object类果然是顶级父类,不可撼动),并完成了两个接口:

  • Comparable:支撑罗列对象的比较。
  • Serializable:支撑罗列对象的序列化。

1、唯一的组织器

    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }

官方文档如许说的:程序员不能去挪用这个组织器,它用于编译器相应enum范例声明发出的代码,关于这一点,我们背面体会会越发深入一些。

2、重要的要领们

关于Object类中的要领,这边就不赘述了,重要提一提特别的要领。

public final String name()

返回这个罗列常量的称号。官方发起:大多数状况,最好运用toString()要领,因为能够返回一个友爱的名字。而name()要领以final润饰,没法被重写。

public String toString()

源码上看,toString()要领和name()要领是雷同的,然则发起:假若有更友爱的常量称号显现,能够重写toString()要领。

public final int ordinal()

返回此罗列常量的序号(其在enum声明中的位置,个中初始常量的序号为零)。

大多数程序员将不须要这类要领。它被用于庞杂的基于罗列的数据构造中,如EnumSet和EnumMap。

public final int compareTo(E o)

这个要领用于指定罗列对象比较次序,同一个罗列实例只能与雷同范例的罗列实例举行比较。

    public final int compareTo(E o) {
        Enum<?> other = (Enum<?>)o;
        Enum<E> self = this;
        if (self.getClass() != other.getClass() && // optimization
            //getDeclaringClass()要领返回该罗列常量对应Enum类的类对象
            self.getDeclaringClass() != other.getDeclaringClass())
            throw new ClassCastException();
        //该罗列常量次序在o常量之前,返回负整数
        return self.ordinal - other.ordinal;
    }

public static <T extends Enum > T valueOf(Class enumType,
String name)

该静态要领返回指定罗列类中指定称号的罗列常量。

3、凭空涌现的values()要领

为何会想到总结这个要领呢?实在也是有肯定心路历程的,官方文档特别强调了一句话:

Note that when using an enumeration type as the type of a set or as the type of the keys in a map, specialized and efficient set and map implementations are available.

平常Note开头的玩艺儿,照样比较重要的。大抵意义以下:

当运用罗列范例作为鸠合的范例或映照中的键的范例时,能够运用专门化且有用的鸠合和映照完成。

看完异常不明白,因而开始查找材料,发明有一种用法:

Arrays.asList(AccountType.values())

很明显挪用了这个罗列类的values()要领,然则适才对罗列类的要领一通剖析,也没看到有values()要领啊。然则编译器确切提醒,有,确切有!

Java:枚举类也就这么回事 IT教程 第1张

这是怎么回事呢?JDK文档是这么说的:

The compiler automatically adds some special methods when it creates an enum. For example, they have a static values method that returns an array containing all of the values of the enum in the order they are declared.

编译器会在建立一个罗列类的时刻,自动在里面到场一些特别的要领,比方静态的values()要领,它将返回一个数组,依据罗列常量声明的次序寄存它们。

如许一来,罗列类就能够和鸠合等玩艺儿很好地合营在一起了,细致咋合营,今后碰到了就晓得了。

关于这一点,待会反编译今后会越发印象深入。

六、反编译罗列类

注:因为学问尚浅,这部分内容总结起来虚虚的,然则总归查找了很多的材料,若有说的不对的处所,还望批评区批评指正。

那末,回到我们文章开头提到的那到面试题,我们依据结果来推想程序运转今后发作的状况:

  • 个中的组织器被挪用了三次,申明定义的罗列常量确切是三个活生生的实例,也就是说,每次建立实例就会挪用一次组织器。
  • 然后,System.out.println(AccountType.FIXED);将会挪用toString()要领,因为子类没有重写,那末将会返回name值,也就是"FIXED"

至此,我们的猜想完毕,实在确切也大差不差了,大抵就是这个历程。在一番查阅材料今后,我又尝试着去反编译这个罗列类文件:

我们先用javap -p AccountType.class敕令试着反编译今后检察一切类和成员。

Java:枚举类也就这么回事 IT教程 第2张

为了看看static中发作的状况,我试着用越发细致的指令,javap -c -l AccountType.class,试图猎取当地变量信息表和行号,虽然我大几率照样看不太懂的。

我们以个中一个为例,参看虚拟机字节码指令表,大抵历程以下:

  static {};
    Code:
       0: new           #4                  //建立一个对象,将其援用值压入栈
       3: dup                               //复制栈顶数值并将复制值压入栈顶
       4: ldc           #10                 //将String常量值SAVING从常量池推送至栈顶
       6: iconst_0                          //将int型0推送至栈顶
       7: invokespecial #11                 //挪用超类组织器
      10: putstatic     #12                 //为指定的静态域赋值

以下为由个人明白简化的编译构造:

public final class AccountType extends java.lang.Enum<AccountType> {
    //静态罗列常量
    public static final AccountType SAVING;

    public static final AccountType FIXED;

    public static final AccountType CURRENT;

    //存储静态罗列常量的私有静态域
    private static final AccountType[] $VALUES;

    //编译器新到场的静态要领
    public static AccountType[] values();

    //挪用实例要领猎取指定称号的罗列常量
    public static AccountType valueOf(java.lang.String);

    static {
        //建立对象,传入罗列常量名和次序
        SAVING = new AccountType("SAVING",0);
        FIXED = new AccountType("FIXED",1);
        CURRENT = new AccountType("CURRENT",2);
        //给静态域赋值
        $VALUES = new AccountType[]{
            SAVING,FIXED,CURRENT
        }
    };     
}

Enum类的组织器,在感应到enum关键字润饰的类今后,将会被挪用,传入罗列常量的字符串字面量值(name)和索引(ordinal),建立的实例存在私有静态域&VALUES中。

而且编译器确切会增加静态的values()要领,用以返回寄存罗列常量的数组。

七、罗列类完成单例

public enum  EnumSingleton {
    INSTANCE;
    public EnumSingleton getInstance(){
        return INSTANCE;
    }
}

这部分比及今后总结单例形式再侃,先在文末贴个地点。

 

objectarx 多段线自交检查

参与评论