第五章 泛型
[x]第25条:列表优先于数组
因为列表在编译阶段会作安全性检查,而数组在运行时才报异常。不能创建不可具体化的类型的数组,例如E[] = new E[3];
[x]第26条:优先考虑泛型
使用泛型比使用需要进行转换的类型来得更加安全。
[x]第27条:优先考虑泛型方法
泛型方法的一个显著特性是,无需明确指定类型参数的值,不像调用泛型构造器的时候必须指定。
[x]第28条:利用有限制通配符来提升API的灵活性
函数接口参数和返回类型采用通配符,能更加灵活
[x]第29条:优先考虑类型安全的异构容器
String.class的类型是Class<String>, Integer.class的类型是Class<Integer>
第六章 枚举和注解
[x]第30条:用enum代替int常量
int枚举模式的缺点:编译器不报警告、打印信息不直接
String枚举模式的缺点:性能问题
enum枚举模式
策略枚举(strategy enum)
[x]第31条:用实例域代替序数
枚举的序号使用
[x]第32条:用EnumSet代替位域
java提供了EnumSet类来有效地表示从单个枚举类型中提取的多个值的集合
[]第33条:用EnumMap代替序数索引
[x]第34条:用接口模拟可伸缩的枚举
枚举类型无法扩展,但是通过编写接口以及实现该接口的基础枚举类型来模拟。
[]第35条:注解优先于命名模式
[x]第36条:坚持使用Override注解
Override只能使用在方法声明中,可以避免异常覆盖超类的方法
[x]第37条:用标记接口定义类型
标识接口是没有任何方法和属性的接口, 当一个类实现了一个标识接口之后就像是给自己打了个标签。
常见的标记接口有 Serializable、 Cloneable。 java.io.Serializable这个接口是用来标记类是否支持序列化的,所谓的序列化就是将对象的各种信息转换成可以存储或者传输的一种形式。如果一个类没有实现该接口,却被拿去序列化的了,那么虚拟机就会抛出不支持序列化的异常。
第7章 方法
[x]第38条 检查参数的有效性
1、增加@throws标签在文档中说明违反参数值限制时抛出的异常;
2、使用断言assert来检查参数;
[x]第39条 必要时进行保护性拷贝
编写方法或者构造器时,对每个可变参数进行保护性拷贝
[x]第40条 谨慎设计方法签名
1、设计方法名称要易于理解、与其他风格一致、大众都认可的
2、不要过于追求提供便利的方法
3、避免过长的参数列表(分解层多个方法、创建辅助类保存参数分组、采用Builder模式保存参数)
4、参数类型优先使用接口
[x]第41条 慎用重载
对于多个具有相同参数的方法来说,应该尽量避免重载方法。
[x]第42条 慎用可变参数
使用可变参数方法,每次调用都会导致进行一次数组分配和初始化,因此性能会下降。
[x]第43条 返回零长度的数组或者集合,而不是null
建议返回零长度的数组或者集合
[x]第44条 为所有导出的API元素编写文档注释
第8章 通用程序设计
[x]第45条 将局部变量的作用域最小化
局部变量作用域最小化,最有效的办法就是在第一次使用它的地方声明。
[x]第46条 for-each循环优先于传统的for循环
for-each循环能通过完全隐藏迭代器或者索引变量,避免出现混乱和出错的可能,适用于数组和集合。
但是三种情况无法使用for-each,过滤某个元素、转换某个元素、平行迭代
[x]第47条 了解和使用类库
随机用Random.nextInt(int)
[x]第48条 如果需要精确的答案,请避免使用float和double
使用BigDecimal
[x]第49条 基本类型优先于装箱基本类型
Integer,Long之类的装箱基本类型,和基本类型转换时会执行拆箱操作。导致效率下降
[x]第50条 如果其他类型更适合,则尽量避免使用字符串
字符串不适合代替其他值类型;
字符串不适合代替枚举类型;
字符串不适合代替聚合类型;
字符串也不适合代替能力表
[x]第51条 当心字符串连接的性能
如果字符串拼接的数量巨大,推荐使用StringBuilder代替String拼接
[x]第52条 通过接口引用对象
[]第53条 接口优先于反射机制
[x]第54条 谨慎使用本地方法
本地方法可以提高性能,访问特定于平台的机制,提供访问遗留代码库的能力。
但是本地方法不是安全的方法,因和平台相关,使用本地方法的应用程序也不再是可以自由移植的,而且更难调试。
如果本地方法只做少量工作,法尔会降低性能。
[x]第55条 谨慎地进行优化
不要为了性能而牺牲合理的结构,在设计过程中要考虑到性能问题。
努力避免那些限制性能的设计决策。
[x]第56条 遵守普遍接受的命名惯例
1、包的名称应该是层次状的,用句号分隔每个部分。
2、包名称的其钰部分应该包括一个或多个描述该包的组成部分。鼓励使用有意义的缩写形式。
3、类和接口的名称,包括枚举和注解类型的名称,都应该包括一个或多个单词,每个单词的首字母大写,如Timer。
4、方法和域的名称首字母应该小写。
5、常量域的名称应该包含一个或多个大写的单词。
6、局部变量的命名取决于所在的上下文环境。
7、类型参数名称通常由单个字母组成,T表示任意的类型、E表示结合的元素类型、K和V表示映射的键和值类型、X表示异常。
8、执行某个动作的方法通常用动词或者动词短语来命名,返回boolean的方法往往以“is”开头,后面跟名词或者名词短语,或者任何具有形容词功能的单词或短语。
9、如果方法所在的类是个Bean(JavaBeans),就要强制使用以get开头的形式。
第9章 异常
[x]第57条 只针对异常的情况才使用异常
异常是为了在异常情况下使用而设计的,不要将它们用于普通的控制流,也不要编写迫使它们这么做的API。
[x]第58条 对可恢复的情况使用受检异常,对编程错误使用运行时异常
java的三种可抛出异常结构:受检的异常(checked exception)、运行时异常(run-time exception)和错误(error)。
对于可恢复的情况,使用受检的异常;对于程序错误,使用运行时异常。
[x]第59条 避免不必要地使用受检异常
如果方法抛出一个或者多个受检的异常,调用该方法的代码就必须在一个或者多个catch块中处理这些异常,或者它必须声明它抛出这些异常,并让它们传播出去。
受检异常的处理,会给程序员带来非常高的额外负担。
把受检的异常改为未受检的异常的一种方法是,把抛出异常的方法分成两个方法,其中第一个方法返回一个boolean,表明是否应该抛出异常。
[]第60条 优先使用标准的异常
重用现有的异常有很多方法,好处是使API更加易于学习和使用,对于用到这些API的程序而言,它们的可读性会更好,因为它们不会出现很多程序员不熟悉的异常。
最常用标准异常:
IllegalArgumentException 非null的参数值不正确
IllegalStateException 对于方法调用而言,对象状态不合适
NullPointerException 在禁止使用null的情况下参数值为null
IndexOfBoundsException 下标参数值越界
ConcurrentModificationException 在禁止并发修改的情况下,检测到对象的并发修改
UnsupportedOperationException 对象不支持用户请求的方法
[]第61条 抛出与抽象相对应的异常
高层的API实现应该捕获底层的异常,同时抛出可以按照高层抽象进行解释的异常。这种做法被称为异常转译(exception translation)。
例如取自于AbstractSequentialList类,该类是List接口的一个骨架实现。
/**
*Returns the element at the specified position in this list.
*@throws IndexOutOfBoundsException if the index is out of range
*({@code index < 0 || index >=size()}).
*/
public E get(int index) {
ListIterator <E> i = listIterator(index);
try {
return i.next();
} catch (NoSuchElementException e) {
throw new IndexOutOfBoundsException("Index: " + index);
}
}
如果不能阻止或者处理来自更底层的异常,一般的做法是使用异常转译,除非底层方法碰巧可以保证它抛出的所有异常对高层也合适才可以将异常从底层传播到高层。
[x]第62条 每个方法抛出的异常都要有文档
使用Javadoc的@throws标签记录下一个方法可能抛出的每个未受检异常,但是不要使用throws关键字将未受检的异常包含在方法的声明中。
由Javadoc的@throws标签所产生的文档就会提供明显的提示信息,以帮助程序员区分受检的异常和未受检的异常。
[x]第63条 在细节消息中包含能捕获失败的信息
为了确保在异常的细节信息中包含足够的能捕获失败的信息,一种办法是在异常的构造器而不是字符串细节消息中引入这些信息。
[x]第64条 努力使失败保持原子性
[x]第65条 不要忽略异常
空的catch块会使异常达不到应有的目的,即强迫你处理异常的情况。至少需要为catch块增加一条说明,解释忽略异常的原因。
第10章 并发
[x]第66条 同步访问共享的可变数据
1、多线程环境下,如果读和写操作没有都被同步,同步就不会起作用。
2、volatile修饰符不执行互斥访问,但它可以保证任何一个线程在读取该域的时候都将看到最近刚刚被写入的值。适用于boolean volatile变量,能保证原子性和同步性。
3、将对象引用从一个线程传递到其他的线程被称作安全发布(safe publication).安全发布对象引用有许多种方法:
可以将它保存在静态域中,作为类初始化的一部分;
可以将它保存在volatile域、final域或者通过正常锁定访问的域中;
或者可以将它放到并发的集合中。
4、当多个线程共享可变数据的时候,每个读或者写数据的线程都必须执行同步。