這一章介紹方法設計的幾個方面:如何對待參數和返回值,如何設計方法簽名,如何註釋方法
Item38: 檢查參數的合法性
大部分使用的方法參數都有一定的限制,如不爲null,size>0等
通用的原則就是預防大於整改,提前發現錯誤可以更快的規避問題,而不是在程序運行中發生
對於公共方法,使用Javadoc@塊標記,來記錄在違反參數值限制時拋出的異常(Item62)。
/**
* Returns a BigInteger whose value is (this mod m). This method
* differs from the remainder method in that it always returns a
* non-negative BigInteger.
*
* @param m the modulus, which must be positive
* @return this mod m
* @throws ArithmeticException if m is less than or equal to 0
*/
public BigInteger mod(BigInteger m) {
if (m.signum() <= 0)
throw new ArithmeticException("Modulus <= 0: " + m);
... // Do the computation
}
對於私有的方法則使用斷言
// Private helper function for a recursive sort
private static void sort(long a[], int offset, int length) {
assert a != null;
assert offset >= 0 && offset <= a.length;
assert length >= 0 && length <= a.length - offset;
... // Do the computation
}
assert在實際項目中使用的很少,更多的還是使用的if判斷
每次寫一個方法或者構造函數的時候,在方法的開頭考慮參數的合法性是必不可少的
Item39: 必要的時候使用拷貝對象
Java是一門安全的語言,即使在Java語言中,也要假設用戶正想方設法的破壞你的程序.除了少部分人想破壞系統的安全性,大部分問題都是編程人員可以控制的
雖然沒有對象的幫助,另一個類不太可能改變對象的內部狀態,但偶爾也有疏忽的地方
//一個不可變的週期類
// Broken "immutable" time period class
public final class Period {
private final Date start;
private final Date end;
/**
* @param start the beginning of the period
* @param end the end of the period; must not precede start
* @throws IllegalArgumentException if start is after end
* @throws NullPointerException if start or end is null
*/
public Period(Date start, Date end) {
if (start.compareTo(end) > 0)
throw new IllegalArgumentException(
start + " after " + end);
this.start = start;
this.end = end;
}
public Date start() {
return start;
}
public Date end() {
return end;
}
... // Remainder omitted
}
其中Date對象是可變的
// bad
Date start = new Date();
Date end = new Date();
Period p = new Period(start, end);
end.setYear(78); // 修改了p的內部狀態
爲了保護對象的狀態,我們需要在構造函數中對可變參數執行拷貝防禦
// Repaired constructor - makes defensive copies of parameters
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);
}
// Repaired accessors - make defensive copies of internal fields
public Date start() {
return new Date(start.getTime());
}
public Date end() {
return new Date(end.getTime());
}
這裏沒有使用clone方法,因爲Date有其它不可信任的子類
經驗上講,在內部儘量不要使用可變的類,如Date,可用long型替代Date.getTime()
總結:如果一個類調用了或返回可變的對象,則需要用拷貝對象防禦,如果很信任調用者不會修改類的內部狀態,則需要有一份警告文檔提示調用者不能修改類的狀態