检查参数的有效性
方法在执行前,首先对参数进行检查,如果参数不合法,应该清楚地以一个适当的异常指明错误的原因。 对于非公有方法,调用者应该保证传入的参数是合理的,所以应该以assert断言参数的有效性。 但“检查参数的有效性”这条规则也有例外,比如检查工作十分昂贵,或者不切实际,并且在计算过程中有效性检查工作也被隐含完成时。需要时使用保护性拷贝
public class Period { private final Date start; private final Date end; public Period(Date start, Date end) { // if (start.compareTo(end) > 0) { //不是线程安全的 // throw new IllegalArgumentException(); // } // // this.start = start; // this.end = end; this.start = new Date(start.getTime()); // 保护性拷贝是在检查参数有效性之前进行。 this.end = new Date(end.getTime()); if (this.start.compareTo(this.end) > 0) { throw new IllegalArgumentException(); } } public Date start() { // return this.start; return (Date) this.start.clone(); } public Date end() { // return this.end; return (Date) this.end.clone(); }}
Period中被注释掉的代码,没有采用保护性的拷贝。这样的话,Period保存的是两个引用,start和end返回的也是引用,那么用户可以通过引用改变Period的状态,导致start>end非法情况出现。
注意到Period的构造方法中,保护性拷贝是在检查参数有效性之前进行,为什么呢?
这是为了线程安全考虑。假如线程A刚好执行完参数有效性检查,OK了,然后紧接的执行线程B,线程B改变了end的值,使start>end。轮到线程A执行了,有效性检查OK了,开始拷贝..此时start和end的值已经不合法了。
谨慎使用重载方法
java中,重载方法的选择是在编译时决定的,跟类的多态不同。所以有些时候代码行为可能出乎我们的意料。
比如如下代码输出的是三个"unknow",而不是分别输出"set""list""unknow"
public class Test { public static String classify(Set s) { return "set"; } public static String classify(List l) { return "list"; } public static String classify(Collection c) { return "unknow"; } public static void main(String[] args) { // TODO Auto-generated method stub Collection[] c = new Collection[] { new HashSet(), new ArrayList(), new HashMap().values() }; for (Collection collection : c) { System.out.println(classify(collection)); // 重载方法的选择是在编译时决定的,因为collection的类型是Collection,所以选择第三个重载方法。 } }}为了使如上代码按照我们所想象的那样工作,应该在Collection的重载方法中,用instanceof判断参数的具体类型再做具体的逻辑。
对于重载方法,最好还是别用了吧...
看看java的ObjectOutputStream类,它的write方法没有采用重载,而是采用了变形方法:writeBoolean(boolean) writeInt(int) writeLong(long)返回0长度的数组而不是null
经常可以看到如下代码:public Cheese[] getCheeses() { if (cheeseInstock.size() == 0) { return null; ...... }}这样,用户使用这个接口时,还要写额外代码去判断数组是否为null,这很容易出错,建议应该返回0大小的数组而不是null。