閱讀755 返回首頁    go 阿裏雲 go 技術社區[雲棲]


java enum的使用以及字符串其字符串之間的轉換

文章簡單,相信在很多網站都能搜索到java enum枚舉的使用方式;可能有些東西我當時在剛開始用的時候沒找到,所以我寫了這篇文章,例如:

大多數地方寫的枚舉都是給一個枚舉然後例子就開始switch,可是我想說,我代碼裏頭來源的數據不太可能就是枚舉,通常是字符串或數字,比如一個SQL我解析後首先判定SQL類型,通過截取SQL的token,截取出來可能是SELECT、DELETE、UPDATE、INSERT、ALTER等等,但是都是字符串,此時我想用枚舉就不行了,我要將字符串轉換成枚舉怎麼轉呢,類似的情況還有從數據庫取出數據根據一些類型做判定,從頁麵傳入數據,根據不同的類型做不同的操作,但是都是字符串,不是枚舉,悲劇的是我很少看到有人寫到這個東西;所以我把它寫下來,希望有人能用到。


首先為什麼要用枚舉?我們在什麼時候用枚舉比較好,用枚舉有啥優勢?

我認為哈,當你在一些一個範疇類,並可列舉,不變化的類型,用以指導程序向不同的地方路由,用枚舉是較好的選擇;

聽起來有點繞,不過有個例子也許可以明白,例如:

我們可以列舉下日常工作日所做的事情:

上班、開會、吃飯、睡覺等

我們可以列舉醫院五官科需要檢查人的部位:

眼睛、鼻子、耳朵、嘴巴等

這些都是可以被列舉的,且每種事情我們要用不同的方式去做


當然你可以說:

1、可以用動態方法分派,通過配置文件或annotation;

2、可以使用常量來達到類似的效果;

3、直接通過字符串的equals來表達,用if else來表達


   如果用配置加方法分派來做,是靈活,便於修改;但是如果在很多不經常修改的參數上,我們用這中方式往往增加配置的負擔,並且當你需要看係統邏輯的時候,需要需要一遍看配置一遍看代碼;不過,如果參數是可動態變換的信息,用配置是正確的選擇;


   而常量的使用,通常在switch case的時候都是數字,字符串在java中是不能做switch case的,使用常量的目的比case 1、case 2 ...這種增加了可讀性;但是字符串數據也麻煩,除非再映射一次,那沒那個必要,其實枚舉也差不多是幫你映射了一次,隻是它將代碼封裝了而已吧了,既然他弄好了,而且語法上支持,幹嘛不用呢!其次,常量雖然增加了可讀性,不過他沒有範疇和管理類型的概念,即一個枚舉的定義會定義個範疇,可以很好的將這個範圍所需要的東西列舉出來,而常量通常是些自己定義的一些池,放在一些公共類中或隨機定義,都是比較零散的,並且枚舉在switch的時候就明確定義好了就在鎖列舉的範圍內case,既可以控製好係統,增加可讀性,並且可以隨時查看這個範疇的枚舉信息到底有那些,達到類似看配置文件的作用;不過還是回到那句話,如果參數是可變的,那麼就不適合做枚舉,枚舉是一定是可列舉的,或者說當前係統考慮範圍是可以被枚舉的,例如上麵的醫院五官科,可能還有很多沒有列舉到,但是當前醫院隻處理幾個部位,不處理其他的,就是這個道理;什麼是可變的呢,例如URL參數來分派到對應方法,不可能大家加一段邏輯就去加一個枚舉,加一個case,此時用【配置+動態方法分派】更好,當然配置可以用文件或annotation而已。


   還有最土的就是,通過字符串equals,用if else來實現,嗬嗬,這個並沒有什麼不好,隻是這個寫比較零散,其次,字符串匹配的equals每次匹配都需要對比每個字符,如果你的代碼中大量循環,性能並不是很好,其餘的看看上麵的描述就更加清楚了;


其次,枚舉提供一種類型管理的組件,讓麵向對象的體係更加完善,使得一些類型的管理既可配置化,並可以管理,在使用枚舉的地方都可以沿著枚舉的定義找到那些有處理過,那些沒處理過,而上述幾種很難做到;例如,數據庫的操作類型定義了10種,那麼再判定的過程中就可以講枚舉像配置文件一樣看待,而又非常簡單的來管理。


最後,枚舉絕對是單例的,對比的性能和數字性能相當,既可以得到可讀性,也可以得到性能。


我們先定義個簡單枚舉(這裏隻是個例子,就簡單定義3個變量了):

public enum SqlTypeEnum {
    INSERT , 
    UPDATE ,
    DELETE ,
    SELECT
}


此時解析SQL後,獲取出來一個token,我們要獲取這個token的枚舉怎麼獲取呢?

這樣獲取:

String token = "select";
SqlTypeEnum sqlTypeEnum = SqlTypeEnum.valueOf(token.toUpperCase());


如果沒獲取到,java會拋出一個異常哦:IllegalArgumentException No enum const class SqlTypeEnum.XXX


我做大寫處理的原因是因為枚舉也是大寫的(當然如果你的枚舉是小寫的,那你就小寫,不過混寫比較麻煩哈),其實valueOf就是調用了枚舉的底層映射:



調用的時候會調用這個方法:



所以內部也是一個HashMap,嗬嗬!


拿到這個信息後,就可以做想要的操作了:

switch(sqlTypeEnum) {
  case INSERT:處理insert邏輯;break;
  case DELETE:處理delete邏輯;break;
 ....
}


OK,有些時候可能我們不想直接用INSERT、UPDATE這樣的字符串在交互中使用,因為很多時候命名規範的要求;

例如定義一些用戶操作類型:


1、保存用戶信息

2、通過ID獲取用戶基本信息

3、獲取用戶列表

4、通過ID刪除用戶信息

等等

我們可能定義枚舉會定義為:

public enum UserOptionEnum {
    SAVE_USER,
    GET_USER_BY_ID,
    GET_USER_LIST,
    DELETE_USER_BY_ID
}


但是係統的方法和一些關鍵字的配置,通常會寫成:

saveUser、getUserById、getUserById、deleteUserById

當然各自有各自的規則,不過中間這層映射,你不想做,就一方麵妥協,要麼枚舉名稱全部換掉,貌似挺奇怪的,要麼方法名稱全部換掉,更加奇怪,要麼自己做映射,可以,稍微麻煩點,其實也不麻煩?


我們首先寫個將枚舉下劃線風格的數據轉換為駝峰的方法,放在一個StringUtils裏麵:

public static String convertDbStyleToJavaStyle(String dbStyleString , boolean firstUpper) {
        dbStyleString = dbStyleString.toLowerCase();
        String []tokens = dbStyleString.split("_");
        StringBuilder stringBuilder = new StringBuilder(128);
        int length = 0;
        for(String token : tokens) {
            if(StringUtils.isNotBlank(token)) {
                if(length == 0 && !firstUpper) {
                    stringBuilder.append(token);
                }else {
                    char c = token.charAt(0);
                    if(c >= 'a' || c <= 'z') c = (char)(c - 32);
                    stringBuilder.append(c);
                    stringBuilder.append(token.substring(1));
                }
            }
            ++length;
        }
        return stringBuilder.toString();
    }



重載一個方法:

public static String convertDbStyleToJavaLocalStyle(String dbStyleString) {
        return convertDbStyleToJavaStyle(dbStyleString , false);
    }



然後定義枚舉:

public enum UserOptionEnum {
    SAVE_USER,
    GET_USER_BY_ID,
    GET_USER_LIST,
    DELETE_USER_BY_ID;

    private final static Map<String , UserOptionEnum> ENUM_MAP = new HashMap<String, UserOptionEnum>(64);


    static {
        for(UserOptionEnum v : values()) {
            ENUM_MAP.put(v.toString() , v); 
        }
    }

    public staticUserOptionEnum fromString(String v) {
        UserOptionEnum userOptionEnum = ENUM_MAP.get(v);
        return userOptionEnum == null ? DEFAULT :userOptionEnum;
    }

    public String toString() {
        String stringValue = super.toString();
        return StringUtil.convertDbStyleToJavaLocalStyle(stringValue);
    }
}

OK,這樣傳遞一個event參數讓如果是:saveUser,此時就用:

String event = "saveUser";//假如這裏得到參數
UserOptionEnum enum = UserOptionEnum.fromString(event);


其實就是自己做了一個hashMap,我這加了一個fromString,因為枚舉有一些限製,有些方法不讓你覆蓋,比如valueOf方法就是這樣。


其實沒啥好講的了,非要說,再說說枚舉加一些自定義變量吧,其實枚舉除了是單例的外,其餘的和普通類也相似,它也可以有構造方法,隻是默認情況下不是而已,也可以提供自定義的變量,然後獲取set、get方法,但是如果有set的話,線程不是安全的哦,要注意這點;所以一般是構造方法就寫好了:

public enum SqlTypeEnum {
   INSERT("insert into"),
   DELETE("delete from")
   ......省略;

   private String name;//定義自定義的變量

   private SqlTypeEnum(String name) {
      this.name = name;
   }

   public String getName() {
       return name;
   }

   public String toString() {
       return name + " 我靠";//重寫toString方法
  }
  //一般不推薦
  public void setName(String name) {
        this.name = name;
  }
}


調用下:


SqlTypeEnum sqlTypeEnum = SqlTypeEnum.valueOf("INSERT");
System.out.println(sqlTypeEnum);
System.out.println(sqlTypeEnum.getName());


不推薦也調用下:

sqlTypeEnum.setName("我靠");


在另一個線程:

SqlTypeEnum sqlTypeEnum = SqlTypeEnum.valueOf("INSERT");
System.out.println(sqlTypeEnum);
System.out.println(sqlTypeEnum.getName());


發現結果被改了,嗬嗬!



最後更新:2017-04-04 07:03:27

  上一篇:go 關於12306訂票係統相關的文檔收集整理
  下一篇:go 【最近麵試遇到的一些問題】java中list、set和map 的區別