- 浏览: 402198 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
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 1426《第9章 线 程》 通过使用线程(thread ... -
【第47条】不要忽略异常
2009-06-15 11:51 1398作为本章的最后一条,此条目是一条“纪律”,一条你必须遵 ... -
【第46条】努力使失败保持原子性
2009-06-15 11:23 1625所谓失败的原子性,就是在一个方法失败之后,使对象保持“它 ... -
【第45条】在细节消息中包含失败-捕获信息
2009-06-15 10:54 1361这一条是写给那些自己写Exception的程序员的。 ... -
【第44条】每个方法抛出的异常都要有文档
2009-06-09 17:12 1590虽然在【第40条】中说到了,Java的throws语法 ... -
【第43条】抛出的异常要合适于相应的抽象
2009-06-09 16:52 1498如果一个方法抛出的异常与它所执行的任务没有明显关联关系 ... -
【第42条】尽量使用标准的异常
2009-06-09 16:37 1386代码重用,是程序员们“千百年来”所追求的目标,同样Ex ... -
【第41条】避免不必要地使用被检查的异常
2009-06-09 16:21 1450也就是说,在使用checkedException时,你必 ... -
【第40条】对于可恢复的条件使用被检查的异常,对于程序错误使用运行时异常
2009-06-09 12:09 2036Java一共有三种可 ... -
【第39条】只针对不正常的条件才使用异常
2009-06-09 11:28 1323《第8章 异常》 异常是Java语言中非常重要 ... -
【第38条】遵守普遍接受的命名惯例
2009-06-03 17:15 1875Java平台(其实整 ... -
【第37条】谨慎地进行优化
2009-06-03 15:57 1480这一条我没有什么好评价的,记住作者的忠告就好。 ... -
【第36条】谨慎地使用本地方法
2009-06-03 15:50 1497Java Native Interface(JNI,J ... -
【第35条】接口优于映像机制
2009-05-31 15:18 1198我个人更觉得,“映像机制”翻译为“反射机制”应该更好一 ... -
【第34条】通过接口引用对象
2009-05-31 15:04 2246在【第25条】中已经讲过“应该使用接口,而不是类作为参数的类型 ... -
【第33条】了解字符串连接的性能
2009-05-27 15:58 1348这一条是一个良好的“习惯”,你可能一直没有注意到,但也 ... -
【第32条】如果其他类型更合适,请尽量避免使用字符串
2009-05-27 15:40 1564这两天在研究通过Hessian远程连接Java和C#。功能强大 ... -
【第29条】将局部变量的作用域最小化
2009-05-25 17:33 2045《第7章 通用程序设计》 本章主要讨论Java语言的语 ... -
【第31条】如果要求精确的答案,请避免使用float和double
2009-05-25 16:30 2393float和double型,的底层实现是二进制的。十进制 ... -
【第30条】了解和使用库
2009-05-25 15:46 1271Java语言的丰富多彩,很大程度上是体现在丰富的类库上 ...
相关推荐
- 使用`std::swap()`函数来代替文中提到的自定义符号`<->`进行变量交换。 - 输入三个整数后,通过三次比较和可能的交换,确保它们按从大到小的顺序输出。 ##### 1.17 求k阶斐波那契序列的第m项的值 ```c Status ...
第Ⅲ部分 类设计者的工具 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...
- **区别**: `&&` 具有短路特性,即如果第一个表达式为假,则第二个表达式不会被评估;而`&`会一直评估所有表达式,即使结果已知。 - **注意事项**: 在条件判断中优先使用`&&`以提高效率和安全性。 **4. 在JAVA中...
两者之间的主要区别在于 `&&` 具有短路效应:如果第一个表达式为 `false`,则第二个表达式不会被评估。而 `&` 操作符会始终计算两边的表达式。 **4. 在 JAVA 中如何跳出当前的多重嵌套循环?** 在 Java 中,可以...
2. **布尔运算**:题目中的第二题涉及布尔逻辑运算符`&&`,表示逻辑与。当`x`和`y`均为`bool`类型时,`x && y`只有在两者都为真时才为真。正确答案是D,它们都为真。 3. **逻辑表达式取反**:第三题要求找出逻辑...
2. 第二范式(2NF):非主键属性完全依赖于主键。 3. 第三范式(3NF):消除传递依赖。 七、数据库安全性 1. 用户权限管理:GRANT和REVOKE命令分配和撤销用户权限。 2. 视图(View):提供安全访问,只允许用户看到...
静态构造函数仅在第一次访问类的静态成员时执行一次。 ```csharp public class MyClass { public static string StaticValue; static MyClass() { StaticValue = "Hello, World!"; } } ``` #### 14. 用多个...
因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...
8. 定义二维数组时,第一维长度可以省略,宏定义是用宏名代替一字符串,也就是作简单的置换,不作语法检查。 9. 逻辑运算优先级别都低于算术运算,continue 语句的作用是提前结束整个循环的执行。 10. 打开文件的...
例如,十进制数10在计算机中存储为二进制`0000 0000 0000 0000 0000 0000 0000 1010`,而其补码表示为正数时不变,为负数时会取反加1,如-10的补码是`1111 1111 1111 1111 1111 1111 1111 0110`。 理解这些基础知识...
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 我需要根据条件把一个复杂的表达式赋值给两个...
例如,可以设置一个别名`@todo`来代替`@see \todo`。 3. **ALLEXTERNALS**:如果开启,Doxygen会处理所有外部链接的文档,这对于构建完整的项目文档非常有用。 4. **ALPHABETICAL_INDEX**:开启后,Doxygen会生成...
2.22 有没有一种自动方法来跟踪联合的哪个域在使用? 30 枚举 31 2.23 枚举和一组预处理的#define有什么不同? 31 2.24 枚举可移植吗? 31 2.25 有什么显示枚举值符号的容易方法吗? 31 位域 31 2.26 ...