日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
Java中的枚舉,這一篇全了,一些不為人知的干貨

本文轉(zhuǎn)載自微信公眾號(hào)「程序新視界」,作者二師兄。轉(zhuǎn)載本文請(qǐng)聯(lián)系程序新視界公眾號(hào)。

你所需要的網(wǎng)站建設(shè)服務(wù),我們均能行業(yè)靠前的水平為你提供.標(biāo)準(zhǔn)是產(chǎn)品質(zhì)量的保證,主要從事成都做網(wǎng)站、成都網(wǎng)站建設(shè)、企業(yè)網(wǎng)站建設(shè)、手機(jī)網(wǎng)站制作設(shè)計(jì)、網(wǎng)頁設(shè)計(jì)、成都品牌網(wǎng)站建設(shè)、網(wǎng)頁制作、做網(wǎng)站、建網(wǎng)站。創(chuàng)新互聯(lián)擁有實(shí)力堅(jiān)強(qiáng)的技術(shù)研發(fā)團(tuán)隊(duì)及素養(yǎng)的視覺設(shè)計(jì)專才。

Java枚舉,也稱作Java枚舉類型,是一種字段由一組固定常量集合組成的類型。枚舉的主要目的是加強(qiáng)編譯時(shí)類型的安全性。enum關(guān)鍵字是Java中的保留關(guān)鍵字。

在編譯或設(shè)計(jì)時(shí),當(dāng)我們知道所有變量的可能性時(shí),盡量使用枚舉類型。本篇文章就帶大家全面系統(tǒng)的了解枚舉的使用,以及會(huì)遇到的一些問題。

Java中的枚舉

枚舉通常是一組相關(guān)的常量集合,其他編程語言很早就開始用枚舉了,比如C++。從JDK1.5起,Java也開始支持枚舉類型。

枚舉是一種特殊的數(shù)據(jù)類型,它既是一種類(class)類型卻又比類類型多了些特殊的約束,這些約束也造就了枚舉類型的簡(jiǎn)潔性、安全性以及便捷性。

在Java中,通過enum來聲明枚舉類型,默認(rèn)繼承自java.lang.Enum。所以聲明枚舉類時(shí)無法再繼承其他類。

枚舉聲明

在生活中我們會(huì)經(jīng)常辨認(rèn)方向,東南西北,它們的名稱、屬性等基本都是確定的,我們就可以將其聲明為枚舉類型:

 
 
 
  1. public enum Direction {
  2.    EAST, WEST, NORTH, SOUTH;
  3. }

同樣,每周七天也可以聲明成枚舉類型:

 
 
 
  1. enum Day {
  2.     MONDAY, TUESDAY, WEDNESDAY,
  3.     THURSDAY, FRIDAY, SATURDAY, SUNDAY
  4. }

在沒有枚舉或沒使用枚舉的情況下,并不是說不可以定義變量,我們可以通過類或接口進(jìn)行常量的定義:

 
 
 
  1. public class Day {
  2.     public static final int MONDAY =1;
  3.     public static final int TUESDAY=2;
  4.     public static final int WEDNESDAY=3;
  5.     public static final int THURSDAY=4;
  6.     public static final int FRIDAY=5;
  7.     public static final int SATURDAY=6;
  8.     public static final int SUNDAY=7;
  9. }

但這樣存在許多不足,如在類型安全和使用方便性上。如果存在定義int值相同的變量,混淆的幾率還是很大的,編譯器也不會(huì)提出任何警告。因此,當(dāng)能使用枚舉的時(shí)候,并不提倡這種寫法。

枚舉的底層實(shí)現(xiàn)

上面我們已經(jīng)說了,枚舉是一個(gè)特殊的類,每一個(gè)枚舉項(xiàng)本質(zhì)上都是枚舉類自身的實(shí)例。

因此,上面枚舉類Direction可以通過下面代碼進(jìn)行示例:

 
 
 
  1. final class Direction extends Enum{
  2.     public final static Direction EAST = new Direction();
  3.     public final static Direction WEST = new Direction();
  4.     public final static Direction NORTH = new Direction();
  5.     public final static Direction SOUTH = new Direction();
  6. }

首先通過javac命令對(duì)Direction進(jìn)行編譯,然后通過javap命令來查看一下對(duì)應(yīng)class文件內(nèi)容:

 
 
 
  1. bogon:enums apple$ javap Direction.class 
  2. Compiled from "Direction.java"
  3. public final class com.choupangxia.enums.Direction extends java.lang.Enum {
  4.   public static final com.choupangxia.enums.Direction EAST;
  5.   public static final com.choupangxia.enums.Direction WEST;
  6.   public static final com.choupangxia.enums.Direction NORTH;
  7.   public static final com.choupangxia.enums.Direction SOUTH;
  8.   public static com.choupangxia.enums.Direction[] values();
  9.   public static com.choupangxia.enums.Direction valueOf(java.lang.String);
  10.   static {};
  11. }

可以看到,一個(gè)枚舉在經(jīng)過編譯器編譯過后,變成了一個(gè)抽象類,它繼承了java.lang.Enum;而枚舉中定義的枚舉常量,變成了相應(yīng)的public static final屬性,而且其類型就抽象類的類型,名字就是枚舉常量的名字。

枚舉使用實(shí)例

通過上面的反編譯我們可以看到,枚舉的選項(xiàng)本質(zhì)上就是public static final的變量,所以就把它當(dāng)做這樣的變量使用即可。

 
 
 
  1. public class EnumExample {
  2.     public static void main(String[] args) {
  3.         Direction north = Direction.NORTH;
  4.         System.out.println(north);        //Prints NORTH
  5.     }
  6. }

枚舉的ordinal()方法

ordinal()方法用于獲取枚舉變量在枚舉類中聲明的順序,下標(biāo)從0開始,與數(shù)組中的下標(biāo)很相似。它的設(shè)計(jì)是用于EumSet和EnumMap復(fù)雜的基于枚舉的數(shù)據(jù)結(jié)構(gòu)使用。

 
 
 
  1. Direction.EAST.ordinal();     //0
  2.  
  3. Direction.NORTH.ordinal();    //2

需要注意的是如果枚舉項(xiàng)聲明的位置發(fā)生了變化,那么ordinal方法的值也隨之變化。所以,進(jìn)來避免使用該方法。不然,當(dāng)枚舉項(xiàng)比較多時(shí),別人在中間增刪一項(xiàng),會(huì)導(dǎo)致后續(xù)的所有順序變化。

枚舉的values()和valueOf()

values()方法可獲取枚舉類中的所有變量,并作為數(shù)組返回:

 
 
 
  1. Direction[] directions = Direction.values();
  2.  
  3. for (Direction d : directions) {
  4.     System.out.println(d);
  5. }
  6.  
  7. //Output:
  8.  
  9. EAST
  10. WEST
  11. NORTH
  12. SOUTH

values()方法是由編譯器插入到枚舉類中的static方法,而它的父類Enum中并不存在這個(gè)方法。

valueOf(String name)方法與Enum類中的valueOf方法的作用類似根據(jù)名稱獲取枚舉變量,同樣是由編譯器生成的,但更簡(jiǎn)潔些,只需傳遞一個(gè)參數(shù)。

 
 
 
  1. Direction east = Direction.valueOf("EAST");
  2.          
  3. System.out.println(east);
  4.  
  5. //Output:
  6.  
  7. EAST

枚舉命名約定

按照約定,枚舉屬于常量,因此采用所有字母大寫,下劃線分割的風(fēng)格(UPPER_CASE)。也就是說枚舉類名與普通類約定一樣,而枚舉中的變量與靜態(tài)變量的命名規(guī)范一致。

枚舉的構(gòu)造方法

默認(rèn)情況下,枚舉類是不需要構(gòu)造方法的,默認(rèn)的變量就是聲明時(shí)的字符串。當(dāng)然,你也可以通過自定義構(gòu)造方法,來初始化枚舉的一些狀態(tài)信息。通常情況下,我們會(huì)在構(gòu)造參數(shù)中傳入兩個(gè)參數(shù),比如,一個(gè)編碼,一個(gè)描述。

以上面的方向?yàn)槔?/p>

 
 
 
  1. public enum Direction {
  2.     // enum fields
  3.     EAST(0), WEST(180), NORTH(90), SOUTH(270);
  4.  
  5.     // constructor
  6.     private Direction(final int angle) {
  7.         this.angle = angle;
  8.     }
  9.  
  10.     // internal state
  11.     private int angle;
  12.  
  13.     public int getAngle() {
  14.         return angle;
  15.     }
  16. }

如果我們想訪問每個(gè)方向的角度,可以通過簡(jiǎn)單的方法調(diào)用:

 
 
 
  1. Direction north = Direction.NORTH;
  2.          
  3. System.out.println(north);                      //NORTH
  4.  
  5. System.out.println(north.getAngle());           //90
  6.  
  7. System.out.println(Direction.NORTH.getAngle()); //90

枚舉中的方法

枚舉就是一個(gè)特殊的類,因此也可以像普通的類一樣擁有方法和屬性。在枚舉中不僅可以聲明具體的方法,還可以聲明抽象方法。

方法的訪問權(quán)限可以是private、protected和public。可以通過這些方法返回枚舉項(xiàng)的值,也可以做一些內(nèi)部的私有處理。

 
 
 
  1. public enum Direction {
  2.     // enum fields
  3.     EAST, WEST, NORTH, SOUTH;
  4.      
  5.     protected String printDirection() {
  6.         String message = "You are moving in " + this + " direction";
  7.         System.out.println( message );
  8.         return message;
  9.     }
  10. }

對(duì)應(yīng)方法的使用如下:

 
 
 
  1. Direction.NORTH.printDirection(); 
  2. Direction.EAST.printDirection(); 

枚舉類中還可以定義抽象的方法,但每個(gè)枚舉項(xiàng)中必須實(shí)現(xiàn)對(duì)應(yīng)的抽象方法:

 
 
 
  1. public enum Direction 
  2. {
  3.     // enum fields
  4.     EAST {
  5.         @Override
  6.         public String printDirection() {
  7.             String message = "You are moving in east. You will face sun in morning time.";
  8.             return message;
  9.         }
  10.     },
  11.     WEST {
  12.         @Override
  13.         public String printDirection() {
  14.             String message = "You are moving in west. You will face sun in evening time.";
  15.             return message;
  16.         }
  17.     },
  18.     NORTH {
  19.         @Override
  20.         public String printDirection() {
  21.             String message = "You are moving in north. You will face head in daytime.";
  22.             return message;
  23.         }
  24.     },
  25.     SOUTH {
  26.         @Override
  27.         public String printDirection() {
  28.             String message = "You are moving in south. Sea ahead.";
  29.             return message;
  30.         }
  31.     };
  32.  
  33.     public abstract String printDirection();
  34. }

抽象方法的調(diào)用,與普通方法一樣:

 
 
 
  1. Direction.NORTH.printDirection(); 
  2. Direction.EAST.printDirection(); 

通過這種方式就可以輕而易舉地定義每個(gè)枚舉實(shí)例的不同行為方式。比如需要每個(gè)枚舉項(xiàng)都打印出方向的名稱,就可以定義這么一個(gè)抽象的方法。

上面的實(shí)例enum類似乎表現(xiàn)出了多態(tài)的特性,可惜的是枚舉類型的實(shí)例終究不能作為類型傳遞使用。下面的方式編譯器都無法通過:

 
 
 
  1. //無法通過編譯,Direction.NORTH是個(gè)實(shí)例對(duì)象
  2.  public void text(Direction.NORTH instance){ }

枚舉的繼承

上面已經(jīng)提到過枚舉繼承自java.lang.Enum,Enum是一個(gè)抽象類:

 
 
 
  1. public abstract class Enum>
  2.         implements Comparable, Serializable {
  3.     // ...
  4. }

也就是說,所有的枚舉類都支持比較(Comparable)和序列化(Serializable)的特性。也正因?yàn)樗械拿杜e類都繼承了Enum,所以無法再繼承其他類了,但是可以實(shí)現(xiàn)接口。

枚舉的比較

所有的枚舉默認(rèn)都是Comparable和單例的,因此可以通過equals方法進(jìn)行比較,甚至可以直接用雙等號(hào)“==”進(jìn)行比較。

 
 
 
  1. Direction east = Direction.EAST;
  2. Direction eastNew = Direction.valueOf("EAST");
  3.  
  4. System.out.println( east == eastNew );           //true
  5. System.out.println( east.equals( eastNew ) );    //true

枚舉集合:EnumSet和EnumMap

在java.util包下引入了兩個(gè)枚舉集合類:EnumSet和EnumMap。

EnumSet

EnumSet類的定義如下:

 
 
 
  1. public abstract class EnumSet> extends AbstractSet
  2.     implements Cloneable, java.io.Serializable{
  3.     // ...
  4. }

EnumSet是與枚舉類型一起使用的專用Set集合,EnumSet中所有元素都必須是枚舉類型。與其他Set接口的實(shí)現(xiàn)類HashSet/TreeSet不同的是,EnumSet在內(nèi)部實(shí)現(xiàn)是位向量。

位向量是一種極為高效的位運(yùn)算操作,由于直接存儲(chǔ)和操作都是bit,因此EnumSet空間和時(shí)間性能都十分可觀,足以媲美傳統(tǒng)上基于int的“位標(biāo)志”的運(yùn)算,關(guān)鍵是我們可像操作set集合一般來操作位運(yùn)算。

EnumSet不允許使用null元素,試圖插入null將拋出 NullPointerException,但測(cè)試判斷是否存在null元素或移除null元素則不會(huì)拋出異常,與大多數(shù)Collection實(shí)現(xiàn)一樣,EnumSet不是線程安全的,在多線程環(huán)境下需注意數(shù)據(jù)同步問題。

使用實(shí)例:

 
 
 
  1. public class Test {
  2.    public static void main(String[] args) {
  3.      Set enumSet = EnumSet.of(  Direction.EAST,
  4.                                 Direction.WEST,
  5.                                 Direction.NORTH,
  6.                                 Direction.SOUTH
  7.                               );
  8.    }
  9.  }

EnumMap

EnumMap的聲明如下:

 
 
 
  1. public class EnumMap, V> extends AbstractMap
  2.     implements java.io.Serializable, Cloneable
  3. {}

與EnumSet類似,EnumMap是一個(gè)特殊的Map,Map的Key必須是枚舉類型。EnumMap內(nèi)部是通過數(shù)組實(shí)現(xiàn)的,效率比普通的Map更高一些。EnumMap的key值不能為null,并且EnumMap也不是線程安全的。

EnumMap使用實(shí)例如下:

 
 
 
  1. public class Test {
  2.   public static void main(String[] args){
  3.     //Keys can be only of type Direction
  4.     Map enumMap = new EnumMap(Direction.class);
  5.  
  6.     //Populate the Map
  7.     enumMap.put(Direction.EAST, Direction.EAST.getAngle());
  8.     enumMap.put(Direction.WEST, Direction.WEST.getAngle());
  9.     enumMap.put(Direction.NORTH, Direction.NORTH.getAngle());
  10.     enumMap.put(Direction.SOUTH, Direction.SOUTH.getAngle());
  11.   }
  12. }

枚舉與switch

使用switch進(jìn)行條件判斷時(shí),條件參數(shù)一般只能是整型,字符型,同時(shí)也支持枚舉型,在java7后switch也對(duì)字符串進(jìn)行了支持。

使用實(shí)例如下:

 
 
 
  1. enum Color {GREEN,RED,BLUE}
  2. public class EnumDemo4 {
  3.     public static void printName(Color color){
  4.         switch (color){
  5.             //無需使用Color進(jìn)行引用
  6.             case BLUE: 
  7.                 System.out.println("藍(lán)色");
  8.                 break;
  9.             case RED:
  10.                 System.out.println("紅色");
  11.                 break;
  12.             case GREEN:
  13.                 System.out.println("綠色");
  14.                 break;
  15.         }
  16.     }
  17.     public static void main(String[] args){
  18.         printName(Color.BLUE);
  19.         printName(Color.RED);
  20.         printName(Color.GREEN);
  21.     }
  22. }

枚舉與單例

單例模式是日常使用中最常見的設(shè)計(jì)模式之一了,單例的實(shí)現(xiàn)有很多種實(shí)現(xiàn)方法(餓漢模式、懶漢模式等),這里就不再贅述,只以一個(gè)最普通的單例來做對(duì)照,進(jìn)而看看基于枚舉如何來實(shí)現(xiàn)單例模式。

餓漢模式的實(shí)現(xiàn):

 
 
 
  1. public class Singleton {
  2.     private static Singleton instance = new Singleton();
  3.     private Singleton() {
  4.     }
  5.     public static Singleton getInstance() {
  6.         return instance;
  7.     }
  8. }

簡(jiǎn)單直接,缺點(diǎn)是可能在還不需要時(shí)就把實(shí)例創(chuàng)建出來了,沒起到lazy loading的效果。優(yōu)點(diǎn)就是實(shí)現(xiàn)簡(jiǎn)單,而且安全可靠。

這樣一個(gè)單例場(chǎng)景,如果通過枚舉進(jìn)行實(shí)現(xiàn)如下:

 
 
 
  1. public enum Singleton {
  2.     INSTANCE;
  3.     public void doSomething() {
  4.         System.out.println("doSomething");
  5.     }
  6. }

在effective java中說道,最佳的單例實(shí)現(xiàn)模式就是枚舉模式。利用枚舉的特性,讓JVM來幫我們保證線程安全和單一實(shí)例的問題。除此之外,寫法還特別簡(jiǎn)單。

直接通過Singleton.INSTANCE.doSomething()的方式調(diào)用即可。方便、簡(jiǎn)潔又安全。

小結(jié)

枚舉在日常編碼中幾乎是必不可少的,如何用好,如何用精,還需要基礎(chǔ)知識(shí)的鋪墊,本文也正是基于此帶大家從頭到尾梳理了一遍。有所收獲就點(diǎn)個(gè)贊吧。


網(wǎng)站名稱:Java中的枚舉,這一篇全了,一些不為人知的干貨
當(dāng)前地址:http://m.5511xx.com/article/cdpiogj.html