JAVA_浅析枚举

1.简介

==Enum 一般用来表示一组相同类型的常量==。如:性别、日期、月份、颜色等。对这些属性用常量的好处是显而易见的,不仅可以保证单例,且在比较的时候可以用 ”==” 来替换 equals。在 JDK1.5 之前是没有 Enum 这个类型的,那时候一般用接口常量来替代。

2.Enum究竟是啥

你是否被问到过以下的问题:

  • 枚举允许继承类吗?
  • 枚举允许实现接口吗?
  • 枚举可以用等号比较吗?
  • 可以继承枚举吗?
  • 枚举是单例吗?
  • 当使用compareTo()比较枚举时,比较的是什么?
  • 当使用equals()比较枚举的时候,比较的是什么?

面试官的问题五花八门,但归根结底都是在考察同一个问题:枚举的本质。

那么枚举究竟是啥?

废话不说,先看源代码:

1
2
3
4
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable {
//*******
}

从源代码可以看出Enum的本质是一个抽象类,所以上述问题基本上都迎刃而解了.

3.Enum的特性

  • Enum常量隐式的加上了static和final,一旦被创建就无法修改
  • Enum提供了是类型安全的(type-safe)
  • Enum隐式的加上了values()方法,返回一个数组包含所有的Enum常量
  • 两个Enum常量可以使用 ==equals()方法比较
  • Enum可以用在switch语句中,就像int,String。
  • Enum有自己的名称空间
  • Enum可以实现Java接口
  • 可以在Enum中定义构造器

4.Enum的注意事项

Enum的用法

1 . Enum的声明,Enum可以声明在一个类之外或者在类内部,但是不能在方法中。

Enum的用法

  1. Enum的声明,Enum可以声明在一个类之外或者在类内部,但是不能在方法中。
  2. ==<font color="red">在创建Enum常量的时候可以指定值,但是这个时候你需要定义一个成员变量构造器。构造器必须是private的,不然会报编译错误.</font>==

5.EumDemo

1)创建一个水果相关的枚举

1
2
3
public enum Fruit {  
APPLE, PEAR, PEACH, ORANGE;
}

再看看Fruit反编译的结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package test;  
public final class Fruit extends Enum {
private Fruit(String s, int i) {
super(s, i);
}
public static Fruit[] values() {
Fruit afruit[];
int i;
Fruit afruit1[];
System.arraycopy(afruit = ENUM$VALUES, 0, afruit1 = new Fruit[i = afruit.length], 0, i);
return afruit1;
}
public static Fruit valueOf(String s) {
return (Fruit)Enum.valueOf(test/Fruit, s);
}
public static final Fruit APPLE;
public static final Fruit PEAR;
public static final Fruit PEACH;
public static final Fruit ORANGE;
private static final Fruit ENUM$VALUES[];
static {
APPLE = new Fruit("APPLE", 0);
PEAR = new Fruit("PEAR", 1);
PEACH = new Fruit("PEACH", 2);
ORANGE = new Fruit("ORANGE", 3);
ENUM$VALUES = (new Fruit[] {
APPLE, PEAR, PEACH, ORANGE
});
}
}

注意这几行:

1
2
3
4
public static final Fruit APPLE;  
public static final Fruit PEAR;
public static final Fruit PEACH;
public static final Fruit ORANGE;

可以看到我们定义的几个成员变量,JVM底层把它转换成Eunm类型.

我们还是再写点代码看看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public enum Fruit {  
APPLE {
public void test() {
System.out.println("I am an apple.");
}
},
PEAR {
public void test() {
System.out.println("I am a pear.");
}
},
PEACH {
public void test() {
System.out.println("I am a peach.");
}
},
ORANGE;

public void test() {
System.out.println("I am a fruit.");
}
}

其中,只有Orange没有Overide test()方法;

在main方法里面调用:

1
2
3
4
5
6
public static void main(String[] args) {  
Fruit.APPLE.test();
Fruit.PEAR.test();
Fruit.PEACH.test();
Fruit.ORANGE.test();
}

输出结果:

1
2
3
4
I am an apple. 
I am a pear.
I am a peach.
I am a fruit.

可以看到,重新定义了test方法的APPLE,PEAR,PEACH覆盖了从父类继承过来的默认行为,而未从新定义test方法的ORANGE却沿袭了父类的行为,多态性在这里展现出来了。

再看看反编译后的文件:

发现多了几个内部类的字节码文件,看看反编译后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// Decompiled by Jad v1.5.8e. Copyright 2001 Pavel Kouznetsov.  
// Jad home page: http://www.geocities.com/kpdus/jad.html
// Decompiler options: packimports(3)
// Source File Name: Fruit.java
package test;
import java.io.PrintStream;
public class Fruit extends Enum {
private Fruit(String s, int i) {
super(s, i);
}
public void test() {
System.out.println("I am a fruit.");
}
public static Fruit[] values() {
Fruit afruit[];
int i;
Fruit afruit1[];
System.arraycopy(afruit = ENUM$VALUES, 0, afruit1 = new Fruit[i = afruit.length], 0, i);
return afruit1;
}
public static Fruit valueOf(String s) {
return (Fruit)Enum.valueOf(test/Fruit, s);
}
Fruit(String s, int i, Fruit fruit) {
this(s, i);
}
public static final Fruit APPLE;
public static final Fruit PEAR;
public static final Fruit PEACH;
public static final Fruit ORANGE;
private static final Fruit ENUM$VALUES[];

static {
APPLE = new Fruit("APPLE", 0) {
public void test()
{
System.out.println("I am an apple.");
}
};
PEAR = new Fruit("PEAR", 1) {

public void test()
{
System.out.println("I am a pear.");
}
};
PEACH = new Fruit("PEACH", 2) {

public void test()
{
System.out.println("I am a peach.");
}
};
ORANGE = new Fruit("ORANGE", 3);
ENUM$VALUES = (new Fruit[] {
APPLE, PEAR, PEACH, ORANGE
});
}
}

注意这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static   
{
APPLE = new Fruit("APPLE", 0) {
public void test()
{
System.out.println("I am an apple.");
}
};
PEAR = new Fruit("PEAR", 1) {
public void test()
{
System.out.println("I am a pear.");
}
};
PEACH = new Fruit("PEACH", 2) {
public void test()
{
System.out.println("I am a peach.");
}
};
ORANGE = new Fruit("ORANGE", 3);

 这个时候的APPLE,PEAR,PEACH已经以匿名内部类的方式对Fruit进行了Overide,自然体现出了多态,多出的那三个疑似内部类的class文件也就是它们!而ORANGE,没有重写test方法,仍然以一个Fruit实例的形式出现。

6.反思-Enum多态体现的好处

其实通过上面我们可以通过Enum(枚举类)实现单例工厂模式,这样就能省去多余的判断步骤和不必要的父类和其他的相关类,大大提高我们的代码效率.

<font color="red">练习小Demo​**</font>**

1.创建Dog和Persion类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com._521it.Enum.Model;

/**
* @Auther: yangxiangnan
* @Date: 2019/7/17 14:47
* @Description:
*/
public class Dog {

private String name;
private String age;

public Dog() {
System.out.println("狗狗的无参数构造器");
}

public Dog(String name, String age) {
this.name = name;
this.age = age;
System.out.println("狗狗的有参数构造器");
}

public void bark(){
System.out.println("汪汪");
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getAge() {
return age;
}

public void setAge(String age) {
this.age = age;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com._521it.Enum.Model;

/**
* @Auther: yangxiangnan
* @Date: 2019/7/17 14:51
* @Description:
*/
public class Persion {
private String name;
private String age;

public Persion() {
System.out.println("人的无参数构造器");
}

public Persion(String name, String age) {
this.name = name;
this.age = age;
System.out.println("人的有参数构造器");
}

public void speak(){
System.out.println("说话");
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getAge() {
return age;
}

public void setAge(String age) {
this.age = age;
}
}

2.创建单例工厂类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com._521it.Enum.Singletion;

import com._521it.Enum.Model.Dog;
import com._521it.Enum.Model.Persion;

/**
* @Auther: yangxiangnan
* @Date: 2019/7/17 14:52
* @Description:
*/
public enum SingletonInstanceFactory {

INSTANCE_FACTORY;

private Persion persion;
private Dog dog;

private SingletonInstanceFactory(){
persion = new Persion();
dog = new Dog();
}

public Persion getPersion(){
return persion;
}

public Dog getDog(){
return dog;
}
}

3.进行测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com._521it.Enum;

import com._521it.Enum.Model.Dog;
import com._521it.Enum.Model.Persion;
import com._521it.Enum.Singletion.SingletonInstanceFactory;
import org.junit.Test;

/**
* @Auther: yangxiangnan
* @Date: 2019/7/17 17:18
* @Description:
*/
public class Demo {
@Test
public void test01(){
Dog dog1 = SingletonInstanceFactory.INSTANCE_FACTORY.getDog();
Dog dog2 = SingletonInstanceFactory.INSTANCE_FACTORY.getDog();
System.out.println("dog1 = " + dog1);
System.out.println("dog2 = " + dog2);
dog1.bark();
dog2.bark();
System.out.println(dog1 == dog2);

Persion persion1 = SingletonInstanceFactory.INSTANCE_FACTORY.getPersion();
Persion persion2 = SingletonInstanceFactory.INSTANCE_FACTORY.getPersion();
System.out.println("persion1 = " + persion1);
System.out.println("persion2 = " + persion2);
persion1.speak();
persion2.speak();
System.out.println(persion1 == persion2);
}



}

4.运行结果

结果如下:


JAVA_浅析枚举
https://github.com/yangxiangnanwill/yangxiangnanwill.github.io/2024/01/03/好好码代码吖/JAVA/JAVA特性/JAVA_浅析枚举/
作者
will
发布于
2024年1月3日
许可协议