十年網(wǎng)站開發(fā)經(jīng)驗(yàn) + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊(duì)
量身定制 + 運(yùn)營維護(hù)+專業(yè)推廣+無憂售后,網(wǎng)站問題一站解決
前言
創(chuàng)新互聯(lián)建站于2013年成立,先為鹿泉等服務(wù)建站,鹿泉等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為鹿泉企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。
在 Java 中, 枚舉, 也稱為枚舉類型, 其是一種特殊的數(shù)據(jù)類型, 它使得變量能夠稱為一組預(yù)定義的常量。 其目的是強(qiáng)制編譯時(shí)類型安全。
枚舉類更加直觀,類型安全。使用常量會(huì)有以下幾個(gè)缺陷:
1. 類型不安全。若一個(gè)方法中要求傳入季節(jié)這個(gè)參數(shù),用常量的話,形參就是int類型,開發(fā)者傳入任意類型的int類型值就行,但是如果是枚舉類型的話,就只能傳入枚舉類中包含的對(duì)象。
2. 沒有命名空間。開發(fā)者要在命名的時(shí)候以SEASON_開頭,這樣另外一個(gè)開發(fā)者再看這段代碼的時(shí)候,才知道這四個(gè)常量分別代表季節(jié)。
因此, 在 Java 中, enum 是保留的關(guān)鍵字。

1. 枚舉的定義
在 Java 是在 JDK 1.4 時(shí)決定引入的, 其在 JDK 1.5 發(fā)布時(shí)正式發(fā)布的。
舉一個(gè)簡單的例子:以日常生活中的方向來定義, 因?yàn)槠涿Q, 方位等都是確定, 一提到大家就都知道。
1.1 傳統(tǒng)的非枚舉方法
如果不使用枚舉, 我們可能會(huì)這樣子定義
public class Direction {
public static final int EAST = 0;
public static final int WEST = 1;
public static final int SOUTH = 2;
public static final int NORTH = 3;
}以上的定義也是可以達(dá)到定義的, 我們?cè)谑褂脮r(shí)
@Test
public void testDirection() {
System.out.println(getDirectionName(Direction.EAST));
System.out.println(getDirectionName(5));// 也可以這樣調(diào)用
}
public String getDirectionName(int type) {
switch (type) {
case Direction.EAST:
return "EAST";
case Direction.WEST:
return "WEST";
case Direction.SOUTH:
return "SOUTH";
case Direction.NORTH:
return "NORTH";
default:
return "UNKNOW";
}
}運(yùn)行起來也沒問題。 但是, 我們就如同上面第二種調(diào)用方式一樣, 其實(shí)我們的方向就在 4 種范圍之內(nèi),但在調(diào)用的時(shí)候傳入不是方向的一個(gè) int 類型的數(shù)據(jù), 編譯器是不會(huì)檢查出來的。
1.2 枚舉方法
我們使用枚舉來實(shí)現(xiàn)上面的功能
定義
public enum DirectionEnum {
EAST, WEST, NORTH, SOUTH
}測試
@Test
public void testDirectionEnum() {
System.out.println(getDirectionName(DirectionEnum.EAST));
// System.out.println(getDirectionName(5));// 編譯錯(cuò)誤
}
public String getDirectionName(DirectionEnum direction) {
switch (direction) {
case EAST:
return "EAST";
case WEST:
return "WEST";
case SOUTH:
return "SOUTH";
case NORTH:
return "NORTH";
default:
return "UNKNOW";
}
}以上只是一個(gè)舉的例子, 其實(shí), 枚舉中可以很方便的獲取自己的名稱。
通過使用枚舉, 我們可以很方便的限制了傳入的參數(shù), 如果傳入的參數(shù)不是我們指定的類型, 則就發(fā)生錯(cuò)誤。
1.3 定義總結(jié)
以剛剛的代碼為例
public enum DirectionEnum {
EAST, WEST, NORTH, SOUTH
}2 枚舉的本質(zhì)
枚舉在編譯時(shí), 編譯器會(huì)將其編譯為 Java 中 java.lang.Enum 的子類。
我們將上面的 DirectionEnum 進(jìn)行反編譯, 可以獲得如下的代碼:
// final:無法繼承
public final class DirectionEnum extends Enum
{
// 在之前定義的實(shí)例
public static final DirectionEnum EAST;
public static final DirectionEnum WEST;
public static final DirectionEnum NORTH;
public static final DirectionEnum SOUTH;
private static final DirectionEnum $VALUES[];
// 編譯器添加的 values() 方法
public static DirectionEnum[] values()
{
return (DirectionEnum[])$VALUES.clone();
}
// 編譯器添加的 valueOf 方法, 調(diào)用父類的 valueOf 方法
public static DirectionEnum valueOf(String name)
{
return (DirectionEnum)Enum.valueOf(cn/homejim/java/lang/DirectionEnum, name);
}
// 私有化構(gòu)造函數(shù), 正常情況下無法從外部進(jìn)行初始化
private DirectionEnum(String s, int i)
{
super(s, i);
}
// 靜態(tài)代碼塊初始化枚舉實(shí)例
static
{
EAST = new DirectionEnum("EAST", 0);
WEST = new DirectionEnum("WEST", 1);
NORTH = new DirectionEnum("NORTH", 2);
SOUTH = new DirectionEnum("SOUTH", 3);
$VALUES = (new DirectionEnum[] {
EAST, WEST, NORTH, SOUTH
});
}
}通過以上反編譯的代碼, 可以發(fā)現(xiàn)以下幾個(gè)特點(diǎn)
2.1 繼承 java.lang.Enum
通過以上的反編譯, 我們知道了, java.lang.Enum 是所有枚舉類型的基類。查看其定義
public abstract class Enum> implements Comparable , Serializable {
可以看出來, java.lang.Enum 有如下幾個(gè)特征
因此, 相對(duì)應(yīng)的, 枚舉類型也可以進(jìn)行比較和序列化
2.2 final 類型
final 修飾, 說明枚舉類型是無法進(jìn)行繼承的
2.3 枚舉常量本身就是該類的實(shí)例對(duì)象
可以看到, 我們定義的常量, 在類內(nèi)部是以實(shí)例對(duì)象存在的, 并使用靜態(tài)代碼塊進(jìn)行了實(shí)例化。
2.4 構(gòu)造函數(shù)私有化
不能像正常的類一樣, 從外部 new 一個(gè)對(duì)象出來。
2.5 添加了 $values[] 變量及兩個(gè)方法
3 枚舉的一般使用
枚舉默認(rèn)是有幾個(gè)方法的
3.1 類本身的方法
從前面我的分析, 我們得出, 類本身有兩個(gè)方法, 是編譯時(shí)添加的
3.1.1 values()
先看其源碼
public static DirectionEnum[] values() {
return (DirectionEnum[])$VALUES.clone();
}返回的是枚舉常量的克隆數(shù)組。
使用示例
@Test
public void testValus() {
DirectionEnum[] values = DirectionEnum.values();
for (DirectionEnum direction:
values) {
System.out.println(direction);
}
}輸出
EAST
WEST
NORTH
SOUTH
3.1.2 valueOf(String)
該方法通過字符串獲取對(duì)應(yīng)的枚舉常量
@Test
public void testValueOf() {
DirectionEnum east = DirectionEnum.valueOf("EAST");
System.out.println(east.ordinal());// 輸出0
}3.2 繼承的方法
因?yàn)槊杜e類型繼承于 java.lang.Enum, 因此除了該類的私有方法, 其他方法都是可以使用的。
3.2.1 ordinal()
該方法返回的是枚舉實(shí)例的在定義時(shí)的順序, 類似于數(shù)組, 第一個(gè)實(shí)例該方法的返回值為 0。
在基于枚舉的復(fù)雜數(shù)據(jù)結(jié)構(gòu) EnumSet和EnumMap 中會(huì)用到該函數(shù)。
@Test
public void testOrdinal() {
System.out.println(DirectionEnum.EAST.ordinal());// 輸出 0
System.out.println(DirectionEnum.NORTH.ordinal()); // 輸出 2
}3.2.2 compareTo()
該方法時(shí)實(shí)現(xiàn)的 Comparable 接口的, 其實(shí)現(xiàn)如下
public final int compareTo(E o) {
Enum<?> other = (Enum<?>)o;
Enum self = this;
if (self.getClass() != other.getClass() && // optimization
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
} 首先, 需要枚舉類型是同一種類型, 然后比較他們的 ordinal 來得出大于、小于還是等于。
@Test
public void testCompareTo() {
System.out.println(DirectionEnum.EAST.compareTo(DirectionEnum.EAST) == 0);// true
System.out.println(DirectionEnum.WEST.compareTo(DirectionEnum.EAST) > 0); // true
System.out.println(DirectionEnum.WEST.compareTo(DirectionEnum.SOUTH) < 0); // true
}3.2.3 name() 和 toString()
該兩個(gè)方法都是返回枚舉常量的名稱。 但是, name() 方法時(shí) final 類型, 是不能被覆蓋的! 而 toString 可以被覆蓋。
3.2.4 getDeclaringClass()
獲取對(duì)應(yīng)枚舉類型的 Class 對(duì)象
@Test
public void testGetDeclaringClass() {
System.out.println(DirectionEnum.WEST.getDeclaringClass());
// 輸出 class cn.homejim.java.lang.DirectionEnum
}2.3.5 equals
判斷指定對(duì)象與枚舉常量是否相同
@Test
public void testEquals() {
System.out.println(DirectionEnum.WEST.equals(DirectionEnum.EAST)); // false
System.out.println(DirectionEnum.WEST.equals(DirectionEnum.WEST)); // true
}4 枚舉類型進(jìn)階
枚舉類型通過反編譯我們知道, 其實(shí)也是一個(gè)類(只不過這個(gè)類比較特殊, 加了一些限制), 那么, 在類上能做的一些事情對(duì)其也是可以做的。 但是, 個(gè)別的可能會(huì)有限制(方向吧, 編譯器會(huì)提醒我們的)
4.1 自定義構(gòu)造函數(shù)
首先, 定義的構(gòu)造函數(shù)可以是 private, 或不加修飾符

自定義構(gòu)造函數(shù)
我們給每個(gè)方向加上一個(gè)角度
public enum DirectionEnum {
EAST(0), WEST(180), NORTH(90), SOUTH(270);
private int angle;
DirectionEnum(int angle) {
this.angle = angle;
}
public int getAngle() {
return angle;
}
}測試
@Test
public void testConstructor() {
System.out.println(DirectionEnum.WEST.getAngle()); // 180
System.out.println(DirectionEnum.EAST.getAngle()); // 0
}4.2 添加自定義的方法
以上的 getAngle 就是我們添加的自定義的方法
4.2.1 自定義具體方法
我們?cè)诿杜e類型內(nèi)部加入如下具體方法
protected void move() {
System.out.println("You are moving to " + this + " direction");
}測試
@Test
public void testConcreteMethod() {
DirectionEnum.WEST.move();
DirectionEnum.NORTH.move();
}輸出
You are moving to WEST direction
You are moving to NORTH direction
4.2.2 在枚舉中定義抽象方法
在枚舉類型中, 也是可以定義 abstract 方法的
我們?cè)贒irectinEnum中定義如下的抽象方法
abstract String onDirection();
定義完之后, 發(fā)現(xiàn)編譯器報(bào)錯(cuò)了, 說我們需要實(shí)現(xiàn)這個(gè)方法

按要求實(shí)現(xiàn)

測試
@Test
public void testAbstractMethod() {
System.out.println(DirectionEnum.EAST.onDirection());
System.out.println(DirectionEnum.SOUTH.onDirection());
}輸出
EAST direction 1
NORTH direction 333
也就是說抽象方法會(huì)強(qiáng)制要求每一個(gè)枚舉常量自己實(shí)現(xiàn)該方法。 通過提供不同的實(shí)現(xiàn)來達(dá)到不同的目的。
4.3 覆蓋父類方法
在父類 java.lang.Enum 中, 也就只有 toString() 是沒有使用 final 修飾啦, 要覆蓋也只能覆蓋該方法。 該方法的覆蓋相信大家很熟悉, 在此就不做過多的講解啦
4.4 實(shí)現(xiàn)接口
因?yàn)镴ava是單繼承的, 因此, Java中的枚舉因?yàn)橐呀?jīng)繼承了 java.lang.Enum, 因此不能再繼承其他的類。
但Java是可以實(shí)現(xiàn)多個(gè)接口的, 因此 Java 中的枚舉也可以實(shí)現(xiàn)接口。
定義接口
public interface TestInterface {
void doSomeThing();
}實(shí)現(xiàn)接口
public enum DirectionEnum implements TestInterface{
// 其他代碼
public void doSomeThing() {
System.out.println("doSomeThing Implement");
}
// 其他代碼
}測試
@Test
public void testImplement() {
DirectionEnum.WEST.doSomeThing(); // 輸出 doSomeThing Implement
}5 使用枚舉實(shí)現(xiàn)單例
該方法是在 《Effective Java》 提出的
public enum Singlton {
INSTANCE;
public void doOtherThing() {
}
}使用枚舉的方式, 保證了序列化機(jī)制, 絕對(duì)防止多次序列化問題, 保證了線程的安全, 保證了單例。 同時(shí), 防止了反射的問題。
該方法無論是創(chuàng)建還是調(diào)用, 都是很簡單。 《Effective Java》 對(duì)此的評(píng)價(jià):
單元素的枚舉類型已經(jīng)成為實(shí)現(xiàn)Singleton的最佳方法。
6 枚舉相關(guān)的集合類
java.util.EnumSet 和 java.util.EnumMap, 在此不進(jìn)行過多的講述了。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)創(chuàng)新互聯(lián)的支持。