A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© aqiu 中级黑马   /  2019-5-15 10:22  /  768 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 aqiu 于 2019-5-15 10:24 编辑

首先先抛出几个问题:
1.枚举允许继承类吗?
2.枚举允许实现接口吗?
3.枚举可以用等号比较吗?
4.可以继承枚举吗?
5.枚举可以实现单例模式吗?
6. 当使用compareTo()比较枚举时,比较的是什么?
7. 当使用equals()比较枚举的时候,比较的是什么?


下面我们就来详谈以下枚举类:
先来个Demo
[Java] 纯文本查看 复制代码
public enum Fruit{
    APPLE(1),ORANGE(2),BANANA(3);
    int code;

    Fruit(int code){
        this.code=code;
    }
}

反编译Fruit.class文件之后,可以得到如下内容:
[Java] 纯文本查看 复制代码
public final class Fruit extends Enum
{

    public static Fruit[] values()
    {
        return (Fruit[])$VALUES.clone();
    }

    public static Fruit valueOf(String s)
    {
        return (Fruit)Enum.valueOf(Fruit, s);
    }

    private Fruit(String s, int i, int j)
    {
        super(s, i);
        code = j;
    }

    public static final Fruit APPLE;
    public static final Fruit ORANGE;
    public static final Fruit BANANA;
    int code;
    private static final Fruit $VALUES[];

    static
    {
        APPLE = new Fruit("APPLE", 0, 1);
        ORANGE = new Fruit("ORANGE", 1, 2);
        BANANA = new Fruit("BANANA", 2, 3);
        $VALUES = (new Fruit[] {
            APPLE, ORANGE, BANANA
        });}
}


可见,Jvm编译器背地里是使用上面的方式来处理枚举的。它做了几件事:
  • 1.定义一个继承自Enum类的Fruit类,Fruit类是用final修饰的
  • 2.为每个枚举实例对应创建一个类对象,这些类对象是用public static final修饰的。同时生成一个数组,用于保存全部的类对象
  • 3.生成一个静态代码块,用于初始化类对象和类对象数组
  • 4.生成一个构造函数,构造函数包含自定义参数和两个默认参数(下文会讲解这两个默认参数)
  • 5.生成一个静态的values()方法,用于返回所有的类对象
  • 6.生成一个静态的valueOf()方法,根据name参数返回对应的类实例(下文会讲解name参数)
  • 关于最基本的Enum类,Jdk文档是这样描述的:
  • This is the common base class of all Java language enumeration types. More information about enums, including descriptions of the implicitly declared methods synthesized by the compiler, can be found in section 8.9 ofThe Java™ Language Specification.
    这是Java语言中所有枚举类型的基础类,更多的关于枚举的信息,包括一些编译器隐式声明的方法的描述,可以在The Java™ Language Specification文档的8.9小节找到。

  • 源码解析:
    [Java] 纯文本查看 复制代码
    public abstract class Enum<E extends java.lang.Enum<E>>
            implements Comparable<E>, Serializable {
        private final String name;
        private final int ordinal;
        
        public final String name() {
            return name;
        }
        
        public final int ordinal() {
            return ordinal;
        }
        
        protected Enum(String name, int ordinal) {
            this.name = name;
            this.ordinal = ordinal;
        }
        public String toString() {
            return name;
        }
        public final boolean equals(Object other) {
            return this == other;
        }
        public final int hashCode() {
            return super.hashCode();
        }
        protected final Object clone() throws CloneNotSupportedException {
            throw new CloneNotSupportedException();
        }
    
        public final int compareTo(E o) {
            java.lang.Enum other = (java.lang.Enum) o;
            java.lang.Enum self = this;
            if (self.getClass() != other.getClass() && // optimization
                    self.getDeclaringClass() != other.getDeclaringClass())
                throw new ClassCastException();
            return self.ordinal - other.ordinal;
        }
    
        public final Class<E> getDeclaringClass() {
            Class clazz = getClass();
            Class zuper = clazz.getSuperclass();
            return (zuper == java.lang.Enum.class) ? clazz : zuper;
        }
    
        public static <T extends java.lang.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);
        }
    
        protected final void finalize() {
        }
    
        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");
        }
    }

  • 在Enum源代码中,有以下几个值得关注的点:

  • 1.Enum类有两个成员变量:name和ordinal。其中,name用于记录枚举常量的名字。比如APPLE、ORANGE和BANANA。ordinal用于记录枚举常量在声明时的顺序(从0开始)。比如APPLE是0、ORANGE是1、BANANA是2。

    • 2.Enum类有一个构造函数,它有两个入参,分别为name和ordianl赋值。
    • 3.Enum类重写了toString()方法,返回枚举常量的name值。
    • 4.Enum类重写了equals()方法,直接用等号比较。
    • 5.Enum类不允许克隆,clone()方法直接抛出异常。(保证枚举永远是单例的)
    • 6.Enum类实现了Comparable接口,直接比较枚举常量的ordinal的值。
    • 7.Enum类有一个静态的valueOf()方法,可以根据枚举类型以及name返回对应的枚举常量。
    • 8.Enum类不允许反序列化,为了保证枚举永远是单例的。

    到这里,再来回答以上几个题目的答案。
    1. 枚举不允许继承类。Jvm在生成枚举时已经继承了Enum类,由于Java语言是单继承,不支持再继承额外的类(唯一的继承名额被Jvm用了)。
    2. 枚举允许实现接口。因为枚举本身就是一个类,类是可以实现多个接口的。
    3. 枚举可以用等号比较。Jvm会为每个枚举实例对应生成一个类对象,这个类对象是用public static final修饰的,在static代码块中初始化,是一个单例。
    4. 不可以继承枚举。因为Jvm在生成枚举类时,将它声明为final。
    5. 枚举本身就是一种对单例设计模式友好的形式,它是实现单例模式的一种很好的方式。
    6. 枚举类型的compareTo()方法比较的是枚举类对象的ordinal的值。
    7. 枚举类型的equals()方法比较的是枚举类对象的内存地址,作用与等号等价。

    以上原文借鉴知乎https://zhuanlan.zhihu.com/p/55327937

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马