在方法開頭校驗入參的合法性html
/** * @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; }
可使用assert,但必要時須要異常轉義,本身拋自定義異常java
// Repaired constructor - makes defensive copies of parameters - Page 185 // Stops first attack // 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 - Page 186 // Stops second attack // public Date start() { // return new Date(start.getTime()); // } // // public Date end() { // return new Date(end.getTime()); // } public class Attack { public static void main(String[] args) { // Attack the internals of a Period instance - Page 185 Date start = new Date(); Date end = new Date(); Period p = new Period(start, end); end.setYear(78); // Modifies internals of p! System.out.println(p); // Second attack on the internals of a Period instance - Page 186 start = new Date(); end = new Date(); p = new Period(start, end); p.end().setYear(78); // Modifies internals of p! System.out.println(p); } }
Date類型可變,於是返回一個新對象,而不使用原始對象數組
有效性檢查,針對拷貝後的進行檢查,能夠防止多線程時出現錯誤,拷貝後的纔是類本身的。這樣能夠防止從檢查參數開始到拷貝參數之間的時間段裏頭,有另一個線程改變類的參數。安全
public Collection<User> getUsers(){ return Collections.unmodifiableCollection(originalCollection); }
A、易於理解多線程
B、與大衆一致ide
方法太多則會使類難以學習、維護、測試post
目標是四個參數或更少,相同類型的參數容易出錯,特別是用戶混淆了其順序。性能
縮短參數個數的方法:學習
好比用Map,替代HashMap做爲方法參數類型測試
方便閱讀
public enum TemperatureScale{ FAHRENHEIT,CELSIUS}
public class CollectionClassifier { public static String classify(Set<?> s) { return "Set"; } public static String classify(List<?> lst) { return "List"; } public static String classify(Collection<?> c) { return "Unknown Collection"; } public static void main(String[] args) { Collection<?>[] collections = { new HashSet<String>(), new ArrayList<BigInteger>(), new HashMap<String, String>().values() }; for (Collection<?> c : collections) System.out.println(classify(c)); } } #輸出Unknown Collection
建議,永遠不要導出兩個具備相同參數數目的重載方法,對於可變參數的方法,不要去重載它。
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"; } } public class Overriding { public static void main(String[] args) { Wine[] wines = { new Wine(), new SparklingWine(), new Champagne() }; for (Wine wine : wines) System.out.println(wine.name()); } }
覆蓋可以按照預期輸出。
public class SetList { public static void main(String[] args) { Set<Integer> set = new TreeSet<Integer>(); List<Integer> list = new ArrayList<Integer>(); for (int i = -3; i < 3; i++) { set.add(i); list.add(i); } for (int i = 0; i < 3; i++) { set.remove(i); list.remove(i); } System.out.println(set + " " + list); } }
set.remove(i)調用選擇了重載方法remove(E),於是輸出符合預期;
list.remove(i)調用選擇了重載方法remove(int i),於是,其根據下標刪除,不符合預期,須要改成
list.remove(Integer.valueOf(i)); 或者 list.remove((Integer)i);
儘可能作到,當傳遞一樣的參數時,全部重載方法的行爲必須一致。
public class Varargs { // Simple use of varargs - Page 197 static int sum(int... args) { int sum = 0; for (int arg : args) sum += arg; return sum; } // The WRONG way to use varargs to pass one or more arguments! - Page 197 // static int min(int... args) { // if (args.length == 0) // throw new IllegalArgumentException("Too few arguments"); // int min = args[0]; // for (int i = 1; i < args.length; i++) // if (args[i] < min) // min = args[i]; // return min; // } // The right way to use varargs to pass one or more arguments - Page 198 static int min(int firstArg, int... remainingArgs) { int min = firstArg; for (int arg : remainingArgs) if (arg < min) min = arg; return min; } public static void main(String[] args) { System.out.println(sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); System.out.println(min(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); } }
#wrong way to println array System.out.println(Arrays.asList(myArray)); #right way System.out.println(Arrays.toString(myArray));
可變參數方法的每次調用都會致使進行一次數組的分配和初始化,若是沒法承受這一成本,就聲明該方法的幾個重載方法。
public Cheese[] getCheeses(){ if(cheesesInStack.size() == 0){ return null; } }
private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0]; public Cheese[] getCheese(){ return cheesesInStack.toArray(EMPTY_CHEESE_ARRAY); }
有人認爲返回null比零長度數組好,由於避免了分配數組所須要的開銷。這種觀點是站不住腳的。
A、在這個級別上擔憂性能分配問題是不明智的,除非分析代表這個方法正是形成性能問題的根源
B、對於不返回任何元素的調用,每次都返回同一個零長度數組是有可能的,由於零長度數組是不可變的,能夠自由共享。
實際上,Collection.toArray(T[])的規範保證了,若是輸入數組大到足夠容納這個集合,則返回這個輸入數組。所以這種作法永遠也不會分配零長度的數組。
一樣的,Collections.emptyList(),emptySet(),emptyMap()正是你所須要的。
A、前提條件precondition
通常由@throw隱含描述
B、後置條件postcondition
調用成功後哪些條件被知足
C、反作用SideEffect
系統狀態的變化,是否線程安全等
D、參數和返回值類型及說明
<pre> @code your code here <pre> this is {@literal |x+y| < |x| + |y|}
A、枚舉中要註明常量
B、註解要說明其成員及類型
A、成員註釋,採用單行的 /** here */
B、方法註釋,採用多行的
/**
*
*/