枚举和注解都是在Java1.5中引入的,虽然他们是后起之秀,但是功能不容小觑,枚举改变了常量的声明方式,注解耦合了数据和代码.

建议83:推荐使用枚举定义常量

一、分析

常量的声明是每一个项目中不可或缺的,在Java1.5之前,我们只有两种方式的声明:类常量和接口常量。不过,在1.5版之后有了改进,即新增了一种常量声明方式,枚举常量。代码如下:

 enum Season{
Spring,Summer,Autumn,Winter;
}

JLS(Java Language Specification,Java语言规范)提倡枚举项全都大写,字母之间用下划线分隔.这也是从常量的角度考虑的.

那么枚举常量与我们的经常使用的类常量和静态常量比有什么优势呢?

枚举的优点主要表现在以下四个方面.

1.枚举常量更简单 

先把Season枚举翻译成接口,代码如下:

 interface Season{
int Sprint = 0;
int Summer = 1;
int Autumn = 2;
int Winter = 3;
}

首先对比以下两者的定义,枚举常量只需要定义每个枚举项,不需要定义枚举值,而接口常量(或类常量)则必须定义值,否则编译通不过,即使我们不需要关注其值是多少也必须定义;其次,虽然两个引用的方式相同(都是“类名.属性”,如Season.Sprint),但是枚举表示的是一个枚举项,字面含义是春天,而接口常量却是一个int类型,虽然其字面含义也是春天,但在运算中我们势必要关注其int值.

2.枚举常量属于稳态型 

例如:我们要给外星人描述一下地球上的春夏秋冬是什么样子的,使用接口常量应该是这样写.

 public void describe(int s){
//s变量不能超越边界,校验条件
if(s >= 0 && s <4){
switch(s){
case Season.Summer:
System.out.println("Summer is very hot!");
break;
case Season.Winter:
System.out.println("Winter is very cold!");
break;
.....
}
}
}

我们需要用switch语句判断是哪一个常量,然后输出.但问题是我们得对输入值进行检查,确定是否越界,如果常量非常庞大,校验输入就是一件非常麻烦的事情,但这是一个不可逃避的过程,特别是如果我们的校验条件不严格,虽然可以编译照样通过,但是运行期就会产生无法预知的后果.

我们再来看看枚举常量是否能够避免校验问题,代码如下:

 public void describe(Season s){
switch(s){
case Season.Summer:
System.out.println("Summer is very hot!");
break;
case Season.Winter:
System.out.println("Winter is very cold!");
break;
......
}
}

不用校验,已经限定了是Season枚举,所以只能是Season类的四个实例。这也是我们看重枚举的地方:在编译期间限定类型,不允许发生越界的情况。

3.枚举具有内置方法 

有一个很简单的问题:如果要列出所有的季节常量,如何实现?接口常量或者类常量可以通过反射来实现,这没错,只是虽然能实现,但会非常繁琐.但是对于枚举就可以非常简单的实现.

 public static void main(String[] args){
for(Season s:Season.values()){
System.out.println(s);
}
}

通过values()方法获得所有的枚举项.这得益于枚举内置的方法,每个枚举都是java.lang.Enum的子类,该基类提供了诸如获得排序值的ordinal方法、compareTo比较方法等,大大简化了常量的访问。

4.枚举可以自定义方法 

这一点似乎不是枚举的优点,类常量也可以有自己的方法,但关键是枚举常量不仅仅可以定义静态方法,还可以定义非静态方法,而且还能够从根本上杜绝常量类被实例化。比如我们在定义获取最舒服的季节,使用枚举的代码如下:

 enum Season{
Spring,Summer,Autumn,Winter;
//最舒服的季节
public static Season getComfortableSeason(){
return Spring;
}
}

我们知道每个枚举项都是该枚举的一个实例,对于我们的例子来说,也就表示Spring其实是Season的一个实例,Summer也是其中的一个实例.那我们再枚举中定义的静态方法既可以在类(Season类)中引用,也可以在实例(也就是枚举项Spring,Summer,Autumn,Winter)中引用,看如下代码:

 enum Season{
Spring,Summer,Autumn,Winter;
//最舒服的季节
public static Season getComfortableSeason(){
return Spring;
}
} public class Client {
public static void main(String[] args) {
System.out.println("The most comfortable season is " + Season.getComfortableSeason());
System.out.println("kxh test " + Season.getComfortableSeason());
}
}

那如果使用类常量要如何实现呢?代码如下:

 class Season{
public final static int Spring = 0;
public final static int Summer = 1;
public final static int Autumn = 2;
public final static int Winter = 3; //最舒服的季节
public static int getComfortableSeason(){
return Spring;
}
}

想想看,我们要怎么才能打印出"The most comfortable season is Spring" 这句话呢? 除了使用switch判断外没有其他更好的办法了.

虽然枚举在很多方面都比接口常量和类常量好用,但是它有一点比不上接口常量和类常量的,就是继承,枚举类型是不能有继承的,也就是说一个枚举常量定义完毕后,除非修改重构,否则无法做扩展。

三、建议

在项目开发中,推荐使用枚举常量代替接口常量或类常量。

[改善Java代码]推荐使用枚举定义常量的更多相关文章

  1. 拔高你的Java代码质量吧:推荐使用枚举定义常量(转)

    提高你的Java代码质量吧:推荐使用枚举定义常量 一.分析 常量的声明是每一个项目中不可或缺的,在Java1.5之前,我们只有两种方式的声明:类常量和接口常量.不过,在1.5版之后有了改进,即新增了一 ...

  2. [改善Java代码]推荐覆写toString方法

    建议49: 推荐覆写toString方法 为什么要覆写toString方法,这个问题很简单,因为Java提供的默认toString方法不友好,打印出来看不懂,不覆写不行,看这样一段代码: public ...

  3. [改善Java代码]推荐使用String直接量赋值

    建议52:推荐使用String直接量赋值 一.建议 String对象的生成方式有两种: 1.通过new关键字生成,String str3 = new String(“中国”); 2.直接声明,如:St ...

  4. [改善Java代码]推荐在复杂字符串操作中使用正则表达式

    一.分析  字符串的操作,诸如追加.合并.替换.倒序.分隔等,都是在编码过程中经常用到的,而且Java也提供了append.replace.reverse.split等方法来完成这些操作,它们使用起来 ...

  5. [改善Java代码] 推荐使用序列化实现对象的拷贝

    建议44: 推荐使用序列化实现对象的拷贝 上一个建议说了对象的浅拷贝问题,实现Cloneable接口就具备了拷贝能力,那我们来思考这样一个问题:如果一个项目中有大量的对象是通过拷贝生成的,那我们该如何 ...

  6. [改善Java代码]小心switch带来的空值异常

    使用枚举定义常量时,会伴有大量的switch语句判断,目的是伪类每个枚举项解释其行为,例如: public class Client { public static void main(String[ ...

  7. 提高Java代码质量:使用枚举定义常量(转)

    一.分析  常量的声明是每一个项目中不可或缺的,在Java1.5之前,我们只有两种方式的声明:类常量和接口常量.不过,在1.5版之后有了改进,即新增了一种常量声明方式,枚举常量.代码如下: enum ...

  8. [改善Java代码]使用构造函数协助描述枚举项

    一.分析 一般来说,我们经常使用的枚举项只有一个属性,即排序号,其默认值是从0.1.2... ....但是除了排序号外,枚举还有一个(或多个)属性:枚举描述,它的含义是通过枚举的构造函数,声明每个枚举 ...

  9. [改善Java代码]在switch的default代码块中增加AssertionError错误

    switch的后跟枚举类型,case后列出所有的枚举项,这是一个使用枚举的主流写法,那留着default语句似乎没有任何作用了,程序已经列举出了所有的可能选项,肯定不会执行到default语句,. 错 ...

随机推荐

  1. bashrc的加载

    无意中将home下的所有文件都删除了,一些配置文件都丢了. 重新登陆后,发现无法加载bashrc. 查找后,发现问题不在于bashrc,而在与.bash_profile丢失 login shell m ...

  2. Java设计模式系列之责任链模式

    责任链模式 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求.发出这个请求的客户端并不知道 ...

  3. [iOS基础控件 - 6.10.4] 项目启动原理 项目中的文件

    A.项目中的常见文件 1.单元测试Test   2.Frameworks(xCode6 创建的SingleView Project没有) 依赖框架   3.Products 打包好的文件   4. p ...

  4. Educational Codeforces Round 15 (A - E)

    比赛链接:http://codeforces.com/contest/702 A. Maximum Increase A题求连续最长上升自序列. [暴力题] for一遍,前后比较就行了. #inclu ...

  5. POJ 3666 Making the Grade (DP)

    题意:输入N, 然后输入N个数,求最小的改动这些数使之成非严格递增即可,要是非严格递减,反过来再求一下就可以了. 析:并不会做,知道是DP,但就是不会,菜....d[i][j]表示前 i 个数中,最大 ...

  6. Windows xp下IDT Hook和GDT的学习

    一.前言   对于IDT第一次的认知是int 2e ,在系统调用的时候原来R3进入R0的方式就是通过int 2e自陷进入内核,然后进入KiSystemService函数,在根据系统服务调用号调用系统服 ...

  7. 使用gcc编译gdb调试

    gcc 在linux系统中,默认是没有安装gcc编译器的,可以通过命令 rpm -q | grep gcc 来查看.安装命令为: yum -y install gcc 安装后,编写一个.c结尾的文件. ...

  8. Javascript继承实现

    S1:js中一切皆对象,想想如果要实现对父对象属性和方法的继承,最初我们会怎样子来实现呢,考虑到原型的概念,最初我是这样来实现继承的 function Parent(){ this.name='123 ...

  9. Bootstrap排版

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta na ...

  10. 如何同时启动多个Tomcat服务

    在项目开发中,有时会需要同时启动多个Tomcat服务,如果直接启动多个的话,会报以下错误: Port busy xxxx java.net.SocketException: Unrecognized ...