Java8 Enum 源码,用法,官方文档

参考:
oracle官方文档
枚举类型入门
深入研究枚举类型
Java Enumeration (枚举类型) (1) – 基本概念

一.Enum源码分析

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

查看Enum源码,我们会发现Enum是abstract class 抽象类无法直接创建对象,只能被子类继承后,创建子类对象。
而Enum 又不能被继承,也就是只能用enum关键字来定义。
在这里插入图片描述

An enum type is a special data type that enables for a variable to bea set of predefined constants. The variable must be equal to one of the values that have been predefined for it.

This is the common base class of all Java language enumeration types. specialized and efficient {@linkplain java.util. EnumSet set} and {@linkplain java.util. EnumMap map} implementations are available.

Enumerated types are subclasses of java.lang.Enum, which is new in Java 5.0. (Enum is not itself an enumerated type.) You cannot produce an enumerated type by manually extending the Enum class, and it is a compilation error to attempt this. The only way to define an enumerated type is with the enum keyword.

Enum 实现了 Comparable, Serializable两个接口

但不用担心会产生新的实例

Enums implement java.io.Serializable so they can be serialized, but the Java serialization mechanism handles them specially to ensure that no new instances are ever created.

1.成员变量


	private final String name;
	private final int ordinal;
	
	public final String name() {return name;}
    public final int ordinal() {return ordinal; }
    
    public String toString() {
        return name;
    }

Enum有2个成员变量,和相应的方法。
name:枚举常量的名称,这个引用不能被改变
ordinal:枚举常量在枚举类中声明的顺序 从0开始
private final 不能被改变,子类也只能查看值
toString()和name()方法的不同之处在于toString()可以被重写。

1.final修饰的不能被继承(String、StringBuilder、StringBuffer、Math,不可变类),其中所有的方法都不能被重写(这里需要注意的是不能被重写,但是可以被重载,这里很多人会弄混),所以不能同时用abstract和final修饰类(abstract修饰的类是抽象类,抽象类是用于被子类继承的,和final起相反的作用);
2.final修饰的方法 不能被重写,但是子类可以用父类中final修饰的方法;
3.final修饰的成员变量 是不可变的,如果成员变量是基本数据类型,初始化之后成员变量的值不能被改变,如果成员变量是引用类型,那么它只能指向初始化时指向的那个对象,不能再指向别的对象,但是对象当中的内容是允许改变的。

在这里插入图片描述

2.构造方法

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

这是一个protected的方法 但只能由编译器调用

Sole constructor. Programmers cannot invoke this constructor.It is for use by code emitted by the compiler in response to enum type declarations.

Enumerated types have no public constructor. The only instances of an enumerated type are those declared by the enum.

3. clone()

protected final Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }

clone 方法不可以被重写(final). 并且使用会抛异常(CloneNotSupportedException()),这是为了保证枚举是单例的。

Enums are not Cloneable, so copies of the existing instances cannot be created.

Throws CloneNotSupportedException. This guarantees that enums are never cloned, which is necessary to preserve their “singleton” status.

4. equals(),hashCode()

    public final boolean equals(Object other) {
        return this==other;
    }
    public final int hashCode() {
        return super.hashCode();
    }

都是final方法不能被重写

Enumerated types do have a working equals( ) method, however. The method uses = = final so that it cannot be overridden. This working equals( ) method allows enumerated values to be used as members of collections such as Set, List, and Map. internally and is

Enumerated types have a working hashCode() method consistent with their equals( )equals(), hashCode( ) is final. It allows enumerated values to be used with classes like java.util.HashMap. method. Like

5. compareTo()

public final int compareTo(E o) {
        Enum<?> other = (Enum<?>)o;
        Enum<E> self = this;
        if (self.getClass() != other.getClass() && // optimization
            self.getDeclaringClass() != other.getDeclaringClass())
            throw new ClassCastException();
        return self.ordinal - other.ordinal;
    }

compareTo方法比较的是ordinal,也就是定义时的顺序,

Enumerated types implement java.lang.Comparable, and the compareTo() method orders enumerated values in the order in which they appear in the enum declaration.

二. 反编译

1 语法糖

参考:
什么是语法糖Java中的10颗语法糖Java语法糖系列四:枚举类型
语法糖(Syntactic Sugar):也称糖衣语法,指在计算机语言中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用。通常来说,使用语法糖能够增加程序的可读性,减少程序代码出错的机会。
enum就是一颗语法糖,编译器已经为我们做了如下的工作:

2 反编译后的代码分析

参考:
枚举操作枚举的操作续java枚举类型的实现原理字节码层面理解java枚举Enum
我们来创建一个枚举~

public enum FlowerEnum {
    ROSE,
    LILY,
    CUCKOO;
    @Override
    public String toString(){
      return "重写"+super.toString();
    }
}

找到class文件 javap -c -l FlowerEnum >flow.txt 反汇编后如下
参考: https://blog.csdn.net/woyixiaoren/article/details/84772418
在这里插入图片描述
整理一下大概像下面这样

Compiled from "FlowerEnum.java"
public final class com.example.junittest.controller.FlowerEnum 
			extends java.lang.Enum<com.example.junittest.controller.FlowerEnum> {
			
  public static final com.example.junittest.controller.FlowerEnum ROSE;

  public static final com.example.junittest.controller.FlowerEnum LILY;

  public static final com.example.junittest.controller.FlowerEnum CUCKOO;

  public static com.example.junittest.controller.FlowerEnum[] values();

  public static com.example.junittest.controller.FlowerEnum valueOf(java.lang.String);

  public java.lang.String toString();

  static {};
   
}

可以看出用enum定义的枚举
(1)是一个继承了Enum的(所以不能继承其他类),并且final决定枚举不能被继承。
(2)会生成对应的枚举常量
public static final FlowerEnum ROSE; 静态并且不可以被修改
因为ROSE实际是一个常量,所以我们在定义的时候用大写,_,符合常量的定义规范。

By convention, the values of enumerated types are written using all capital letters, just as other static final fields are.

(3)有两个静态方法
values() 返回枚举数组,常量顺序和声明顺序一样。
valueOf(String) 会调用Enum.valueOf(Class,String)方法返回同名Color实例返回对应的枚举,没有对应枚举会报错。

三. 枚举的原理

我们在定义枚举ROSE,LILY的时候相当于下面这样定义了静态常量,所以用逗号分隔分号结尾

public class Flower {
    private static final Flower ROSE = "rose", LILY = "lily";
}

枚举可以为空,但是如果有任何方法或者成员变量,IDE会提示必须有 ;,第一个;之前会被认为是枚举常量
在这里插入图片描述

实际我们在定义常量的时候,使用了FlowerEnum默认的无参构造方法,如果添加一个有参的构造方法(编译器不会再生成默认的无参构造方法),就不能用ROSE,LILY() 这种方式定义枚举了。

在这里插入图片描述
枚举的构造方法只能不写或private

It is a compile-time error if a constructor declaration of an enum type is public or protected.

子类的构造方法在调用前会先使用父类的构造方法,而Enum只有一个构造方法Enum(String name, int ordinal) 。所以相当于下面代码这样。

new Enum<FlowerEnum>("ROSE", 0);
new Enum<FlowerEnum>("LILY", 1);

四. 官方文档

英语不太好,就不都翻译了

8.9. Enums

An enum declaration specifies a new enum type .
在这里插入图片描述
Enum types (§8.9) must not be declared abstract; doing so will result in a compile-time error.
枚举是final的,除非他有一个包含方法体的枚举常量
An enum type is implicitly final unless it contains at least one enum constant that has a class body.
不能将枚举声明为final-
It is a compile-time error to explicitly declare an enum type to be final.
可以在类的内部定义枚举
Nested enum types are implicitly static. It is permissible to explicitly declare a nested enum type to be static.
This implies that it is impossible to define a local (§14.3) enum, or to define an enum in an inner class (§8.1.3).

The direct superclass of an enum type named E is Enum (§8.1.4).
只能在定义枚举的时候实例化枚举常量,其他显示式实例化枚举都会产生编译错误。
An enum type has no instances other than those defined by its enum constants. It is a compile-time error to attempt to explicitly instantiate an enum type (§15.9.1).
不能重写clone,特殊的序列化方法,禁止枚举类型的反射实例化 这四项保证了枚举成为单例
The final clone method in Enum ensures that enum constants can never be cloned, and the special treatment by the serialization mechanism ensures that duplicate instances are never created as a result of deserialization. Reflective instantiation of enum types is prohibited. Together, these four things ensure that no instances of an enum type exist beyond those defined by the enum constants.

8.9.1 Enum Constants

The body of an enum type may contain enum constants. An enum constant defines an instance of the enum type.
在这里插入图片描述
An enum constant may optionally be preceded by annotation modifiers. If an annotation a (§9.7) on an enum constant corresponds to an annotation type T (§9.6), and T has a (meta-)annotation m that corresponds to java.lang.annotation.Target, then m must have an element whose value is java.lang.annotation.ElementType.FIELD, or a compile-time error occurs.

The Identifier in a EnumConstant may be used in a name to refer to the enum constant.

The scope and shadowing of an enum constant is specified in §6.3 and §6.4.

An enum constant may be followed by arguments, which are passed to the constructor of the enum type when the constant is created during class initialization as described later in this section. The constructor to be invoked is chosen using the normal overloading rules (§15.12.2). If the arguments are omitted, an empty argument list is assumed.
有方法体的时候隐式定义了内部类
The optional class body of an enum constant implicitly defines an anonymous class declaration. (§15.9.5) that extends the immediately enclosing enum type. The class body is governed by the usual rules of anonymous classes; in particular it cannot contain any constructors.

Instance methods declared in these class bodies may be invoked outside the enclosing enum type only if they override accessible methods in the enclosing enum type.
内部类不能有抽象方法
It is a compile-time error for the class body of an enum constant to declare an abstract method.

Because there is only one instance of each enum constant, it is permissible to use the == operator in place of the equals method when comparing two object references if it is known that at least one of them refers to an enum constant.

The equals method in Enum is a final method that merely invokes super.equals on its argument and returns the result, thus performing an identity comparison.

Example 8.9.1-1. Iterating Over Enum Constants With An Enhanced for Loop
public class Test {
    enum Season { WINTER, SPRING, SUMMER, FALL }

    public static void main(String[] args) {
        for (Season s : Season.values())
            System.out.println(s);
    }
}

WINTER
SPRING
SUMMER
FALL

Example 8.9.1-2. Use Of java.util.EnumSet For Subranges
import java.util.EnumSet;

public class Test {
    enum Day { MONDAY, TUESDAY, WEDNESDAY,
               THURSDAY, FRIDAY, SATURDAY, SUNDAY }

    public static void main(String[] args) {
        System.out.print("Weekdays: ");
        for (Day d : EnumSet.range(Day.MONDAY, Day.FRIDAY))
            System.out.print(d + " ");
    }
}

Weekdays: MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY

8.9.2 Enum Body Declarations

Any constructor or member declarations within an enum declaration apply to the enum type exactly as if they had been present in the class body of a normal class declaration, unless explicitly stated otherwise.
构造方法不能是public or protected
It is a compile-time error if a constructor declaration of an enum type is public or protected.
默认无参构造方法
If an enum type has no constructor declarations, then a private constructor that takes no parameters (to match the implicit empty argument list) is automatically provided.
不能有finalize()方法
It is a compile-time error for an enum declaration to declare a finalizer. An instance of an enum type may never be finalized.
enum type E 不能有抽象方法除非……
It is a compile-time error for an enum type E to have an abstract method m as a member unless E has one or more enum constants, and all of E’s enum constants have class bodies that provide concrete implementations of m.

In addition to the members that an enum type E inherits from Enum, for each declared enum constant with the name n, the enum type has an implicitly declared public static final field named n of type E.
These fields are considered to be declared in the same order as the corresponding enum constants, before any static fields explicitly declared in the enum type.
Each such field is initialized to the enum constant that corresponds to it. Each such field is also considered to be annotated by the same annotations as the corresponding enum constant. The enum constant is said to be created when the corresponding field is initialized.

In addition, if E is the name of an enum type, then that type has the following implicitly implicitly declared static methodsl:

/**
* Returns an array containing the constants of this enum 
* type, in the order they're declared.  This method may be
* used to iterate over the constants as follows:
*
*    for(E c : E.values())
*        System.out.println(c);
*
* @return an array containing the constants of this enum 
* type, in the order they're declared
*/
public static E[] values();

/**
* Returns the enum constant of this type with the specified
* name.
* The string must match exactly an identifier used to declare
* an enum constant in this type.  (Extraneous whitespace 
* characters are not permitted.)
* 
* @return the enum constant with the specified name
* @throws IllegalArgumentException if this enum type has no
* constant with the specified name
*/
public static E valueOf(String name);

It follows that enum type declarations cannot contain fields that conflict with the enum constants, and cannot contain methods that conflict with the automatically generated methods (values() and valueOf(String)) or methods that override the final methods in Enum (equals(Object), hashCode(), clone(), compareTo(Object), name(), ordinal(), and getDeclaringClass()).

It is a compile-time error to reference a static field of an enum type: that is not a constant variable (§4.12.4) from constructors, instance initializer blocks, or instance variable initializer expressions of that type.

It is a compile-time error for the constructors, instance initializer blocks, or instance variable initializer expressions of an enum constant e to refer to e or to an enum constant of the same type that is declared to the right of e.

Example 8.9.2-1. Restriction On Enum Constant Self-Reference

Without this rule, apparently reasonable code would fail at run time due to the initialization circularity inherent in enum types. (A circularity exists in any class with a “self-typed” static field.) Here is an example of the sort of code that would fail:

import java.util.Map;
import java.util.HashMap;
enum Color {

    RED, GREEN, BLUE;

    static final Map<String,Color> colorMap = new HashMap<String,Color>();
    
    Color() { colorMap.put(toString(), this); }
}

枚举构造方法运行时,colorMap 还没有初始化
Static initialization of this enum type would throw a NullPointerException because the static variable colorMap is uninitialized when the constructors for the enum constants run. The restriction above ensures that such code won’t compile.

Note that the example can easily be refactored to work properly:

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);
    }
}

应该写在静态代码块中
The refactored version is clearly correct, as static initialization occurs top to bottom.

Example 8.9.2-2. Enum Type With Members
enum Coin {
    PENNY(1), NICKEL(5), DIME(10), QUARTER(25);
    Coin(int value) { this.value = value; }
    private final int value;
    public int value() { return value; }
}

Each enum constant arranges for a different value in the field value, passed in via a constructor. The field represents the value, in cents, of an American coin. Note that there are no restrictions on the type or number of parameters that may be declared by an enum type’s constructor.
switch支持枚举
A switch statement is useful for simulating the addition of a method to an enum type from outside the type. This example “adds” a color method to the Coin type, and prints a table of coins, their values, and their colors.

class Test {
    public static void main(String[] args) {
        for (Coin c : Coin.values())
            System.out.println(c + "\t\t" + c.value() + "\t" + color(c));
    }

    private enum CoinColor { COPPER, NICKEL, SILVER }
    
    private static CoinColor color(Coin c) {
        switch(c) {
            case PENNY:
                return CoinColor.COPPER;
            case NICKEL:
                return CoinColor.NICKEL;
            case DIME: case QUARTER:
                return CoinColor.SILVER;
            default:
                throw new AssertionError("Unknown coin: " + c);
        }
    }
}

This program produces the output:

PENNY 1 COPPER
NICKEL 5 NICKEL
DIME 10 SILVER
QUARTER 25 SILVER

Example 8.9.2-3. Multiple Enum Types

In the following program, a playing card class is built atop two simple enum types. Note that each enum type would be as long as the entire example in the absence of the enum facility:

import java.util.List;
import java.util.ArrayList;
class Card implements Comparable<Card>,
                      java.io.Serializable {
    public enum Rank { DEUCE, THREE, FOUR, FIVE, SIX, SEVEN,
                       EIGHT, NINE, TEN,JACK, QUEEN, KING, ACE }

    public enum Suit { CLUBS, DIAMONDS, HEARTS, SPADES }

    private final Rank rank;
    private final Suit suit;
    public Rank rank() { return rank; }
    public Suit suit() { return suit; }

    private Card(Rank rank, Suit suit) {
        if (rank == null || suit == null)
            throw new NullPointerException(rank + ", " + suit);
        this.rank = rank;
        this.suit = suit;
    }

    public String toString() { return rank + " of " + suit; }

    // Primary sort on suit, secondary sort on rank
    public int compareTo(Card c) {
        int suitCompare = suit.compareTo(c.suit);
        return (suitCompare != 0 ?
                    suitCompare :
                    rank.compareTo(c.rank));
    }

    private static final List<Card> prototypeDeck =
        new ArrayList<Card>(52);

    static {
        for (Suit suit : Suit.values())
            for (Rank rank : Rank.values())
                prototypeDeck.add(new Card(rank, suit));
    }

    // Returns a new deck
    public static List<Card> newDeck() {
        return new ArrayList<Card>(prototypeDeck);
    }
}

The following program exercises the Card class. It takes two integer parameters on the command line, representing the number of hands to deal and the number of cards in each hand:

import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
class Deal {
    public static void main(String args[]) {
        int numHands     = Integer.parseInt(args[0]);
        int cardsPerHand = Integer.parseInt(args[1]);
        List<Card> deck  = Card.newDeck();
        Collections.shuffle(deck);
        for (int i=0; i < numHands; i++)
            System.out.println(dealHand(deck, cardsPerHand));
    }

    /**
     * Returns a new ArrayList consisting of the last n
     * elements of deck, which are removed from deck.
     * The returned list is sorted using the elements'
     * natural ordering.
     */
    public static <E extends Comparable<E>>
    ArrayList<E> dealHand(List<E> deck, int n) {
        int deckSize = deck.size();
        List<E> handView = deck.subList(deckSize - n, deckSize);
        ArrayList<E> hand = new ArrayList<E>(handView);
        handView.clear();
        Collections.sort(hand);
        return hand;
    }
}

The program produces the output:

java Deal 4 3
[DEUCE of CLUBS, SEVEN of CLUBS, QUEEN of DIAMONDS]
[NINE of HEARTS, FIVE of SPADES, ACE of SPADES]
[THREE of HEARTS, SIX of HEARTS, TEN of SPADES]
[TEN of CLUBS, NINE of DIAMONDS, THREE of SPADES]

Example 8.9.2-4. Enum Constants with Class Bodies
enum Operation {
    PLUS {
        double eval(double x, double y) { return x + y; }
    },
    MINUS {
        double eval(double x, double y) { return x - y; }
    },
    TIMES {
        double eval(double x, double y) { return x * y; }
    },
    DIVIDED_BY {
        double eval(double x, double y) { return x / y; }
    };

    // Each constant supports an arithmetic operation
    abstract double eval(double x, double y);

    public static void main(String args[]) {
        double x = Double.parseDouble(args[0]);
        double y = Double.parseDouble(args[1]);
        for (Operation op : Operation.values())
            System.out.println(x + " " + op + " " + y +
                               " = " + op.eval(x, y));
    }
}

Constant-specific class bodies attach behaviors to the constants. The program produces the output:

java Operation 2.0 4.0
2.0 PLUS 4.0 = 6.0
2.0 MINUS 4.0 = -2.0
2.0 TIMES 4.0 = 8.0
2.0 DIVIDED_BY 4.0 = 0.5

The above pattern is much safer than using a switch statement in the base type (Operation), as the pattern precludes the possibility of forgetting to add a behavior for a new constant (since the enum declaration would cause a compile-time error).

五. Enum的其他方法

1.getDeclaringClass()

public final Class<E> getDeclaringClass() {
        Class<?> clazz = getClass();
        Class<?> zuper = clazz.getSuperclass();
        return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
}

2.valueOf(Class enumType, String name)

public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) {
        T result = enumType.enumConstantDirectory().get(name);
        if (result != null)
            return result;
        if (name == null)
            throw new NullPointerException("Name is null");
        throw new IllegalArgumentException(
            "No enum constant " + enumType.getCanonicalName() + "." + name);
}

3.finalize()

不可以重写清除方法

protected final void finalize() { }

4.readObject(ObjectInputStream in), readObjectNoData()

避免序列化

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");
}

参考:
深入理解Java枚举类型(enum)
深入理解final关键字
java 枚举Enum源码解析
为什么说Enum实现单例模式是最佳实践
equals 与==

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值