A1 词汇问题
A 1.1 字母l在许多字体中都与数字1相像
规则:在long类型字面常量中,应该总是用大写L,千万不要用小写l。不要用孤零零的一个l作为变量名。 谜题4
A 1.2 负的十六进制字面常量看起来像正的
规则: 要避免混合类型的计算。在恰当的地方要用long类型的字面常量代替int类型的字面常量。谜题5
A 1.3 八进制字面常量与十进制字面常量相像
规则: 要避免八进制字面常量。如果非得使用它们,那么请对所有用到的地方进行注释,以使得你的意图清晰。 谜题59
A 1.4 ASCII字符的Unicode转义字符容易令人迷茫
规则: 不要使用ASCII字符的Unicode转义字符。如果可以的话,应该直接使用ASCII字符。在字符串字面常量和字符字面常量中,应该优选转义字符列而不是Unicode转义字符。 谜题14、16、17
A 1.5 反斜杠必须被转义,即使是在注释中
规则: 如果你在编写一个可以生成Java源代码的系统,在生成的字符字面常量、字符串字面常量和注释中都要对反斜杠进行转义。Windows文件名最容易引发问题。 谜题15、16
A 1.6 块注释不要嵌套
规则使用单行注释来注释掉代码 谜题19
A 2 整数运算
A 2.1 %操作符的非零结果具有和左操作数相同的正负号
规则: 如果你需要一个非负的余数,而%操作符的结果又是负的,那么你应该在结果上加一个模数。 谜题1、64
A 2.2 整数运算的悄悄溢出
规则: 要用足够大的类型来保存结果,包括中间结果。 谜题3、26、33、65
A 2.3 int数值之差的正负号不能可靠地指示其大小顺序
规则: 不要用基于减法的比较器,除非你可以肯定数值的差不会大于Ingeter.MAX_VALUE。 谜题65
A 2.4 复合赋值操作符可能造成悄悄的窄化转型
规则: 不要在类型为byte、short或char的变量上使用复合操作符。 谜题9、31
A 2.5 整数类型不对称: Integer.MIN_VALUE是它自己的负值,Long.MIN_VALUE也一样
规则: 要保守地编程,如果有必要就用long来代替int。 谜题33、64
A 2.6 移位操作符只用了其右操作数的低位
规则: 移位的位数应该用常量。如果移位的位数必须用变量,那么一定要检查移位的距离是否在允许的范围内。 谜题27
A 2.7 当在整数类型之间转换时,如果源类型是有符号的则执行符号扩展
规则: 操作byte类型的数值时一定要小心,它是有符号的。要想禁止符号扩展,可以使用位掩码。 谜题6
A 3 浮点运算
A 3.1 浮点运算是不精确的
规则: 在需要精确结果的地方不要使用浮点,应该使用一个整数类型或BigDecimal。
要避免浮点类型的循环索引。
要避免在浮点变量上使用++和—操作符,因为这些操作对多数的浮点数都不起任何作用。
避免测试浮点值是否相等。
宁愿用double,而不用float。 谜题2、28、34
A 3.2 NaN不等于任何浮点数值,也包括它自身
规则: 要避免测试浮点数的相等性。这么做并不总能避免问题,但至少是一个良好的开端。 谜题29
A 3.3 从int到float、从long到float以及从long到double的转换是有损精度的
规则: 要避免整型和浮点型混合的计算。要优选整型算术而不是浮点型算术。 谜题34、 87
A 3.4 BigDecimal(double)构造器返回的是其浮点型参数的精确值
规则: 应该总是使用BigDecimal(String)构造器,而永远不要使用BigDecimal(double)。 谜题2
A 4 表达式计算
A 4.1 混合类型计算容易令人迷茫
规则: 要避免混合类型的计算。
当把?:操作符作用于数字操作数时,第二个和第三个操作数要使用相同的数字类型。宁愿使用不变的变量,不愿使用内置的幻数。 谜题5、8、24
A 4.2 操作符的操作数是从左到右计算的
规则: 要避免在同一个表达式中对相同的变量多次赋值。尤其是在同一个表达式中的多重复合赋值操作符特别令人迷茫 谜题7、25、42
A 4.3 操作符的优先级并不总是很明显
规则: 要使用括号而不是空格来让优先级变得明显。要用具名的常量变量来替换内联的常量表达式。 谜题11、 35、 95
A 4.4 操作符==和!= 在被包装的基本类型上执行引用比较
规则: 要想强制进行值比较,需要在比较之前将一个操作数赋值或转型成恰当的基本类型。 谜题32
A 4.5 常量变量在所用的地方是内联的
规则: 要避免导出常量字段,除非它们表示的是永远都不会变化的真正的常量。可以使用一个恒等函数将一个表达式变成非常量。谜题93
A 4.6 操作符&和|即使在作用于布尔类型的数值时,也要同时计算其两个操作数
规则: 要避免将&和|作用与布尔类型的操作数。对有意识的使用要加以注释。谜题42
A 5 控制流
A 5.1 在switch case语句中缺少break将导致控制流一贯而下
规则: 不要从一个非空的case一贯而下到另一个case:要用一个break终止每一个非空的case。对任何有意识的一贯而下要加以注释 。 谜题23
A 5.2 在Integer.MAX_VALUE上终止以int为索引的循环是困难的
规则: 要在Integer.MAX_VALUE处终止一个以int为索引的循环,需要使用long类型的循环索引,或者要非常仔细小心地编写该循环。 谜题26
A 5.3 finally语句块的意外完成将掩饰挂起的控制传输
规则: 要确保每一个finally语句块都正常完成,以防止致命的错误。不要从一个finally语句块中返回或抛出一个异常。 谜题36、41
A 5.4 为正常的控制流使用异常将导致bug和很差的性能
规则: 应该只为异常情况使用异常,永远不要为正常的控制流使用异常。谜题42
A 6 类初始化
A.6.1 类初始化是自顶向下的
规则:要确保静态字段以恰当的顺序被初始化。要使用延时初始化来解决初始化循环问题,该问题可以涉及一个或多个类。 谜题49 52
A.6.2 oNoClassDefFoundError出现的时机是不可靠的
规则:不要捕获NoClassDefFoundError, 而是应该使用反射并捕获ClassNotFoundException。更一般地讲,不要捕获Error及其子类。 谜题 44
A.7 实例的创建与销毁
A.7.1 实例初始器在构造器方法体之前执行
规则:如果自身类型的实例字段在构造阶段会引发递归,那么要确保该递归能够终止。 谜题40
A.7.2 在构造器中调用被覆盖的方法会导致该方法在实例初始化之前运行
规则:永远不要在构造器中调用可覆盖的方法。要用延迟初始化来解决初始化循环问题。谜题51
A.7.3 引用无效失败会导致内存泄露
规则: 要能够使长生命周期的对象中已过时的对象引用变成无效。不能做到这一点就会导致内存泄露,在诸如java这种垃圾回收语言中,更恰当的说法是无意识的对象保留。
A.7.4 添加私有构造器失败会使类可实例化
规则:如果你想让一个类不可实例化,那么就添加一个私有构造器。更一般地讲,应该总是至少提供一个构造器,永远都不要依赖于默认的构造器。
A.7.5 终结器是不可预知的、危险的,且速度很慢
规则: 要避免使用终结器。
A.7.6 被克隆的对象可以共享内部状态
规则: 要避免实现Cloneable接口。如果实现了它,那么你应该复制所有不想在该对象及其克隆之间共享的内部对象。
A.8 其他与类和实例相关的主题
A.8.1 在静态方法上没有任何动态分派
规则: 永远不要用表达式来限定静态方法调用,应该总是用类型来限定。谜题48
A.8.2 内部类是令人迷茫的
规则: 优先考虑使用静态成员类而不是内部类。 谜题80 89 92
A. 8.3 不能做保护复制就会破坏不变性
规则: 在需要的时候,要对输入参数和输出值做保护复制。
A.8.4 实现一个接口会影响实现类的API
规则:不要去实现一个可以获取对其静态字段的无限定访问权的接口。不要编写只是由字段组成的接口,即所谓的常量接口。在5.0及以后,可以使用静态导入作为静态接口这一有害模式的替代物。
A.8.5 int常量作为枚举值不安全
规则: 应该使用enum类型,或者,如果你使用5.0之前的版本,应该实现类型安全的枚举。
A.8.6 混合使用基本类型和参数化类型将弱化类型检查机制
规则: 应该在你的代码中消除被报告的“不受检查”的警告。在5.0及以后,避免使用基本类型。 谜题88
A. 8.7 返回null而不是0长度的数组或集合有产生错误的倾向
规则:不要从一个返回数组或集合的方法中返回null。
A.9 名字重用
A.9.1 想要覆盖时很容易就变成了重载
规则:请机械地复制每一个你想要覆盖的超类方法的声明,但最好还是让IDE帮忙做。如果使用5.0,请使用@Override注释。 谜题58
A.9.2 重载解析规则不明显
规则: 要避免重载。如果你在API中的两个方法都可以应用于某些调用,那么请确保这两个方法在相同的实参上具有相同的行为。 谜题11 46 74
A.9.3 隐藏实体的程序难以理解
规则: 要避免隐藏 谜题66 72 92
A.9.4 遮蔽实体的程序难以理解
规则:要避免遮蔽。不要在公共API中重用java.lang.Object中的名字。不要试图在一个已经被定义的名字上使用静态导入。 谜题71 73 89
A.9.5 遮掩实体的程序难以理解
规则: 要避免遮掩。要遵守命名约定。 谜题68 69
A.9.6 与所在类具有相同名字的方法看似构造器
规则: 要遵守命名约定。 谜题63
A.9.7 重用平台类名的程序难以理解
规则: 要避免重用平台类的名字,并且永远不要重用java.lang中的类名。 谜题67
A.10 字符串
A.10.1 数组不能覆盖Object.toString
规则: 对于char数组,应该使用String.valueOf来获取表示指定字符序列的字符串。对于其他类型的数组,应该使用Arrays.toString,如果是5.0以前,应该使用Arrays.asList。 谜题12
A.10.2 String.replaceAll以正则表达式作为第一个参数
规则: 要确保该参数是一个合法的正则表达式,要不然就用String.replace来代替。 谜题20
A.10.3 String.replaceAll以置换字符串作为第二个参数
规则: 要确保该参数是一个合法的置换字符串,要不然就用String.replace来代替。 谜题20
A.10.4 重复地进行字符串连接可能导致极差的性能
规则: 要避免在循环中使用字符串连接。
A.10.5 从字节数组到字符数组的转换需要指定字符集
规则:在将一个byte数组转换成一个字符串或一个char数组时,总是要选择一个字符集; 如果你没有这么做,就会使用平台默认的字符集,从而导致不可预知的行为。 谜题18
A.10.6 char类型值只会默认转换从int,而不是String
规则: 要想把一个char转换成一个字符串,应该使用String.valueOf(char)。 谜题11 23
A 11 I/O
A.11.1 Stream.close可以抛出IOException异常
规则: 要在close上捕获异常,并且一般做法是忽略这些异常。 谜题41
A.11.2 PrintStream.write(int)不刷新输出流
规则: 要避免使用PrintStream.write(int)。如果你使用了它,要在需要时调用flush。 谜题81
A.11.3 要消费掉一个进程的输出,否则该进程可能挂起
规则: 应该总是消费掉你所创建的进程的输出。 谜题82
A 12 线程
A.12.1 调用Thread.run不能启动一个线程。
规则: 永远不要调用Thread.run。 谜题76
A.12.2 库类可能锁住或通知它们的实例
规则:如果你在扩展一个库类,那么请不要使用实例锁。取而代之的是,应该使用存储在一个私有字段中的单独的锁对象。 谜题77
A,12,3 Thread.interrupted会清除中断状态
规则: 不要使用Thread.interrupted,除非你想要清除当前线程的中断状态。 谜题84
A.12.4 类初始化过程中将持有该类的锁
规则: 要避免死锁的风险,永远不要在类初始化过程中等待一个后台线程。 谜题85
A.12.5 在共享可变状态时同步失败,可能导致不能观察状态的变化
规则: 要同步对共享的可变状态的访问。
A.12.6 在被同步的语句块中调用外部方法可能导致死锁
规则: 永远不要将控制流让给在被同步的方法或语句块中调用的外部方法。
A.12.7 在while循环的外部调用wait方法会引发不可预知的行为
规则: 永远不要在一个while循环的外部去调用wait。
A.12.8 对线程调度器的依赖可能导致不定的且平台依赖的行为
规则: 为了编写健壮的、相应迅速的和可移植的多线程程序,应该确保在任何给定的时刻,都只有极少数的线程是可运行的。
A 13 反射
A.13.1 反射将检查对实体和实体所属类的访问权限
规则: 用反射实例化类,用接口访问实例。 谜题78
A.13.2 用反射实例化内部类需要一个额外的参数
规则: 不要在内部类上使用反射。优先考虑使用静态成员类,而不是内部类。 谜题80
A.13.3 Class.newInstance可以抛出未声明的受检查异常
规则: 只要存在构造器会抛出受检查异常的任何可能性,那么就应该使用java.lang.reflact.Constructor.newInstance而不是Class.newInstance。 谜题43
A 14 序列化
A.14.1 让一个类可序列化将引入一个公共的伪构造器
规则: 在让一个类可序列化之前,一定要三思而行。在接受默认的readObject方法之前,一定要三思而行。编写readObject方法时,要采取保护性措施。
A.14.2 序列化形式是类的公共API的一部分
规则: 在设计序列化形式时,应该与设计任何其他的API一样小心仔细。
A.14.3 使用默认的序列化形式会在类的公共API中泄露私有字段
规则: 应该考虑使用某种定制的序列化形式
A.14.4 使用默认的序列化形式可能导致性能低下
规则: 应该考虑使用某种定制的序列化形式。
A.14.5 维护实例控制的不变规则需要一个readResolve方法
规则: 应该总是为单例、自编的类型安全的枚举类型以及其他实例控制的可实例化的类编写一个readResolve方法。 谜题83
A.14.6 声明序列版本UID失败会导致脆弱。
规则: 应该在可序列化的类中声明一个显式的序列版本UID。
A.14.7 如果readObject或readResolve调用了可覆盖的方法,反序列化循环的对象可能引发崩溃
规则: 如果一个HashSet、HashMap或Hashtable将被序列化,那么要确保其内容不会返回来引用到它。在readObject和readResolve方法中,要避免在当前正在被序列化的对象上调用方法,如果你无法听从这条建议,那么也要确保在对象图中不会存在任何有问题的循环。 谜题91
A 15 其他库
A.15.1 覆盖equals方法而不覆盖hashCode方法可能会引发不定的行为
规则: 当覆盖equals方法时,总是要一并覆盖hashCode方法 谜题57
A.15.2 Calendar和Date设计得很差劲
规则: 在使用Calendar和Date时,一定要参考API文档。
A.15.3 许多类不管其方法名是什么,这些类都是不可变的
规则: 不要被误导而认为不可变的类型是可变的,不可变的类型包括String、Integer,Long,Short, Byte, Character, Boolean, Float, Double, BigInteger, BigDecimal. 谜题56
A.15.4 某些被弃用的方法对程序来说是毒药
规则: 要避免使用已经被弃用的方法,例如Thread.stop,Thread.suspend, oRuntime.runFianlizersnExit和System.runFianlizerOnExit。 谜题39 43
A.15. 5 使用自编的解决方案而不是库容易导致努力白费、bug产生以及极差的性能
规则: 要了解并使用库 谜题60 62 94.