- 浏览: 396724 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
c253898303:
求和的时候说是调用store的基础方法,这个能重写吗?如果可以 ...
给Extjs的GridPanel增加“合计”行 -
rhhao:
这个附件怎么用呢?
自己写段代码批量修改照片的Exif数据 -
AndLong:
【转】关于烂代码的那些事(下) -
TonyLian:
无意中翻出这篇老博文,文章中留下的遗憾“纯JSP如何获取req ...
Spring获得各种客户端HttpServletRequest的方法 -
TonyLian:
注释中应该写“这里为什么要做XXX”,“为什么这里没有做XXX ...
【转】关于烂代码的那些事(中)
要先说明一下:本书写作于2001年,正值作者参与建设JDK1.4的时期。后来到了JDK1.5,Java又将抛弃了多年的enum枚举重拾了起来。所以本条是在没有enum的时候写的。
JDK1.4及以前版本省略了enum。其实enum也是一种struct,我们当然是用class来代替之,但为什么又要单独作为一条来讲呢?是因为用类来替代枚举的时候,比较容易(或者说事实中绝大多数人都已经)犯一些错误。
比如我们要一个有三种颜色的Color,有人会定义为
public class Color { public static final String RED = "RED"; public static final String GREEN = "GREEN"; public static final String BLUE = "BLUE"; }
这是我们非常常见的常量定义方法,通过 Color.RED 来使用。但是,你有没有想过,如果一个“不太合格”的程序员很有可能会写出这样的代码:
if (dotA.color.equals("RED")) { ...... }
这就将硬编码写到了程序中,而且如果不做Source Review是很难发现的。
这种写法还有一个“变种”,用int而非String:
public class Color { public static final int RED = 0; public static final int GREEN = 1; public static final int BLUE = 2; }
当然,那个人可以继续写出 if (dotA.color == 0) { 这样的语句,Source Review 发现的概率更低了。
但是使用int型一个小“变种”,回来带一些好处:
public class Color { public static final int RED = 1; public static final int GREEN = 2; public static final int BLUE = 4; }
每个值都是用2的整数幂,这样可以在需要保存多于一个状态的和的时候,将所有项目相加(或者位或)得到“保存值”。使用的时候,在通过此值与相应项目的位与后的Boolean值来判断是否包含之:
// 保存时 int value = Color.RED | Color.BLUE; // 或 Color.RED + Color.BLUE saveValue(value); // 保存,可能是写入数据库 // 使用时 int value = getValue(); // 从数据库中取出 if (value & Color.RED) { system.out.println("包含红色"); } else if (value & Color.GREEN) { system.out.println("包含绿色"); } else if (value & Color.BLUE) { system.out.println("包含蓝色"); } // 结果会是: // 包含红色 // 包含蓝色
当项目中这样的常量组越来越多,constants包中类会越来越多,甚至都想 import xxxx.constants.*; 了。于是有人开始把这些所有的常量组,放入一个Constants类中
public class Constants{ // 颜色 public static final int COLOR_RED = 1; public static final int COLOR_GREEN = 2; public static final int COLOR_BLUE = 4; // 形状 public static final int SHAPE_CIRCLE = 1; public static final int SHAPE_RECTANGLE = 2; public static final int SHAPE_TRIANGLE = 4; ...... }
现在import一个类就好了,但是,那个人又来了,这次他吸取了之前的教训,没有硬编码了,但是他写了:
if (dotA.color == Constants.SHAPE_CIRCLE) { // A点的颜色是圆形吗? &^!#&^*`~ 倒! .......
这样的语句是不会被编译器挑出来的,那么你能做的,除了祈祷就是痛哭了!在之前的模式下,没有写成 if (dotA.color == Shape.CIRCLE) 就已经不错了。
面对这个问题,书中给出了一个“尚未被人知晓”的方法——类型安全枚举模式。注意,它只是一种模式,再次强调JDK1.5之前并没有枚举,它实质上还是一个类。
public class Color { private final String name; private Color(String name){ // 使用者无法创建这个类的实例 this.name = name; } public String toString() { return this.name; } public static final Color RED = new Color("red"); public static final Color GREEN = new Color("green"); public static final Color BLUE = new Color("blue"); }
私有的构造函数,保证了使用者无法创建这个类的实例,除了通过公有的静态final域导出的Color对象外,永远也不会再有其他实例存在。
所谓“类型安全”正式它提供了编译时的类型安全性。任何传入的非null的对象引用,一定表示了三种颜色中的一种。这样的模式下,即防止了类型错误,有防止了硬编码问题。
if (dotA.color.equals(Color.RED)) { // 前提:dotA的实现类中,color的类型既不是int也不是String,而是Color ...... }
我们看到了两大好处。再看看JDK1.5提供的enum关键字(它对应一个类Enum),从使用方法和特征来说,类型安全枚举模式和JDK1.5的enum枚举是非常相似的。那么在没有枚举的年代,作者尚且建议我们使用类型安全枚举模式,如今有了enum,我们就更没有理由拒绝它了。
但是,类型安全枚举模式和enum枚举就没有弱点吗?当然不是,首当其冲的就是刚才提到的,当常量组非常多的时候,使用N多的类型安全枚举模式或enum枚举,可能是一件让人头痛的事情。也许你会因为这一点而一票否决了它。
再有就是如果对应枚举中项目的值(如前例中的dotA.color)将要保存到数据库或外部文件时,势必要用一个int或String来代表之,为此可能要在类型安全枚举类中增加一个private value,然后toString方法返回这个value的String形式;或者在enum中定义抽象方法(类似toString)。但这也可能导致toString方法的滥用,而失去“类型安全”的保护。
最后,当类型安全枚举模式的类(没有研究enum枚举可否)实现了序列化后,如果期待不同版本程序序列化出来的字符流可以相互反序列化,那么后续版本就只能够在原来的最后面追加元素。(当然这一点对于int型和String型常量类也是一样的,而且还可能更糟)
本来还想好好研究一下enum,但是,刚刚得知稍后要进行的公司羽毛球比赛被抽到的下下签,没有心情了,以后再说吧。今天的笔记就写到这儿了。
【Effective Java 学习笔记】系列连载专题请见:
http://tonylian.iteye.com/categories/64208
- 218_枚举.pdf (131.2 KB)
- 下载次数: 20
发表评论
-
【第48条】对共享可变数据的同步访问
2009-06-23 12:18 1385《第9章 线 程》 通过使用线程(thread ... -
【第47条】不要忽略异常
2009-06-15 11:51 1354作为本章的最后一条,此条目是一条“纪律”,一条你必须遵 ... -
【第46条】努力使失败保持原子性
2009-06-15 11:23 1577所谓失败的原子性,就是在一个方法失败之后,使对象保持“它 ... -
【第45条】在细节消息中包含失败-捕获信息
2009-06-15 10:54 1299这一条是写给那些自己写Exception的程序员的。 ... -
【第44条】每个方法抛出的异常都要有文档
2009-06-09 17:12 1550虽然在【第40条】中说到了,Java的throws语法 ... -
【第43条】抛出的异常要合适于相应的抽象
2009-06-09 16:52 1441如果一个方法抛出的异常与它所执行的任务没有明显关联关系 ... -
【第42条】尽量使用标准的异常
2009-06-09 16:37 1346代码重用,是程序员们“千百年来”所追求的目标,同样Ex ... -
【第41条】避免不必要地使用被检查的异常
2009-06-09 16:21 1399也就是说,在使用checkedException时,你必 ... -
【第40条】对于可恢复的条件使用被检查的异常,对于程序错误使用运行时异常
2009-06-09 12:09 1990Java一共有三种可 ... -
【第39条】只针对不正常的条件才使用异常
2009-06-09 11:28 1278《第8章 异常》 异常是Java语言中非常重要 ... -
【第38条】遵守普遍接受的命名惯例
2009-06-03 17:15 1837Java平台(其实整 ... -
【第37条】谨慎地进行优化
2009-06-03 15:57 1434这一条我没有什么好评价的,记住作者的忠告就好。 ... -
【第36条】谨慎地使用本地方法
2009-06-03 15:50 1452Java Native Interface(JNI,J ... -
【第35条】接口优于映像机制
2009-05-31 15:18 1162我个人更觉得,“映像机制”翻译为“反射机制”应该更好一 ... -
【第34条】通过接口引用对象
2009-05-31 15:04 2198在【第25条】中已经讲过“应该使用接口,而不是类作为参数的类型 ... -
【第33条】了解字符串连接的性能
2009-05-27 15:58 1300这一条是一个良好的“习惯”,你可能一直没有注意到,但也 ... -
【第32条】如果其他类型更合适,请尽量避免使用字符串
2009-05-27 15:40 1513这两天在研究通过Hessian远程连接Java和C#。功能强大 ... -
【第29条】将局部变量的作用域最小化
2009-05-25 17:33 1996《第7章 通用程序设计》 本章主要讨论Java语言的语 ... -
【第31条】如果要求精确的答案,请避免使用float和double
2009-05-25 16:30 2336float和double型,的底层实现是二进制的。十进制 ... -
【第30条】了解和使用库
2009-05-25 15:46 1234Java语言的丰富多彩,很大程度上是体现在丰富的类库上 ...
相关推荐
第Ⅲ部分 类设计者的工具 437 第13章 拷贝控制 439 13.1 拷贝、赋值与销毁 440 13.1.1 拷贝构造函数 440 13.1.2 拷贝赋值运算符 443 13.1.3 析构函数 444 13.1.4 三/五法则 447 13.1.5 使用=default...
第Ⅲ部分 类设计者的工具 437 第13章 拷贝控制 439 13.1 拷贝、赋值与销毁 440 13.1.1 拷贝构造函数 440 13.1.2 拷贝赋值运算符 443 13.1.3 析构函数 444 13.1.4 三/五法则 447 13.1.5 使用=default...
第二种:覆盖方法 public new XXXX(){} 第三种:new 约束指定泛型类声明中的任何类型参数都必须有公共的无参数构造函数。 2.如何把一个array复制到arrayList里 foreach( object o in array )arrayList.Add(o); 3....
在这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。 2. 由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进位。 3. 位域...
1.3 什么时候用一条switch语句比用多条if语句更好? 9 1.4 switch语句必须包含default分支吗? 10 1.5 switch语句的最后一个分支可以不要break语句吗? 11 1.6 除了在for语句中之外,在哪些情况下还要使用逗号...
因pdf的容量过大分4个压缩包打包,还有一个源码另外下载。 《.NET深入体验与实战精要》作者身为从事.NET一线开发的资深开发专家,常年耕耘...15.5.14 使用视图代替跨库操作 572 15.5.15 尽量避免大事务操作 572 15.5.16...
因pdf的容量过大分4个压缩包打包,还有一个源码另外下载。 《.NET深入体验与实战精要》作者身为从事.NET一线开发的资深开发专家,常年耕耘...15.5.14 使用视图代替跨库操作 572 15.5.15 尽量避免大事务操作 572 15.5.16...
2.22 有没有一种自动方法来跟踪联合的哪个域在使用? 枚举 2.23 枚举和一组预处理的#define有什么不同? 2.24 枚举可移植吗? 2.25 有什么显示枚举值符号的容易方法吗? 位域 2.26 一些结构声明中的这些...
3 什么时候用一条switch语句比用多条if语句更好? 1. 4 switch语句必须包含default分支吗? 1. 5 switch语句的最后—个分支可以不要break语句吗? 1. 6 除了在for语句中之外,在哪些情况下还要使用逗号运算? 1. 7...
o 4.10 如果我不使用表达式的值, 我应该用 ++i 或 i++ 来自增一个变量吗? o 4.11 为什么如下的代码 int a = 100, b = 100; long int c = a * b; 不能工作? o 4.12 我需要根据条件把一个复杂的表达式赋值给两个...
2.22 有没有一种自动方法来跟踪联合的哪个域在使用? 30 枚举 31 2.23 枚举和一组预处理的#define有什么不同? 31 2.24 枚举可移植吗? 31 2.25 有什么显示枚举值符号的容易方法吗? 31 位域 31 2.26 ...
3.10 如果我不使用表达式的值, 我应该用++i 或i++ 来自增一个变量 吗? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 3.11 为什么如下的代码int a = 100, b = 100; long int c = a * b;...
#include #define LED P1^1 //用符号 LED 代替 P1_1 用符号 //用符号 KEY_ON 代替 P1_6 用符号 //用符号 KEY_OFF 代替 P1_7 用符号 //单片机复位后的执行入口,void 表示空,无输入参数,无返回值 #define KEY_ON ...