枚舉
在很多的程式設計語言中,枚舉是一種比較常見的原生類型,枚舉是一種比較常見的原生類型,但是在Java裡面,一直到 JDK 1.5 之後才正式引入了枚舉結構,其主要的目的是進行多例設計模式的替換。
提示:實際上,傳統的多例設計模式是在進行Java開發的過程中,不得已而使用的一種設計結構,因為需要控制對象産生個數,而傳統的多例設計模式需要通過一個具體的static方法來擷取指定類型的執行個體化對象,可是在獲得的時候必須傳入一些标記,于是很多習慣于使用枚舉結構的開發人員來說就表示嚴重的不習慣。
但是對于一直從事Java開發的工程人員來說,由于可能一直沒有使用枚舉的習慣,是以反而覺得多例設計設計模式簡單明了。
一、定義枚舉類
在 JDK 1.5 之後,如果需要定義枚舉類,那麼可以直接使用enum關鍵字來完成,而枚舉類定義的時候需要提供好若幹個執行個體化對象。
範例:定義枚舉類
enum Color{ //定義枚舉類
RED,GREEN,BLUE; //描述枚舉對象
}
public class Test {
public static void main(String[] args) {
Color c = Color.RED; //直接擷取指定對象
System.out.println(c);
}
}
/**
程式執行結果:RED
**/
此時的Color類就相當于是一個多例設計,但是與傳統的多例設計相比,此時的Color類可以直接擷取類中指定的執行個體化對象,并且避免了之前需要傳遞參數getInstance()方式來擷取。
範例:使用中文定義枚舉對象
enum Color{ //定義枚舉類
紅色,綠色,藍色; //描述枚舉對象
}
public class Test {
public static void main(String[] args) {
Color c = Color.紅色; //直接擷取指定對象
System.out.println(c);
}
}
/**
程式執行結果:紅色
**/
在Java中可以直接使用中文的形式來進行變量或者對象的聲明,是以此時使用中文完全沒有問題,在枚舉對象輸出的時候也會自動調用toString()方法,而它所傳回的預設内容就是枚舉對象的名稱。
在使用傳統多例設計設計模式進行開發的過程之中,很難全面的知道到底有多少個對象可以供使用者使用,但是如果直接使用枚舉這些資訊都可以非常直白地描述出來。
範例:擷取枚舉中的全部資料資訊
enum Color{ //定義枚舉類
RED,GREEN,BLUE; //描述枚舉對象
}
public class Test {
public static void main(String[] args) {
for(Color c : Color.values()) {//values()可以擷取全部枚舉資訊
System.out.print(c + "、");
}
}
}
/**
程式執行結果:RED、GREEN、BLUE、
**/
本程式直接通過枚舉中提供的values()方法将所有的枚舉對象轉為數組的形式傳回,随後利用for-each輸出枚舉中的每一項資訊定義,在 JDK 1.5 之後,不僅追加了枚舉的結構,同時也針對于枚舉改進了switch結構。
swich進化史:
- JDK 1.0 ~ JDK 1.4 :switch支援整型、字元型判斷;
- JDK 1.5:switch支援枚舉判斷;
- JDK 1.7:switch支援String的判斷;
- JDK 1.13:switch可以結合yield實作局部傳回。
範例:在switch中使用枚舉進行判斷
enum Color{ //定義枚舉類
RED,GREEN,BLUE; //描述枚舉對象,此處的大寫與多例設計中的大寫意義相同
}
public class Test {
public static void main(String[] args) {
Color c =Color.RED; //擷取枚舉對象
switch(c) {
case RED:
System.out.println("【RED】紅色");
break;
case GREEN:
System.out.println("【GREEN】綠色");
break;
case BLUE:
System.out.println("【BLUE】藍色");
break;
}
}
}
/**
程式執行結果:【RED】紅色
**/
這種switch判斷僅僅在枚舉上,如果是多例設計模式,那麼沒有這樣的支援語句,隻能夠通過各種if以及對象比較的方法來完成。
二、Enum類
在Java裡面需要通過enum關鍵字來實作枚舉類的定義,但是嚴格意義上來講,使用enum關鍵字所定義的本質上相當于一個類繼承了Enum父類結構,即:如果想要研究枚舉,那麼最佳的做法就是去觀察一下Enum類的定義:
public abstract class Enum<E extends Enum<E>>//此泛型是由枚舉來控制的 extends Object implements Constable, Comparable<E>, Serializable
首先可以發現,Enum類屬于一個抽象類,按照Java面向對象的設計結構來講,所有的抽象類一定要提供有相應的子類,如果子類不屬于抽象類,則一定要覆寫父類中的全部抽象方法,但是Enum類中并沒有提供抽象方法,而之是以将其定義為抽象類是因為不希望這個類直接被使用,而在Enum類中提供有如下的幾個方法:
通過定義可以發現,在枚舉的構造方法上使用了protected通路控制權限,那麼就證明了此時的枚舉中的構造方法使用了封裝定義(不管是單例設計模式還是多例設計模式,首要的要求就是構造方法私有化)。
範例:觀察Enum類中的使用
enum Color{ //定義枚舉類
RED,GREEN,BLUE; //描述枚舉對象,此處的大寫與多例設計中的大寫意義相同
}
public class Test {
public static void main(String[] args) {
for(Color c : Color.values() ) {
System.out.println("【"+ c +"】名稱 = " + c.name() + "、序号 = " + c.ordinal());
}
}
}
/**
程式執行結果:【RED】名稱 = RED、序号 = 0
【GREEN】名稱 = GREEN、序号 = 1
【BLUE】名稱 = BLUE、序号 = 2
**/
通過以上的程式代碼就可以清楚地發現,enum定義的枚舉結構就相當于一個類繼承了Enum父類的結構。
總結:請解釋enum和Enum的差別?
- enum是在JDK 1.5 之後提供枚舉定義的關鍵字;
- 使用enum關鍵字定義的枚舉類從本質上就相當于定義了一個繼承Enum父類的結構。
三、擴充枚舉結構
雖然Java是在 JDK 1.5 之後才正式提供了枚舉類型,但是從其應用的角度來講,其枚舉的功能已經要比任何語言中所要提供的枚舉功能都要強大,因為傳統的枚舉僅僅是能夠定義若幹個枚舉項(對象),而在Java中可以将類的結構充分的定義在枚舉中。
1、在枚舉中定義屬性與方法
enum Color{ //定義枚舉類
//此時類中不提供有無參的構造方法,是以每一個枚舉項(執行個體化對象)都必須明确的調用有參構造方法并傳遞參數
RED("紅色"),GREEN("綠色"),BLUE("藍色"); //枚舉項必須寫在類的首行
private String content; //定義屬性
Color(String content){ //此時的枚舉類中不提供無參構造
this.content = content;
}
public String toString() { //覆寫toString()方法
return this.content;
}
}
public class Test {
public static void main(String[] args) {
for(Color c : Color.values() ) {
System.out.println("【"+ c +"】名稱 = " + c.name() + "、序号 = " + c.ordinal());
}
}
}
/**
程式執行結果:【紅色】名稱 = RED、序号 = 0
【綠色】名稱 = GREEN、序号 = 1
【藍色】名稱 = BLUE、序号 = 2
**/
枚舉的本質就是多例設計模式,而多例設計模式裡面的首要的實作要求:類中的構造方法私有化,當在枚舉中定義構造方法的時候絕對不要使用public通路權限,否則會出現有如下的編譯錯誤。
範例:錯誤的構造方法定義
enum Color{ //定義枚舉類
//此時類中不提供有無參的構造方法,是以每一個枚舉項(執行個體化對象)都必須明确的調用有參構造方法并傳遞參數
RED("紅色"),GREEN("綠色"),BLUE("藍色"); //枚舉項必須寫在類的首行
private String content; //定義屬性
public Color(String content){ //此時的枚舉類中不提供無參構造
this.content = content;
}
public String toString() { //覆寫toString()方法
return this.content;
}
}
public class Test {
public static void main(String[] args) {
for(Color c : Color.values() ) {
System.out.println("【"+ c +"】名稱 = " + c.name() + "、序号 = " + c.ordinal());
}
}
}
/**
程式編譯結果:Illegal modifier for the enum constructor; only private is permitted.
**/
2、枚舉整體運作結構實際上是和類是相同的,那麼既然和類是相同的,是以枚舉本身也可以實作接口,而對于接口的抽象方法,可以分開實作,也可以集中實作。
範例:公共實作接口的抽象方法
interface IMessage{//定義一個擷取消息的接口
public String getColor();//抽象方法
}
enum Color implements IMessage{ //定義枚舉類
//此時類中不提供有無參的構造方法,是以每一個枚舉項(執行個體化對象)都必須明确的調用有參構造方法并傳遞參數
RED("紅色"),GREEN("綠色"),BLUE("藍色"); //枚舉項必須寫在類的首行
private String content; //定義屬性
Color(String content){ //此時的枚舉類中不提供無參構造
this.content = content;
}
public String getColor() { //公共覆寫方法
return this.content;
}
}
public class Test {
public static void main(String[] args) {
IMessage msg = Color.RED; //擷取一個接口的子類對象
System.out.println(msg.getColor());
}
}
/**
程式執行結果:紅色
**/
除了以上的方式之外,在枚舉中的每一個枚舉對象也可以分開來實作具體的抽象方法。
範例:每一個枚舉項實作抽象方法
interface IMessage{//定義一個擷取消息的接口
public String getColor();//抽象方法
}
enum Color implements IMessage{ //定義枚舉類
//此時類中不提供有無參的構造方法,是以每一個枚舉項(執行個體化對象)都必須明确的調用有參構造方法并傳遞參數
RED{
public String getColor() {
return "紅色";
}
},GREEN{
public String getColor() {
return "綠色";
}
},BLUE{
public String getColor() {
return "藍色";
}
};
}
public class Test {
public static void main(String[] args) {
IMessage msg = Color.RED; //擷取一個接口的子類對象
System.out.println(msg.getColor());
}
}
/**
程式執行結果:紅色
**/
3、在枚舉中也可以直接定義抽象方法,而對于這些抽象方法就要求每一個枚舉項都必須明确的進行覆寫。
enum Color{ //定義枚舉類
//此時類中不提供有無參的構造方法,是以每一個枚舉項(執行個體化對象)都必須明确的調用有參構造方法并傳遞參數
RED{
public String getColor() {
return "紅色";
}
},GREEN{
public String getColor() {
return "綠色";
}
},BLUE{
public String getColor() {
return "藍色";
}
};
public abstract String getColor();//抽象方法
}
public class Test {
public static void main(String[] args) {
System.out.println(Color.BLUE.getColor());
}
}
/**
程式執行結果:藍色
**/
實際上通過以上的幾個程式的功能就可以非常清楚地發現,原來整個的枚舉從它的定義來說,是所有語言設計之最。
四、枚舉應用案例
經過一系列的分析之後我們已經清楚了枚舉的基本使用,但是随後還需要進一步的去思考,枚舉在我們的實際開發過程之中應該如何去使用呢?首先一定要清楚使用枚舉就相當于定義了一個操作的範疇,那麼既然有了操作的範疇,對于一些類的對象就有了使用的固定環境,例如:如果要定義圖書類,圖書肯定會有分類,而這個分類就可以通過枚舉來描述。
範例:使用枚舉定義圖書的範圍
enum BookType{ //定義枚舉類
MATH("數學"),PROGRAM("軟體程式設計"),GAME("遊戲");
private String content;
private BookType(String content) {
this.content = content;
}
public String toString() {
return this.content;
}
}
class Book{
private String title;
private String author;
private double price;
private BookType type;
public Book() {
super();
// TODO Auto-generated constructor stub
}
public Book(String title, String author, double price, BookType type) {
super();
this.title = title;
this.author = author;
this.price = price;
this.type = type;
}
/**
* @return the title
*/
public String getTitle() {
return title;
}
/**
* @param title the title to set
*/
public void setTitle(String title) {
this.title = title;
}
/**
* @return the author
*/
public String getAuthor() {
return author;
}
/**
* @param author the author to set
*/
public void setAuthor(String author) {
this.author = author;
}
/**
* @return the price
*/
public double getPrice() {
return price;
}
/**
* @param price the price to set
*/
public void setPrice(double price) {
this.price = price;
}
/**
* @return the type
*/
public BookType getType() {
return type;
}
/**
* @param type the type to set
*/
public void setType(BookType type) {
this.type = type;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "【圖書】名稱:" + this.title + "、作者:" + this.author + "、價格:" + this.price + "、類型:" + this.type ;
}
}
public class Test {
public static void main(String[] args) {
System.out.println(new Book("高等數學" , "小賈老師" , 99.8 , BookType.MATH));
}
}
/**
程式執行結果:【圖書】名稱:高等數學、作者:小賈老師、價格:99.8、類型:數學
**/
很多同學也會覺得,即使此時不使用枚舉也可以實作,但是畢竟從枚舉産生到現在已經度過了15年的時間了,現在對于枚舉的使用早已經深入人心了。