第23条:检查参数的有效性
极大多数方法和构造函数都会对于传递给他们的参数值有某些限制。你应该在文档中清除的指明这些限制,并且在方法体的起始处对参数进行检查,以强迫施加这些限制。
如果一个无效的参数值被传递给一个方法,而这个方法在执行之前首先对参数进行检查,则它很快会失败,并且清除的以一个适当的异常指明错误原因。如果这个方法没有进行参数检查,有可能会发生几种情形。处理过程中失败,并且尝试一个令人迷惑的异常。更差的是,反悔正常,但计算结果是错误的。最糟糕的是,正常返回,但使得对象处于一种被破坏的状态,在某个不确定的时候引发出来错误。
非公有的方法通常应该使用断言来检查他们的参数,而不使用正常的检查语句。如果你使用的开发支持断言,那么你应该使用断言,否则话,你应该使用一种临时的断言机制。
在一个方法执行它的计算任务之前,应该检查它的参数,这条规则也有例外,一个很重要的例外是,在某些情况下,有效性检查工作非常昂贵,或者更本是不且实际的,并且在计算过程中有效性检查工作也被隐含着完成了。
第24条:需要时使用保护性拷贝
假设类的客户尽一切手段来破坏这个类的约束条件,在这样的前提下,你必须保护性的设计程序。编写出一些面对客户的不良行为时仍能保持健壮的类是非常值得投入时间去做的。
public final class Period{
private final Date start;
private final Date end;
public Period(Date start,Date end){
if(start.compareTo(end)>0){
throw new IlleagalArgumentException(start+"after"+end);
this.start=start
this.end=end
}
}
}
但是
Date start=new Date();
Date end=Date();
Period p=new Period(start,end);
end.setYear(78); end被改变了。
对于构造函数的每个可变参数进行保护性拷贝是必要的
public final 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(start.compareTo(end)>0){
throw new IlleagalArgumentException(start+"after"+end);
}
}
}
保护性拷贝动作是在检查参数的有效性之前进行的,并且有效性检查是针对拷贝之后的对象,而不是原始的对象。参数类型可以被不可信方子类化的情形,请不要使用clone方法进行参数的保护性拷贝。
在把可变参数融入到一个对象内部之前进行保护性拷贝并不总是合适的,对于有些方法和构造函数,他们在被调用的时候,其参数所引用的对象有一个显示的交接过程,当客户调用这样的方法的时候,他承诺以后不在直接修改该对象。
如果一个方法或者构造函数期望接管一个由客户提供的可变对象,那么它必须在文档中明确指明这一点。
第25条:谨慎的设计方法的原型
1.谨慎选择方法名字:
2.不要过于追求提供便利的方法:方法太多会使一个类难以学习,使用,文档化,测试和维护。对于接口而言,这无疑是正确的,接口的方法太多会使接口实现者和接口用户的生活变的复杂化。
3。避免长长的参数列表。通常,三个参数应该被看做实践中的最大值,而且参数越少越好。类型相同的长参数列表尤其有害。用户不仅不能够记住参数的顺序,而且当他们弄错了时候,程序仍然可以运行,只不过不会按照编写程序人的意图。
有2项技术可以缩短太长的参数列表。
1.把一个方法分解成多个方法,每一个方法只要求这些参数的子集。
2.创建铺筑类,用来保存参数的聚集。(创建个类,参数列表作为属性,用于这个参数列表频繁使用)
3.对于参数类型,悠闲使用接口而不是类。
4.谨慎的使用函数对象 (匿名类方式)
第26条:谨慎的使用重载
public class CollectionClassifier{
public static String classify(Set s){
return "Set"
}
public static String classify(List l){
return "List"
}
public static String classify(Collection c){
return "Unknow Collection"
}
public static void main(String[] args){
Collection[] tests=new Collection[]{new HashSet(),new ArrayList(),new HashMap().values()};
for(int i=0;i<tests.length;i++){
System.out.println(classify(tests[i]));
}
}
}
结果是打印三个 unkonw collection
原因:调用哪个重载方法是在编译时刻作出的决定。每次迭代的运行参数类型不同,但这不影响对重载方法的选择。
对于重载方法的选择是静态的,而对于被改写的方法的选择是动态的。
被改写的方法,选择的依据是被调用方法所在对象的运行时类型。
修正重载带来的方案是,用一个方法来替换这三个重载的方法,并且在这个方法中一个显示的instanceof测试。
一个安全而保守的策略是永远不要导出两个具有相同参数数目的重载方法。
重载的函数参数不应该存在父子关系,否则容易引发问题。但是例外的是,他们执行相同的功能,返回相同的结果,则重载不会带来危害,确保这种行为的标准做法是,让更一般化的重载方法把调用转发给更特殊的重载方法。
public int comareTo(Objecto){
return compareTo((String)o);
}
第27条:返回零长度的数组而不是null
因为没有理由要求客户必须有额外的代码来处理null返回值。客户很容易忘记写专门的代码处理null返回值。
nul返回值比零长度数组更好,因为它避免了分配数组所需要的开销,这种观点是站不住脚的,原因有两条,第一,在这个层次上担心性能问题是不明智的,除非分析表明这个方法正式造成性能问题的真正源头。第二,对于不返回任何元素的调用,每次都返回同一个零长度的数组是有可能得,因为零长度是非可变的,而非可变对象有可能被自由的共享。
标准做法:private final static Cheese[] NULL_CHEESE_ARRAY=new Cheese[0];
public Chees[] getCheeses(){
return (Cheese[]) Collection.toArray(Object[]);
}
没有理由从一个取数组的方法中返回null而不是返回一个零长度数组。
第28条:为所有导出的api元素编写文档注释
为了正确的编写api文档,你必须在每一个被导出的类,接口,构造函数,方法和域声明之前增加一个文档注释。
每一个方法的文档注释应该简洁的描述出它和客户之间的约定。这个约定应该说明了这个方法做了什么,而不是说明它是如何完成这项工作的。文档注释应该列举出这个方法所有的前提条件和后置条件,除此之外每个方法也应该在文档中描述它的副作用。
极大多数方法和构造函数都会对于传递给他们的参数值有某些限制。你应该在文档中清除的指明这些限制,并且在方法体的起始处对参数进行检查,以强迫施加这些限制。
如果一个无效的参数值被传递给一个方法,而这个方法在执行之前首先对参数进行检查,则它很快会失败,并且清除的以一个适当的异常指明错误原因。如果这个方法没有进行参数检查,有可能会发生几种情形。处理过程中失败,并且尝试一个令人迷惑的异常。更差的是,反悔正常,但计算结果是错误的。最糟糕的是,正常返回,但使得对象处于一种被破坏的状态,在某个不确定的时候引发出来错误。
非公有的方法通常应该使用断言来检查他们的参数,而不使用正常的检查语句。如果你使用的开发支持断言,那么你应该使用断言,否则话,你应该使用一种临时的断言机制。
在一个方法执行它的计算任务之前,应该检查它的参数,这条规则也有例外,一个很重要的例外是,在某些情况下,有效性检查工作非常昂贵,或者更本是不且实际的,并且在计算过程中有效性检查工作也被隐含着完成了。
第24条:需要时使用保护性拷贝
假设类的客户尽一切手段来破坏这个类的约束条件,在这样的前提下,你必须保护性的设计程序。编写出一些面对客户的不良行为时仍能保持健壮的类是非常值得投入时间去做的。
public final class Period{
private final Date start;
private final Date end;
public Period(Date start,Date end){
if(start.compareTo(end)>0){
throw new IlleagalArgumentException(start+"after"+end);
this.start=start
this.end=end
}
}
}
但是
Date start=new Date();
Date end=Date();
Period p=new Period(start,end);
end.setYear(78); end被改变了。
对于构造函数的每个可变参数进行保护性拷贝是必要的
public final 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(start.compareTo(end)>0){
throw new IlleagalArgumentException(start+"after"+end);
}
}
}
保护性拷贝动作是在检查参数的有效性之前进行的,并且有效性检查是针对拷贝之后的对象,而不是原始的对象。参数类型可以被不可信方子类化的情形,请不要使用clone方法进行参数的保护性拷贝。
在把可变参数融入到一个对象内部之前进行保护性拷贝并不总是合适的,对于有些方法和构造函数,他们在被调用的时候,其参数所引用的对象有一个显示的交接过程,当客户调用这样的方法的时候,他承诺以后不在直接修改该对象。
如果一个方法或者构造函数期望接管一个由客户提供的可变对象,那么它必须在文档中明确指明这一点。
第25条:谨慎的设计方法的原型
1.谨慎选择方法名字:
2.不要过于追求提供便利的方法:方法太多会使一个类难以学习,使用,文档化,测试和维护。对于接口而言,这无疑是正确的,接口的方法太多会使接口实现者和接口用户的生活变的复杂化。
3。避免长长的参数列表。通常,三个参数应该被看做实践中的最大值,而且参数越少越好。类型相同的长参数列表尤其有害。用户不仅不能够记住参数的顺序,而且当他们弄错了时候,程序仍然可以运行,只不过不会按照编写程序人的意图。
有2项技术可以缩短太长的参数列表。
1.把一个方法分解成多个方法,每一个方法只要求这些参数的子集。
2.创建铺筑类,用来保存参数的聚集。(创建个类,参数列表作为属性,用于这个参数列表频繁使用)
3.对于参数类型,悠闲使用接口而不是类。
4.谨慎的使用函数对象 (匿名类方式)
第26条:谨慎的使用重载
public class CollectionClassifier{
public static String classify(Set s){
return "Set"
}
public static String classify(List l){
return "List"
}
public static String classify(Collection c){
return "Unknow Collection"
}
public static void main(String[] args){
Collection[] tests=new Collection[]{new HashSet(),new ArrayList(),new HashMap().values()};
for(int i=0;i<tests.length;i++){
System.out.println(classify(tests[i]));
}
}
}
结果是打印三个 unkonw collection
原因:调用哪个重载方法是在编译时刻作出的决定。每次迭代的运行参数类型不同,但这不影响对重载方法的选择。
对于重载方法的选择是静态的,而对于被改写的方法的选择是动态的。
被改写的方法,选择的依据是被调用方法所在对象的运行时类型。
修正重载带来的方案是,用一个方法来替换这三个重载的方法,并且在这个方法中一个显示的instanceof测试。
一个安全而保守的策略是永远不要导出两个具有相同参数数目的重载方法。
重载的函数参数不应该存在父子关系,否则容易引发问题。但是例外的是,他们执行相同的功能,返回相同的结果,则重载不会带来危害,确保这种行为的标准做法是,让更一般化的重载方法把调用转发给更特殊的重载方法。
public int comareTo(Objecto){
return compareTo((String)o);
}
第27条:返回零长度的数组而不是null
因为没有理由要求客户必须有额外的代码来处理null返回值。客户很容易忘记写专门的代码处理null返回值。
nul返回值比零长度数组更好,因为它避免了分配数组所需要的开销,这种观点是站不住脚的,原因有两条,第一,在这个层次上担心性能问题是不明智的,除非分析表明这个方法正式造成性能问题的真正源头。第二,对于不返回任何元素的调用,每次都返回同一个零长度的数组是有可能得,因为零长度是非可变的,而非可变对象有可能被自由的共享。
标准做法:private final static Cheese[] NULL_CHEESE_ARRAY=new Cheese[0];
public Chees[] getCheeses(){
return (Cheese[]) Collection.toArray(Object[]);
}
没有理由从一个取数组的方法中返回null而不是返回一个零长度数组。
第28条:为所有导出的api元素编写文档注释
为了正确的编写api文档,你必须在每一个被导出的类,接口,构造函数,方法和域声明之前增加一个文档注释。
每一个方法的文档注释应该简洁的描述出它和客户之间的约定。这个约定应该说明了这个方法做了什么,而不是说明它是如何完成这项工作的。文档注释应该列举出这个方法所有的前提条件和后置条件,除此之外每个方法也应该在文档中描述它的副作用。