偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

你真的會(huì)用枚舉嗎?

開發(fā) 前端
我們知道一個(gè)枚舉的定義非常簡(jiǎn)單。如果只考慮其作為標(biāo)識(shí)的場(chǎng)景,那么從實(shí)現(xiàn)成本來看,枚舉和 public static final 的傳統(tǒng)方式差不多,甚至前者還更簡(jiǎn)單些。

今天我們來聊聊枚舉。你可能會(huì)想:枚舉那么簡(jiǎn)單,有什么好討論的?

沒錯(cuò),枚舉確實(shí)是一個(gè)極為常見的知識(shí)點(diǎn),常見到很多人會(huì)忽略它,而只關(guān)注它最簡(jiǎn)單的用法。當(dāng)然,這和枚舉誕生的初衷有關(guān)。在 JDK1.5 以前,那是個(gè)沒有枚舉的時(shí)代,人們通過 public static final 的常量來定義一些全局使用的標(biāo)識(shí)。甚至到現(xiàn)在,枚舉已經(jīng)誕生很長(zhǎng)時(shí)間了,但仍有一些人在使用這種方案。而且,在 C 語(yǔ)言中也有類似的“宏”的概念,如果你只是用來做全局標(biāo)識(shí),那么枚舉的意義就沒有那么大了。

但是,你真的了解枚舉嗎?今天我們就分別從實(shí)現(xiàn)原理、數(shù)據(jù)結(jié)構(gòu)和設(shè)計(jì)模式 3 個(gè)方向來重新認(rèn)識(shí)一下枚舉。

枚舉原理

我們知道一個(gè)枚舉的定義非常簡(jiǎn)單。如果只考慮其作為標(biāo)識(shí)的場(chǎng)景,那么從實(shí)現(xiàn)成本來看,枚舉和 public static final 的傳統(tǒng)方式差不多,甚至前者還更簡(jiǎn)單些。

public enum Test{
  A,B
}

每一個(gè)枚舉成員都可以看作是枚舉類的實(shí)例,上面的 Test.A 的類型也是 Test。

Test t=Test.A;

上面這個(gè)賦值語(yǔ)句看上去很簡(jiǎn)單,仔細(xì)思考里面包含了幾層意思。首先左邊是 Test 類型的實(shí)例 t,那么右邊必然是一個(gè)類的實(shí)例。但是 Test.A 看上去像是一個(gè)類,這里很容易混淆。請(qǐng)注意,Test.A 是一個(gè)對(duì)象,不要被這里的大寫忽悠了,它不是類。

我們把這個(gè)枚舉翻譯成下面的樣子你是不是更熟悉?

Test A=new Test();
  Test B=new Test();

Java 枚舉類型的實(shí)現(xiàn)是在編譯階段進(jìn)行的。這個(gè)階段和泛型的實(shí)現(xiàn)一樣,也就是說對(duì) JVM 來說執(zhí)行的字節(jié)碼集合并沒有增加任何新的指令,只是在 Java 代碼的層面加了一些語(yǔ)法。說白了,就是對(duì)已有的 JVM 指令集加了一層皮。

舉個(gè)生活化的例子,“進(jìn)食”是人類最基本的行為,酒店會(huì)說“用餐”,但“用餐”是人體的新功能嗎?并不是。在計(jì)算機(jī)界這叫“語(yǔ)法糖”,看著很神奇,寫著也很爽,但底層還是老的功能。

我們可以對(duì) Test.class 文件進(jìn)行反編譯,注意反編譯命令是 javap,其中-p 的意思是反編譯的時(shí)候要包含私有方法。

javap -p Test.class

輸出結(jié)果為:

public final class Test extends java.lang.Enum<Test> {
   public static final Test A;
   public static final Test B;
   public static Test[] values();
   public static Test valueOf(java.lang.String);
   private Test();
   static {};
 }

我們可以看到,確實(shí) A 和 B 都是 Test 類的實(shí)例。Test 繼承了 java.lang.Enum 類,這里還有一個(gè) Test 的無(wú)參構(gòu)造方法,這里的 A 和 B 分別使用這個(gè)構(gòu)造方法來實(shí)例化。

而實(shí)例化的過程發(fā)生在哪呢?

我們注意到上面代碼段的最后有一個(gè)空的 static 語(yǔ)句塊,我們可以基于 javap 的其他參數(shù)進(jìn)一步分析 static 里面的字節(jié)碼內(nèi)容。static 里面其實(shí)包含了很多字節(jié)碼指令,正是這些指令在做 A、B 的初始化工作。而 static 代碼塊是在類加載的時(shí)候執(zhí)行的。也就是說當(dāng) Test 被加載的時(shí)候,A、B 就被初始化了。

static 內(nèi)部除了完成初始化 A、B,還創(chuàng)建了一個(gè)名叫 ENUM$VALUES 的數(shù)組,然后把 A、B 按照定義的順序放入這個(gè)數(shù)組中。最后我們可以通過 values 方法來訪問這個(gè)數(shù)組。

這里我們可以增加一個(gè)構(gòu)造方法,這樣大家就比較熟悉了。A、B 后面可以加一個(gè)括號(hào)調(diào)用這個(gè)構(gòu)造方法。

public enum Test{
  A("a"),B("b");
  Test(String name){
    this.name=name;
  }
  private String name;
}

上面 A(“a”) 相當(dāng)于如下代碼的簡(jiǎn)寫:

Test A=new Test("a");

我們?cè)侔牙幼龅膹?fù)雜一些,我們?yōu)?Test 增加一個(gè)抽象方法 print。這里就不能像上面那樣直接初始化了。這里必須使用類似匿名內(nèi)部類的寫法。

public enum Test{
  A("a"){
    @Override
    public void print(){
      System.out.print("a");
    }
  },
  B("b"){
    @Override
    public void print(){
      System.out.print("b");
    }
  };
  Test(String name){
    this.name=name;
  }
    private String name;
    public abstract void print();
}

上面這個(gè)寫法有點(diǎn)不倫不類,你需要適應(yīng)一下。如果你去編譯目錄下查看文件,這次你會(huì)發(fā)現(xiàn)編譯后多了 Test和2.class 2 個(gè)文件,也就是匿名內(nèi)部類??梢娬Z(yǔ)法糖已經(jīng)幫我們做好了一切。

數(shù)據(jù)結(jié)構(gòu)

說完了枚舉的實(shí)現(xiàn)原理,我們?cè)倏纯此С值囊恍?shù)據(jù)結(jié)構(gòu)。常見的有 EnumSet 和 EnumMap。1.EnumSet

EnumSet 顯然是為枚舉打造的抽象集合類。它使用了位圖來存儲(chǔ)數(shù)據(jù),因此非常緊湊。

EnumSet 有 2 個(gè)實(shí)現(xiàn)類,RegularEnumSet 和 JumboEnumSet。當(dāng)我們創(chuàng)建 EnumSet 的時(shí)候,如果枚舉成員數(shù)量小于等于 64 將會(huì)使用 RegularEnumSet,大于 64 則會(huì)創(chuàng)建 JumboEnumSet。

為什么創(chuàng)建 EnumSet 的時(shí)候,會(huì)有不同的實(shí)現(xiàn)類呢?

這是因?yàn)?RegularEnumSet 采用 long 來存儲(chǔ)枚舉變量,而 long 是 64 位的,因此只能存儲(chǔ) 64 個(gè)變量。而 JumboEnumSet 使用 long[]來存儲(chǔ)枚舉變量,因此沒有這個(gè)限制。當(dāng)然,你見過一個(gè)枚舉類超過 64 個(gè)成員變量嗎?如果真有這種情況,我認(rèn)為放到 ZooKeeper 中會(huì)更合適一些。

這是一個(gè)很有意思的哲學(xué)問題,當(dāng)你的枚舉變量只有 2 個(gè),這個(gè)枚舉一般是很穩(wěn)定的,但是你的枚舉變量超過了 64 個(gè),我相信隨著業(yè)務(wù)發(fā)展枚舉數(shù)量還會(huì)新增,這種情況下就不適合用枚舉來解決了。也就是說枚舉變量越多,業(yè)務(wù)越不穩(wěn)定。

EnumSet支持常見的集合操作,如取子集、增加、刪除、包含等??梢允褂肊numSet的of方法來初始化。
EnumSet<Test> testSet = EnumSet.of(Test.A, Test.B);

2.EnumMap

EnumMap 很明顯是一個(gè) Map 結(jié)構(gòu),它的 key 就是枚舉,value 可以由你定義。比如下面這個(gè)聲明的意思就是對(duì)所有的 Object 按照 Test 枚舉類型來分類。其結(jié)果是輸出類型 A 及屬于 A 類型的對(duì)象,輸出類型 B 及屬于 B 類型的對(duì)象。

EnumMap<Test,List<Object>> testMap;

設(shè)計(jì)模式

最后我們聊聊枚舉和設(shè)計(jì)模式的關(guān)系。

單例模式有很多實(shí)現(xiàn)方法,其中最好的就是用枚舉來實(shí)現(xiàn)。例如,下面的代碼段:

public enum Singleton{
        INSTANCE;
        private Singleton(){
            //做一些初始化工作
        }
    }

上面的 INSTANCE 就是我們的單例對(duì)象,我們可以把一些初始化工作放到 Singleton 的構(gòu)造方法里面。還記得前面我們說的,枚舉的成員就是枚舉類的實(shí)例化對(duì)象,這個(gè)過程發(fā)生在 static 語(yǔ)句塊中。上面這段話所傳達(dá)的語(yǔ)義類似下面這樣:

Singleton INSTANCE=null;
static{
  INSTANCE = new Singleton();
}

此外枚舉在序列化和反序列化的時(shí)候并不會(huì)調(diào)用構(gòu)造方法,這在一定程度上保障了單例。序列化僅僅是將枚舉對(duì)象的 name 屬性輸出到結(jié)果中,反序列化的時(shí)候則是通過 java.lang.Enum 的 valueOf 方法來根據(jù)名字查找枚舉對(duì)象。這里的處理和普通的類有很大的差異。

另外枚舉還可以實(shí)現(xiàn)模板模式、策略模式等。但是注意不要把太多代碼放到枚舉類,這樣不便于維護(hù)。關(guān)于用枚舉實(shí)現(xiàn)其他的設(shè)計(jì)模式讀者可以自己試試。

總結(jié)

上面就是枚舉的核心內(nèi)容。我們知道枚舉本質(zhì)上是一個(gè)語(yǔ)法糖,底層是通過繼承 java.lang.Enum 來實(shí)現(xiàn)的。枚舉的每個(gè)成員都是枚舉類的實(shí)例,并且還有自己的 Set 和 Map 數(shù)據(jù)結(jié)構(gòu),通過上面的分析我們可以看出枚舉底層實(shí)現(xiàn)很普通,但是很多語(yǔ)法特性超越了普通的 Java 類,在設(shè)計(jì)單例模式以及一些模板模式中將簡(jiǎn)化編碼工作,使得工程整體變的更優(yōu)雅、更緊湊。

責(zé)任編輯:武曉燕 來源: 程序員技術(shù)充電站
相關(guān)推薦

2020-06-04 14:15:55

Java中BigDecimal函數(shù)

2018-09-29 15:34:34

JavaList接口

2016-05-04 10:36:42

iossdwebimage開發(fā)

2023-12-01 11:13:50

JavaTreeSet

2021-11-29 09:45:57

枚舉Go代碼

2025-04-01 08:00:00

curl開發(fā)運(yùn)維

2020-12-18 08:59:51

蘋果iCloud儲(chǔ)存照片

2025-02-14 08:30:49

SpringJava開發(fā)

2024-12-27 09:29:09

2014-04-17 16:42:03

DevOps

2020-04-17 14:25:22

Kubernetes應(yīng)用程序軟件開發(fā)

2022-07-26 00:00:22

HTAP系統(tǒng)數(shù)據(jù)庫(kù)

2016-06-01 15:42:58

Hadoop數(shù)據(jù)管理分布式

2021-09-06 10:42:18

Linux命令服務(wù)器

2025-01-20 00:00:00

反射Java語(yǔ)言

2021-08-11 10:00:51

緩存MyBatis管理

2014-11-28 10:31:07

Hybrid APP

2017-11-02 16:03:12

2020-02-27 10:49:26

HTTPS網(wǎng)絡(luò)協(xié)議TCP

2023-03-16 10:49:55

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)