一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務(wù)器之家 - 編程語言 - Java教程 - 扒一扒 Java 中的枚舉類型

扒一扒 Java 中的枚舉類型

2021-06-18 14:16阿進的寫字臺 Java教程

這篇文章主要給大家介紹了Java中枚舉類型的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

前言

在 java 中, 枚舉, 也稱為枚舉類型, 其是一種特殊的數(shù)據(jù)類型, 它使得變量能夠稱為一組預(yù)定義的常量。 其目的是強制編譯時類型安全。

枚舉類更加直觀,類型安全。使用常量會有以下幾個缺陷:

1. 類型不安全。若一個方法中要求傳入季節(jié)這個參數(shù),用常量的話,形參就是int類型,開發(fā)者傳入任意類型的int類型值就行,但是如果是枚舉類型的話,就只能傳入枚舉類中包含的對象。

2. 沒有命名空間。開發(fā)者要在命名的時候以season_開頭,這樣另外一個開發(fā)者再看這段代碼的時候,才知道這四個常量分別代表季節(jié)。

因此, 在 java 中, enum 是保留的關(guān)鍵字。

扒一扒 Java 中的枚舉類型

1. 枚舉的定義

在 java 是在 jdk 1.4 時決定引入的, 其在 jdk 1.5 發(fā)布時正式發(fā)布的。

舉一個簡單的例子:以日常生活中的方向來定義, 因為其名稱, 方位等都是確定, 一提到大家就都知道。

1.1 傳統(tǒng)的非枚舉方法

如果不使用枚舉, 我們可能會這樣子定義

?
1
2
3
4
5
6
7
8
9
10
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á)到定義的, 我們在使用時

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@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";
}
}

運行起來也沒問題。 但是, 我們就如同上面第二種調(diào)用方式一樣, 其實我們的方向就在 4 種范圍之內(nèi),但在調(diào)用的時候傳入不是方向的一個 int 類型的數(shù)據(jù), 編譯器是不會檢查出來的。

1.2 枚舉方法

我們使用枚舉來實現(xiàn)上面的功能

定義

?
1
2
3
public enum directionenum {
 east, west, north, south
}

測試

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@test
public void testdirectionenum() {
system.out.println(getdirectionname(directionenum.east));
// system.out.println(getdirectionname(5));// 編譯錯誤
}
 
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";
}
}

以上只是一個舉的例子, 其實, 枚舉中可以很方便的獲取自己的名稱。

通過使用枚舉, 我們可以很方便的限制了傳入的參數(shù), 如果傳入的參數(shù)不是我們指定的類型, 則就發(fā)生錯誤。

1.3 定義總結(jié)

以剛剛的代碼為例

?
1
2
3
public enum directionenum {
 east, west, north, south
}
  • 枚舉類型的定義跟類一樣, 只是需要將 class 替換為 enum
  • 枚舉名稱與類的名稱遵循一樣的慣例來定義
  • 枚舉值由于是常量, 一般推薦全部是大寫字母
  • 多個枚舉值之間使用逗號分隔開
  • 最好是在編譯或設(shè)計時就知道值的所有類型, 比如上面的方向, 當(dāng)然后面也可以增加

2 枚舉的本質(zhì)

枚舉在編譯時, 編譯器會將其編譯為 java 中 java.lang.enum 的子類。

我們將上面的 directionenum 進行反編譯, 可以獲得如下的代碼:

?
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
// final:無法繼承
public final class directionenum extends enum
{
 // 在之前定義的實例
 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ù), 正常情況下無法從外部進行初始化
 private directionenum(string s, int i)
 {
 super(s, i);
 }
 
 // 靜態(tài)代碼塊初始化枚舉實例
 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)以下幾個特點

2.1 繼承 java.lang.enum

通過以上的反編譯, 我們知道了, java.lang.enum 是所有枚舉類型的基類。查看其定義

?
1
2
public abstract class enum<e extends enum<e>>
 implements comparable<e>, serializable {

可以看出來, java.lang.enum 有如下幾個特征

  • 抽象類, 無法實例化
  • 實現(xiàn)了 comparable 接口, 可以進行比較
  • 實現(xiàn)了 serializable 接口, 可進行序列化

因此, 相對應(yīng)的, 枚舉類型也可以進行比較和序列化

2.2 final 類型

final 修飾, 說明枚舉類型是無法進行繼承的

2.3 枚舉常量本身就是該類的實例對象

可以看到, 我們定義的常量, 在類內(nèi)部是以實例對象存在的, 并使用靜態(tài)代碼塊進行了實例化。

2.4 構(gòu)造函數(shù)私有化
不能像正常的類一樣, 從外部 new 一個對象出來。

2.5 添加了 $values[] 變量及兩個方法

  • $values[]: 一個類型為枚舉類本身的數(shù)組, 存儲了所有的示例類型
  • values() : 獲取以上所有實例變量的克隆值
  • valueof(): 通過該方法可以通過名稱獲得對應(yīng)的枚舉常量

3 枚舉的一般使用

枚舉默認(rèn)是有幾個方法的

3.1 類本身的方法

從前面我的分析, 我們得出, 類本身有兩個方法, 是編譯時添加的

3.1.1 values()

先看其源碼

?
1
2
3
public static directionenum[] values() {
return (directionenum[])$values.clone();
}

返回的是枚舉常量的克隆數(shù)組。

使用示例

?
1
2
3
4
5
6
7
8
@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)

該方法通過字符串獲取對應(yīng)的枚舉常量

?
1
2
3
4
5
@test
public void testvalueof() {
directionenum east = directionenum.valueof("east");
system.out.println(east.ordinal());// 輸出0
}

3.2 繼承的方法

因為枚舉類型繼承于 java.lang.enum, 因此除了該類的私有方法, 其他方法都是可以使用的。

3.2.1 ordinal()

該方法返回的是枚舉實例的在定義時的順序, 類似于數(shù)組, 第一個實例該方法的返回值為 0。

在基于枚舉的復(fù)雜數(shù)據(jù)結(jié)構(gòu) enumset和enummap 中會用到該函數(shù)。

?
1
2
3
4
5
@test
public void testordinal() {
system.out.println(directionenum.east.ordinal());// 輸出 0
system.out.println(directionenum.north.ordinal()); // 輸出 2
}

3.2.2 compareto()

該方法時實現(xiàn)的 comparable 接口的, 其實現(xiàn)如下

?
1
2
3
4
5
6
7
8
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;
}

首先, 需要枚舉類型是同一種類型, 然后比較他們的 ordinal 來得出大于、小于還是等于。

?
1
2
3
4
5
6
@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()

該兩個方法都是返回枚舉常量的名稱。 但是, name() 方法時 final 類型, 是不能被覆蓋的! 而 tostring 可以被覆蓋。

3.2.4 getdeclaringclass()

獲取對應(yīng)枚舉類型的 class 對象

?
1
2
3
4
5
@test
public void testgetdeclaringclass() {
 system.out.println(directionenum.west.getdeclaringclass());
 // 輸出 class cn.homejim.java.lang.directionenum
}

2.3.5 equals

判斷指定對象與枚舉常量是否相同

?
1
2
3
4
5
@test
public void testequals() {
system.out.println(directionenum.west.equals(directionenum.east)); // false
system.out.println(directionenum.west.equals(directionenum.west)); // true
}

4 枚舉類型進階

枚舉類型通過反編譯我們知道, 其實也是一個類(只不過這個類比較特殊, 加了一些限制), 那么, 在類上能做的一些事情對其也是可以做的。 但是, 個別的可能會有限制(方向吧, 編譯器會提醒我們的)

4.1 自定義構(gòu)造函數(shù)

首先, 定義的構(gòu)造函數(shù)可以是 private, 或不加修飾符

扒一扒 Java 中的枚舉類型

自定義構(gòu)造函數(shù)

我們給每個方向加上一個角度

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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;
 }
}

測試

?
1
2
3
4
5
@test
public void testconstructor() {
system.out.println(directionenum.west.getangle()); // 180
system.out.println(directionenum.east.getangle()); // 0
}

4.2 添加自定義的方法

以上的 getangle 就是我們添加的自定義的方法

4.2.1 自定義具體方法

我們在枚舉類型內(nèi)部加入如下具體方法

?
1
2
3
protected void move() {
system.out.println("you are moving to " + this + " direction");
}

測試

?
1
2
3
4
5
@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 方法的

我們在directinenum中定義如下的抽象方法

?
1
abstract string ondirection();

定義完之后, 發(fā)現(xiàn)編譯器報錯了, 說我們需要實現(xiàn)這個方法

扒一扒 Java 中的枚舉類型

按要求實現(xiàn)

扒一扒 Java 中的枚舉類型

測試

?
1
2
3
4
5
@test
public void testabstractmethod() {
 system.out.println(directionenum.east.ondirection());
 system.out.println(directionenum.south.ondirection());
}

輸出

east direction 1
north direction 333

也就是說抽象方法會強制要求每一個枚舉常量自己實現(xiàn)該方法。 通過提供不同的實現(xiàn)來達(dá)到不同的目的。

4.3 覆蓋父類方法

在父類 java.lang.enum 中, 也就只有 tostring() 是沒有使用 final 修飾啦, 要覆蓋也只能覆蓋該方法。 該方法的覆蓋相信大家很熟悉, 在此就不做過多的講解啦

4.4 實現(xiàn)接口

因為java是單繼承的, 因此, java中的枚舉因為已經(jīng)繼承了 java.lang.enum, 因此不能再繼承其他的類。

但java是可以實現(xiàn)多個接口的, 因此 java 中的枚舉也可以實現(xiàn)接口。

定義接口

?
1
2
3
public interface testinterface {
 void dosomething();
}

實現(xiàn)接口

?
1
2
3
4
5
6
7
public enum directionenum implements testinterface{
 // 其他代碼
 public void dosomething() {
  system.out.println("dosomething implement");
 }
 // 其他代碼
}

測試

?
1
2
3
4
@test
public void testimplement() {
 directionenum.west.dosomething(); // 輸出 dosomething implement
}

5 使用枚舉實現(xiàn)單例

該方法是在 《effective java》 提出的

?
1
2
3
4
5
6
7
public enum singlton {
 instance;
 
 public void dootherthing() {
  
 }
}

使用枚舉的方式, 保證了序列化機制, 絕對防止多次序列化問題, 保證了線程的安全, 保證了單例。 同時, 防止了反射的問題。

該方法無論是創(chuàng)建還是調(diào)用, 都是很簡單。 《effective java》 對此的評價:

單元素的枚舉類型已經(jīng)成為實現(xiàn)singleton的最佳方法。

6 枚舉相關(guān)的集合類

java.util.enumset 和 java.util.enummap, 在此不進行過多的講述了。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對服務(wù)器之家的支持。

原文鏈接:https://www.cnblogs.com/homejim/p/10056701.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 金莲你下面好紧夹得我好爽 | 亚洲精品无码久久不卡 | 国产青草亚洲香蕉精品久久 | 欧美一区二区三区精品国产 | 精品视频在线播放 | 成人午夜爽爽爽免费视频 | 国产精品网站在线观看 | 99热久久这里只精品国产www | 全肉一女n男np高h乳 | 久热在线这里只有精品7 | 我的绝色岳每雯雯 | 国产成人精品高清不卡在线 | 色悠久久久久综合网小说 | 91精品国产9l久久久久 | 色先锋av资源中文字幕 | 色五夜婷婷 | 欧美免赞性视频 | 色先锋av资源中文字幕 | 国产精品国色综合久久 | 天堂网在线.www天堂在线资源 | 我的好妈妈7中字在线观看韩国 | 久久人妻少妇嫩草AV无码 | 国产精品青青青高清在线密亚 | 网友自拍咪咪爱 | 海角社区在线视频 | 色人阁小说| 我的漂亮朋友在线观看全集免费 | 美女沟厕撒尿全过程高清图片 | 高h文道具| 久久视频这有精品63在线国产 | 婷婷去我也去 | 全彩成人18h漫画 | 久久中文字幕综合不卡一二区 | 日产欧产va高清 | 大胸美女被c | 校花被老头夺去第一次动图 | 日日爱669| 91看片淫黄大片.在线天堂 | 国模娜娜一区二区三区 | 五月天精品视频在线观看 | 亚洲国产精品久久精品成人网站 |