【码农每日一题】Java 枚举(Part 1)相关面试题

917 阅读4分钟
关注一下嘛,又不让你背锅!

问:Java 枚举类比较用 == 还是 equals,有啥区别?

答:java 枚举值比较用 == 和 equals 方法没啥区别,两个随便用都是一样的效果。因为枚举 Enum 类的 equals 方法默认实现就是通过 == 来比较的;类似的 Enum 的 compareTo 方法比较的是 Enum 的 ordinal 顺序大小;类似的还有 Enum 的 name 方法和 toString 方法一样都返回的是 Enum 的 name 值。

问:简单谈谈你理解的 Java 枚举本质原理?

答:java 枚举的本质原理是通过普通类来实现的,只是编译器为我们进行了加工处理,每个枚举类型编译后的字节码实质都继承自 java.lang.Enum 的枚举类型同名普通类,而每个枚举常量实质是一个枚举类型同名普通类的静态常量对象,所有枚举常量都通过静态代码块进行初始化实例赋值(由于是静态块,所以在类加载期间就初始化了)。为了加深理解可以通过下面的例子说明:

  1. public enum Status {

  2.    START("a"),

  3.    RUNNING("b"),

  4.    STOP();

  5.    private Status() {

  6.        this("def");

  7.    }

  8.    private Status(String name) {

  9.        this.name = name;

  10.    }

  11.    public String name;

  12. }

我们对如上枚举类型进行 javac 编译后通过 javap -v Status.class 可以查看其编译后字节码如下:

  1. ......

  2. public final class Status extends java.lang.Enum<Status>

  3.  ......

  4. {

  5.  //枚举类型值都成了Status类型类的静态常量成员属性

  6.  public static final Status START;

  7.  public static final Status RUNNING;

  8.  public static final Status STOP;

  9.  public java.lang.String name;

  10.  public static Status[] values();

  11.    ......

  12.  public static Status valueOf(java.lang.String);

  13.    ......

  14.  //静态代码块,类加载时执行

  15.  static {};

  16.    flags: ACC_STATIC

  17.    Code:

  18.      stack=5, locals=0, args_size=0

  19.         //创建一个Status对象通过参数为字符串常量"a"的构造方法初始化赋值给START

  20.         0: new           #4    // class Status

  21.         3: dup

  22.         4: ldc           #10    // String START

  23.         6: iconst_0

  24.         7: ldc           #11    // String a

  25.         9: invokespecial #7    // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V

  26.        12: putstatic     #12    // Field START:LStatus;

  27.        ......

  28. }

上面例子已经解释的很清楚了,记住枚举的本质是编译器处理成了类,枚举值为类的静态常量属性,其属性在类加载时的静态代码块中被初始化实例赋值。枚举可以有修饰符不大于默认修饰符的构造方法(修饰符可为 private,不可为 public 等)等,枚举只是一种语法糖,被编译器生成了最终的类而已。

所以枚举类型其实和我们自己使用 Java 普通类实现的类似,如下:

  1. public class Status {

  2.    public static final Status START;

  3.    public static final Status RUNNING;

  4.    public static final Status STOP;

  5.    static {

  6.        START = new Status("a");

  7.        RUNNING = new Status("b");

  8.        STOP = new Status();

  9.    }

  10.    private Status() {

  11.        this("def");

  12.    }

  13.    private Status(String name) {

  14.        this.name = name;

  15.    }

  16.    public String name;

  17. }

所以从某种意义上可以说 JDK 1.5 后引入的枚举类型是上面枚举常量类的代码封装而已。

问:Java 枚举类与常量的区别有哪些,有啥优缺点?

答:枚举相对于常量类来说定义更简单,其不需要定义枚举值,而常量类中的每个常量必须要手动添加值。枚举作为参数使用时可以在编译时避免弱类型错误,而常量类中的常量作为参数使用时在编译时无法避免弱类型错误(譬如常量类型为 int,参数传递一个常量类中没定义的 int 值)。枚举自动具备内置方法(如 values 方法可以获得所有值的集合来遍历,ordinal 方法可以获得排序值,compareTo 方法可以基于 ordinal 比较),而常量类默认不具备这些方法。枚举的缺点就是不能被继承(编译后生成的类是 final class 的),也不能通过 extends 继承其他类(枚举类编译后实质就是继承了 Enum 类,Java 是单继承机制),但是定义的枚举类可以通过 implements 实现其他接口,枚举值定义完毕后除非修改重构,否则无法做扩展,而常量类可以随意继承。

戛然而止!这是 Part 1 部分,Part 2 部分请明早准时抄收~~

老铁们,别嫌短,长了你肯定不会看完的,所以这就是码农每日一题的宗旨(其他历史文章请查看公众号历史记录)~

看完分享一波嘛,和你的小伙伴一起讨论才更加有意思,右上角分享 666~

看个笑话放松一下

“这位同学,请问你知道《边城》吗?”“呸!别跟我提编程,老子这辈子最讨厌的就是编程!”

It's lucky to gain and fated to miss.

 

得之,我幸。不得,我命。