在工作中遇到這樣一個問題,要在前臺頁面上渲染出來所有的枚舉元素,但是前臺頁面用的是Velocity,所以需要把枚舉元素放到Map里,然后在前臺進行渲染。這里不能一個一個的取出枚舉元素來put的,就想能用一個循環能搞定這個。發現枚舉類有一個方法values,正好可以滿足我的需求。OK話不多說我們一個一個的介紹其中主要的方法。
先看定義的枚舉類:
package com.zkn.newlearn.enums;
/**
*
* @author zkn 2016-07-11
*
*/
public enum EnumTest01 {
UPDATE(1,"更新"),QUERY(2,"查詢"),DELETE(3,"刪除");
private Integer enumValue;
private String enumDesc;
private EnumTest01(Integer enumValue, String enumDesc) {
this.enumValue = enumValue;
this.enumDesc = enumDesc;
}
public int getEnumValue(){
return this.enumValue;
}
public String getEnumDesc(){
return this.enumDesc;
}
}
java.lang.Enum
每一個枚舉類都會隱式的繼承java.lang.Enum這個類,也許你會問,你是怎么知道的?我們可以通過反射的方式來解析下EnunTest01這個枚舉類。
public void testEnumMethod(){
Class clazz = EnumTest01.class;
Class clazzSuper = clazz.getSuperclass();
if(clazzSuper != null)
System.out.println(clazzSuper.getName());
}
輸出結果為:java.lang.Enum。正是因為這個原因,而java中又是沒有多繼承的,所以枚舉類不能繼承。但是可以實現接口。
public enum EnumTest02 implements Sender{
;
@Override
public void doSend() {
// TODO Auto-generated method stub
}
}
package com.zkn.newlearn.gof.factory;
/**
*
* @author zkn
*
*/
public interface Sender {
public void doSend();
}
values方法
通過翻父類中的方法,你也找不到這個方法在哪里。那么這個方法是怎么產生的呢?是編譯器在編譯枚舉類的時候添加上去的。這個我們同樣可以通過反射的方式來獲得這樣的結果。
public void testEnumMethod(){
Class clazz = EnumTest01.class;
Method[] methods = clazz.getDeclaredMethods();
for(Method method : methods){
System.out.println(method.getName());
}
}
輸出結果為:
可以看到其中有一個values方法。
那么這個values方法有什么用呢? 通過values方法我們可以獲取枚舉類中的所有的元素。請看例子:
System.out.println(Arrays.toString(EnumTest01.values()));
//循環所有的枚舉類型
for(EnumTest01 test : EnumTest01.values()){
System.out.println(test.getEnumDesc()+test.getEnumValue());
//System.out.println(test.name()+" "+test.ordinal());
}
輸出結果為:
。當你需要獲取一個枚舉類中的所有枚舉元素的時候,就可以用這個方法了。
valueOf方法
這個方法同樣是編譯在編譯的時候添加到枚舉類中去的。例子見上面。這個方法的作用是通過枚舉元素的名稱獲取一個枚舉類。
System.out.println(EnumTest01.valueOf("UPDATE").getEnumDesc());
與之相對應的是java.lang.Enum這個父類中也有一個valueOf方法。但是這個方法的用法稍微有點不同。如下所示:
System.out.println(Enum.valueOf(EnumTest01.class, "DELETE").getEnumDesc());
switch語句
從java1.6之后,我們可以在switch語句的case中放入枚舉元素。例子如下:
public void testSwitchEnum() {
putSwitchEnum(EnumTest01.DELETE);
}
public void putSwitchEnum(EnumTest01 enumSwitch) {
switch (enumSwitch) {
case DELETE:
System.out.println("這個是delete方法");
break;
case UPDATE:
System.out.println("這個是update方法");
break;
default:
System.out.println("這個是query方法");
break;
}
}
枚舉元素自定義方法
在枚舉元素中可以自定義方法,來重寫枚舉類中的方法。
package com.zkn.newlearn.enums;
/**
*
* @author zkn 2016-07-11
*
*/
public enum EnumTest02{
GREEN{
@Override
public void getInfo(){
System.out.println("這個是綠燈");
}
public void getPrint(){
System.out.println("zhangsan");
}
},
RED{
@Override
public void getInfo(){
System.out.println("這個是紅燈");
}
},
YELLOW{
@Override
public void getInfo(){
System.out.println("這個是黃燈");
}
};
public void getInfo(){
}
}
為什么可以這樣做呢?我們看一下編譯器編譯之后的Class文件:如圖:
我們可以這樣去理解,每一個枚舉元素都是一個繼承枚舉類的內部子類,并且每一個內部子類中都包含所有的枚舉元素。下面這個例子可以佐證:
import static com.zkn.newlearn.enums.EnumTest02.*;
@Test
public void testEnumCustom(){
EnumTest02 enumTest = GREEN;
enumTest.RED.RED.getInfo();
}
實現單例
估計這是最簡單的實現單例的一個方式。只有一個枚舉元素。
package com.zkn.newlearn.gof.singleton;
/**
* 枚舉實現單例
* @author zkn
*
*/
public enum SingletonGofTest04 {
singleton;
}
ordinal方法
這個方法是用來表示枚舉類中元素的順序的,是從0開始的。這個我們在后面會用到。
for (EnumTest01 test : EnumTest01.values()) {
System.out.println(test.name()+" "+test.ordinal());
}
name方法
name方法用來表示枚舉元素的名稱的,他的名稱即你寫的枚舉元素。例子見上圖。
另外:
你可以像寫java類一樣去寫一個枚舉類,只是這個枚舉類不能集成其他類。
EnumMap
Map的實現類有很多種,EnumMap從名字我們可以看出這個Map是給枚舉類用的。它的key為枚舉元素,value自定義。在工作中我們也可以用其他的Map來實現我們關于枚舉的需求,但是為什么要用這個EnumMap呢?因為它的性能高!為什么性能高?因為它的內部是用數組的數據結構來維護的!我們可以看一下它的源碼實現:
put方法
public V put(K key, V value) {
typeCheck(key);
int index = key.ordinal();
Object oldValue = vals[index];
vals[index] = maskNull(value);
if (oldValue == null)
size++;
return unmaskNull(oldValue);
}
typeCheck是用來檢查key的類型的,因為key只能為枚舉元素。接下來的這一句int index = key.ordinal();key.ordinal()這個就是我們上面說的枚舉類型的序號,然后被當做數組的下標,放到vals這個數組里。那么get方法呢?
get方法
public V get(Object key) {
return (isValidKey(key) ?
unmaskNull(vals[((Enum<?>)key).ordinal()]) : null);
}
注意這一句話:vals[((Enum<?>)key).ordinal()]。這個不就是取得下標,根據下標獲取數組中的值嗎?!
remove方法
public V remove(Object key) {
if (!isValidKey(key))
return null;
int index = ((Enum<?>)key).ordinal();
Object oldValue = vals[index];
vals[index] = null;
if (oldValue != null)
size--;
return unmaskNull(oldValue);
}
remove方法的實現也是挺簡單的,就是把相應下標的元素變為null,等著GC回收。
這里我們只是說了EnumMap里比較常用的三個方法,如果有興趣的同學可以看看其他的方法實現。一個使用EnumMap的例子奉上:
EnumMap<EnumTest01, String> enumMap = new EnumMap<EnumTest01, String>(EnumTest01.class);
enumMap.put(EnumTest01.DELETE, "dsdsd");
enumMap.put(EnumTest01.UPDATE, "qqqqqq");
for (Map.Entry<EnumTest01, String> entry : enumMap.entrySet()) {
System.out.println(entry.getValue() + entry.getKey().getEnumDesc());
}
EnumSet
EnumSet這是一個用來操作Enum的集合,是一個抽象類,它有兩個繼承類:JumboEnumSet和RegularEnumSet。在使用的時候,需要制定枚舉類型。它的特點也是速度非常快,為什么速度很快呢?因為每次add的時候,每個枚舉值只占一個長整型的一位。我們可以翻看源碼來看看它的實現:
add方法
public boolean add(E e) {
typeCheck(e);
long oldElements = elements;
elements |= (1L << ((Enum<?>)e).ordinal());
return elements != oldElements;
}
從中我們可以看出是先對一個長整型左移枚舉類型的序列數,然后再和長整型 或 。
of方法
of方法有好幾個重載的方法,它的作用是創建一個最初包含指定元素的枚舉 set。
EnumSet<EnumTest01> enumSets = EnumSet.of(EnumTest01.DELETE);
allOf
創建一個包含指定元素類型的所有元素的枚舉 set。
EnumSet<EnumTest01> enumSets = EnumSet.allOf(EnumTest01.class);
range方法
創建一個指定范圍的Set。
EnumSet<EnumTest01> enumSets = EnumSet.range(EnumTest01.DELETE,EnumTest01.UPDATE);
noneOf方法
創建一個指定枚舉類型的空set。
EnumSet<EnumTest01> enumSet = EnumSet.noneOf(EnumTest01.class);
enumSet.add(EnumTest01.DELETE);
enumSet.add(EnumTest01.UPDATE);
for (Iterator<EnumTest01> it = enumSet.iterator(); it.hasNext();) {
System.out.println(it.next().getEnumDesc());
}
for (EnumTest01 enumTest : enumSet) {
System.out.println(enumTest.getEnumDesc() + " ..... ");
}
copyOf
創建一個set的并copy所傳入的集合中的枚舉元素。
EnumSet<EnumTest01> enumSets = EnumSet.copyOf(enumSet);
其他方法不再多說,不過大家可以思考一下EnumSetIterator中next方法的實現。
好了,關于枚舉的相關介紹就先到這里吧。有問題的可以評論交流。