import java.util.*;
public class MethodDesign {
private Date startday;
public MethodDesign() { }
public MethodDesign(Date startday) {
if(startday == null)
throw new IllegalArgumentException("startday can not be null");
//保护性拷贝
this.startday = new Date(startday.getTime());
}
public Date getStartday() {
//保护性拷贝
return new Date(this.startday.getTime());
}
@SuppressWarnings("unused") private void sort(long[] arr, int offset, int length) {
assert arr != null;
assert offset >= 0 && offset <= arr.length;
assert length >= 0 && length <= arr.length - offset;
}
//overload重载方法的选择是在编译时决定的
public String classify(Set<?> s) {
return "Set";
}
public String classify(List<?> lst) {
return "List";
}
public String classify(Collection<?> c) {
return "Unknown Collection";
}
public static void main(String[] args) {
MethodDesign md = new MethodDesign();
Collection<?>[] collections = {
new HashSet<String>(),
new ArrayList<Integer>(),
new HashMap<String, String>().values()
};
for (Collection<?> c : collections)
System.out.println(md.classify(c));
Wine[] wines = {
new Wine(), new SparklingWine(), new Champagne()
};
for (Wine wine : wines)
System.out.println(wine.name());
List<Integer> list = new ArrayList<Integer>();
for (int i = -3; i < 3; i++) {
list.add(i);
}
for (int i = 0; i < 3; i++) {
//删除Integer元素
list.remove(Integer.valueOf(i));
//按下标删除
//list.remove(i);
}
System.out.println(list);
}
}
//override覆盖方法的选择是运行时决定的
class Wine {
String name() { return "wine"; }
}
class SparklingWine extends Wine {
@Override String name() { return "sparkling wine"; }
}
class Champagne extends SparklingWine {
@Override String name() { return "champagne"; }
}
输出为:
Unknown Collection
Unknown Collection
Unknown Collection
wine
sparkling wine
champagne
[-3, -2, -1]
* 检查方法参数的有效性:
绝大多数方法和构造器对传递给他们的参数值都会有些限制。
应该在文档中清楚地指明这些限制,并且在方法的开头检查参数,以强加这些限制。
如果传递了无效的参数给方法,那么它很快就会失败,并清晰地抛出适当的异常。
通常为:IllegalArgumentException、IndexOutOfBoundsException 或者 NullPointerException。
对于非public的方法,通常应该使用断言(assertion)来检查它们的参数。
断言是在声称被断言的条件将会为真,断言如果失败将会抛出AssertionError。
不同于一般的有效性检查,如果它们没有起到作用,本质上也不会有成本开销。
*************************************************************************
* 必要时进行保护性拷贝:
为了避免实例的内部信息受到攻击,对于构造器的每个可变参数进行保护性拷贝是必要的。
保护性拷贝是在检查参数的有效性之前进行的,并且有效性检查是针对拷贝之后的对象,而不是针对
传递进来的原始对象。
对于参数类型可以被不信任方子类化的参数,不要使用clone方法进行保护性拷贝。
public访问方法提供了对可变内部成员的访问,为了防御攻击,需要使它返回可变内部域的保护性拷贝。
*************************************************************************
* 谨慎设计方法的签名:
谨慎地选择方法的名称:方法的名称应该始终遵循标准的命名习惯。
首要目标是选择易于理解的,并且与包中其他名称风格一致的名称。
其次应该选择与大众认可的名称相一致的名称。
不要过于追求提供便利的方法:每个方法都应该尽其所能。方法太多会使类难以学习、使用、测试
和维护。
避免过长的参数列表:目标是四个参数或者更少。相同类型的长参数序列格外有害。
有三种方法缩短过长的参数:
第一种是把方法分解成多个方法,每个方法只需要这些参数的一个子集。
第二种方法是创建辅助类,用来保存参数的分组。这些辅助类一般为静态成员类。
第三种方法结合前两种,从对象构建到方法调用都采用Builder模式。
对于参数类型要优先使用接口而不是类。
对于boolean参数,要优先使用两个元素的枚举类型。
*************************************************************************
* 慎用重载:要调用哪个重载方法,是在编译时做出决定的。
java对于重载(overload)方法的选择是静态的(编译时决定)。
而对覆盖(override)方法的选择是动态的(运行时决定),选择的依据是被调用方法所在对象的运行时
类型。
对于API的用户来说,如果一组给定的参数,根本不知道哪个重载方法会被调用;那么使用这样的API
可能导致错误。
安全而保守的策略:永远不要导出两个具有相同参数数目的重载方法。如果方法使用可变参数
(varargs),保守的策略是根本不要重载它。
java1.5引入泛型和自动装箱后,破坏List的重载方法remove(int)和remove(E)。要删除Integer类型的数据,
参数必须为remove(Integer.valueOf(i))。remove(int)会按下标删除。
*************************************************************************
* 慎用可变参数(varargs):
java1.5增加可变参数的方法。可变参数方法接受零个或多个指定类型的参数。
可变参数的机制通过预先创建一个数组,数组的大小为在调用位置所传递的参数数量,然后将参数值传到
给数组中,最后将数组传给方法。
可变参数方法的每次调用都会导致一次数组分配和初始化。
*************************************************************************
* 返回零长度的数组或者集合而不是null:
对于不返回任何元素的调用,每次都返回同一个零长度数组是有可能的,因为零长度数组是不可变的,
而不可变对象有可能被自由地共享。