以下为我在《Effective Java》中留下的读书笔记,对一些重要的知识点提取出了摘要.
38、检查参数的有效性
对于共有的方法,要用Javadoc的@throws标签在文档中说明违反参数值限制时会抛出的异常,在方法的开头部分进行检查.
对于未被导出的方法,通常使用断言来检查参数.
检查一些保存起来供以后使用的参数尤为重要.
补充:断言
39、必要时进行保护性拷贝
保护性拷贝步骤:
1、对于构造器的每个可变参数进行保护性拷贝
如果传入参数为非final类型,则不用clone方法来进行保护性拷贝.因为不能保证clone方法一定返回所需类型.
2、修改访问方法,使它返回可变内部域的保护性拷贝
在进行保护性拷贝的时候允许使用clone方法,因为我们知道Period内部的Date对象的类是java.util.Date
package defensiveCopy;
import java.util.Date;
public class Period {
private final Date start;
private final Date end;
public Period(Date start, Date end){
this.start = new Date(start.getTime());
this.end = new Date(end.getTime());
if(this.start.compareTo(this.end) > 0)
throw new IllegalArgumentException(start + " after " + end );
}
public Date getStart() {
return (Date)start.clone();
}
public Date getEnd() {
return (Date)end.clone();
}
}
保护性拷贝是在检查参数的有效性之前进行的,并且有效性检查是针对拷贝之后的对象,而不是针对原始的对象. 这样做是避免期间从另一个线程改变类的参数.
每当编写方法或者构造器时,如果它要允许客户提供的对象进入到内部数据结构中,则有必要考虑一下,客户提供的对象是否可能是可变的.如果是,就要考虑你的类是否能够容忍对象进入数据结构之后发生变化.如果否,就要进行保护性拷贝.
Collections.unmodifiableList(); //返回不可变视图
有经验的程序员通常使用Date.getTime()返回的long基本类型作为内部的时间表示法,而不是使用Date对象引用.因为Date是可变的.
补充:缓冲区溢出、数组越界、非法指针以及其他的内存破坏错误、不可变视图
40、谨慎设计方法签名
谨慎地选择方法的名称:方法的名称应该始终遵循标准的命名习惯.
不要过于追求提供便利的方法
避免过长的参数列表.目标是4个参数,或者更少.相同类型的长参数序列格外有害.
对于参数类型要优先使用接口而不是类.
对于boolean参数,要优先使用两个元素的枚举类型.
41、慎用重载
调用哪个重载方法时在编译时做出决定的,对于被覆盖的方法的选择时在运行时做出决定的.
writeBoolean\writeInt\writeLong 命名模式
自动装箱出现后,遇到的一些麻烦. set.remove(i)调用选择重载方法remove(E),list.remove(i)调用选择重载方法remove(int i). 出现混乱
42、慎用可变参数
不必改造具有final数组参数的每个方法;只当确实是在数量不定的值上执行调用时才使用可变参数.
在重视性能的情况下,假设确定对于某个方法95%的调用会用3个或者更少的参数,可以如此
public void foo(){}
public void foo(int a1){}
public void foo(int a1, int a2){}
public void foo(int a1, int a2, int a3){}
public void foo(int a1, int a2, int a3, int... rest){}
输出基本类型的数组 Arrays.toString(array);
输出引用类型的数组 Arrays.asList(array);
补充:final修饰的参数
43、返回零长度的数组或者集合,而不是null
把一些元素从一个集合转存到一个类型化的数组: Collection.toArray(T[])
返回零长度的数组可以减少客户端中必要的额外处理null的代码.
与返回null比较: 第一,在这个级别上担心性能问题是不明智的,除非分析表明这个方法正是造成性能问题的真正源头.
第二,每次都返回同一个零长度的数组是有可能的,因为零长度数组是不可变的,而不可变队形有可能被自由地共享.
private staitc final Object[] EMPTY_ARRAY = new Object[0];
同样,集合值的方法也可以做成每当需要返回空集合时都返回一个不可变的空集合. Collections.emptySet\emptyList\emptyMap方法
44、为所有导出的API元素编写文档注释
{@literal} {@code}
为了正确地编写API文档,必须在每个被导出的类、接口、构造器、方法和域声明之前增加一个文档注释.
方法的文档注释应该简洁地描述出它和客户端之间的约定.
文档注释应该列举出这个方法的所有前提条件和后置条件.
文档注释也应该描述类或者方法的线程安全性.
文档注释也应该描述类的可序列化性.
同一个类或者接口中的两个成员或者构造器,不应该具有同样的概要描述
规范:@param @return标签 名词短语
@throws 如果,紧接着名词短语
构造器、方法概要描述 完整的动词短语,描述了该方法所执行的动作
类、接口和域 概要描述 名词短语
补充:未受检的异常