摘要:檢查參數(shù)的有效性每當(dāng)編寫方法或者構(gòu)造器時(shí),應(yīng)該考慮它的參數(shù)有哪些限制。為了保護(hù)實(shí)例的內(nèi)部信息避免受到這種攻擊,對(duì)于構(gòu)造器的每個(gè)可變參數(shù)進(jìn)行保護(hù)性拷貝是必要的,并且使用備份對(duì)象作為實(shí)例的組件,而不是使用原始的對(duì)象。
檢查參數(shù)的有效性
每當(dāng)編寫方法或者構(gòu)造器時(shí),應(yīng)該考慮它的參數(shù)有哪些限制。應(yīng)該把這些限制寫到文檔中,并且在這個(gè)方法體開(kāi)頭處,通過(guò)顯示的檢查來(lái)實(shí)施這些限制。養(yǎng)成這樣的習(xí)慣非常重要。
必要時(shí)進(jìn)行保護(hù)性拷貝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 IllegalArgumentException(start + " after " + end); } this.start = start; this.end = end; } public Date start() { return start; } public Date end() { return end; } }
因?yàn)镈ate類本身時(shí)可變的,所以,
Date start = new Date(); Date end = new Date(); Period p = new Period(start, end); end.setYear(78); // 這個(gè)操作把實(shí)例的內(nèi)部信息修改了。
為了保護(hù)Period實(shí)例的內(nèi)部信息避免受到這種攻擊,對(duì)于構(gòu)造器的每個(gè)可變參數(shù)進(jìn)行保護(hù)性拷貝是必要的,并且使用備份對(duì)象作為Period實(shí)例的組件,而不是使用原始的對(duì)象。
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); } }
注意,保護(hù)性拷貝是在檢查參數(shù)的有效性之前進(jìn)行的,并且有效性檢查是針對(duì)拷貝之后的對(duì)象,而不是原始的對(duì)象。
但是改變Period實(shí)例仍然是有可能的,因?yàn)樗脑L問(wèn)方法提供了對(duì)其內(nèi)部成員的訪問(wèn)能力。
Date start = new Date(); Date end = new Date(); Period p = new Period(start, end); p.end().setYear(78); // 這個(gè)操作把實(shí)例的內(nèi)部信息修改了。
為了防御這第二種攻擊,只需改這兩個(gè)訪問(wèn)方法,使它返回可變內(nèi)部域的保護(hù)性拷貝即可。
public Date start() { return new Date(start.getTime()); } public Date end() { return new Date(end.getTime()); }
在內(nèi)部組件被返回給客戶端之前,對(duì)它們進(jìn)行保護(hù)性拷貝也是同樣的道理。
只要有可能,都應(yīng)該使用不可變的對(duì)象作為對(duì)象內(nèi)部的組件,這樣就不必再為保護(hù)性拷貝操心。
如果類具有從客戶端得到或者返回到客戶端的可變組件,類就必須保護(hù)性地拷貝這些組件。
如果拷貝的成本受到限制,并且類信任它的客戶端會(huì)恰當(dāng)?shù)匦薷慕M件,就可以在文檔中指明客戶端的職責(zé)使不得修改受到影響的組件,以此來(lái)代替保護(hù)性拷貝。
首要目標(biāo)是選擇易于理解的。
第二目標(biāo)是選擇與大眾認(rèn)可的名稱相一致的名稱。
不要過(guò)于追求提供便利的方法每個(gè)方法都應(yīng)該盡其所能。方法太多會(huì)使類難以學(xué)習(xí)、使用、文檔化、測(cè)試和維護(hù)。
對(duì)于類和接口所支持的每個(gè)動(dòng)作,都提供一個(gè)功能齊全的方法。
避免過(guò)長(zhǎng)的參數(shù)列表目標(biāo)是四個(gè)參數(shù),或者更少。
有三種方法可以縮短過(guò)長(zhǎng)的參數(shù)列表。
第一種是把方法分解成多個(gè)方法,每個(gè)方法只需要這些參數(shù)的一個(gè)子集。
第二種是創(chuàng)建輔助類,用來(lái)保存參數(shù)的分組。
第三種是從對(duì)象構(gòu)建到方法調(diào)用都采用Builder模式。
如果使用的是類而不是接口,則限制了客戶端只能傳入特定的實(shí)現(xiàn),如果碰巧輸入的數(shù)據(jù)是以其他的形式存在,就會(huì)導(dǎo)致不必要的、可能非常昂貴的拷貝操作。
對(duì)于boolean參數(shù),要優(yōu)先使用兩個(gè)元素的枚舉類型它是代碼更易于閱讀和編寫,也使以后更易于添加更多的選項(xiàng)。
慎用重載下面的程序試圖根據(jù)一個(gè)集合是Set、List,還是其他的集合類型來(lái)分類。
public class CollectionClassifier { public static String classify(Set> set) { return "Set"; } public static String classify(List> list) { return "List"; } public static String classify(Collection> collection) { return "Unknow Collection"; } public static void main(String[] args) { Collection>[] collections = { new HashSet(), new ArrayList (), new HashMap ().values() }; for(Collection> collection : collections) { System.out.println(classify(collection)); } } }
可能期望會(huì)打印“Set”,接著是“List”,以及“Unknow Collection”,實(shí)際上不是這樣。而是“Unknow Collection”打印三次。
因?yàn)閏lassify方法被重載(overload)了,而要調(diào)用哪個(gè)重載方法是在編譯時(shí)做出決定的。對(duì)于for循環(huán)中的三次迭代,參數(shù)的編譯時(shí)類型都是相同的,Collection>。每次迭代的運(yùn)行時(shí)類型都是不同的,但這并不影響對(duì)重載方法的選擇。因?yàn)樵搮?shù)的編譯時(shí)類型為Collection>,所以,唯一合適的重載方法是第三個(gè),classify(Collection>),在循環(huán)的每次迭代中,都會(huì)調(diào)用這個(gè)重載方法。
對(duì)于重載方法(overload method)的選擇是靜態(tài)的,而對(duì)于被覆蓋的方法(overriden method)的選擇則是動(dòng)態(tài)的。
選擇被覆蓋的方法的正確版本是在運(yùn)行時(shí)的,選擇的依據(jù)時(shí)被調(diào)用方法所在對(duì)象的運(yùn)行時(shí)類型。
再看看下面這個(gè)程序。
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()); } } }
這個(gè)程序打印“wine”、“sparkling wine”和“champagne”。
可以對(duì)第一個(gè)程序修改一下,用單個(gè)方法替換三個(gè)重載的classify方法,如下。
public static String classify(Collection> collection) { return collection instanceof Set ? "Set" : collection instanceof List ? "List" : "Unknow Collection"; }
因?yàn)楦采w機(jī)制時(shí)規(guī)范,而重載機(jī)制是例外。
到底怎樣才算是胡亂使用重載機(jī)制呢?這個(gè)問(wèn)題仍有爭(zhēng)議。安全而保守的策略是,永遠(yuǎn)不要導(dǎo)出兩個(gè)具有相同參數(shù)數(shù)目的重載方法。
慎用可變參數(shù)Java 1.5發(fā)行版本中增加了可變參數(shù)方法,一般稱作variable arity method(可匹配不同長(zhǎng)度的變量方法)[JLS,8.4.1]。
可變參數(shù)方法接受零個(gè)或者多個(gè)指定類型的參數(shù)。可變參數(shù)機(jī)制通過(guò)先創(chuàng)建一個(gè)數(shù)組,數(shù)組的大小為在調(diào)用位置所傳遞的參數(shù)數(shù)量,然后將參數(shù)值傳到數(shù)組中,最后將數(shù)組傳遞給方法。
static int sum(int... args) { int sum = 0; for (int arg : args) { sum += arg; } return sum; }
當(dāng)真正需要讓一個(gè)方法帶有不定數(shù)量的參數(shù)時(shí),可變參數(shù)就非常有效。可變參數(shù)是為printf而設(shè)計(jì)的。printf和反射機(jī)制都從可變參數(shù)中極大地受益。
在重視性能地情況下,需要小心。可變參數(shù)方法的每次調(diào)用都會(huì)導(dǎo)致進(jìn)行一次數(shù)組分配和初始化。
需要可變參數(shù)的靈活性,但又無(wú)法承受性能成本,可以這么做。
public void foo() {} public void foo(int a1) {} public void foo(int a1, int a2) {} public void foo(int a1, int a2, int a3) {} public void foo(int a1, int a2, int a3, int... rest) {}
在定義參數(shù)數(shù)目不定的方法時(shí),可變參數(shù)方法是一種很方便的方式,但是不應(yīng)該被過(guò)度濫用。如果使用不當(dāng),會(huì)產(chǎn)生混亂的結(jié)果。
返回零長(zhǎng)度的數(shù)組或者集合,而不是null對(duì)于一個(gè)返回null而不是零長(zhǎng)度數(shù)組或者集合的方法,幾乎每次調(diào)用該方法都需要曲折的處理方式。這樣很容易出錯(cuò),因?yàn)榫帉懣蛻舳顺绦虻某绦騿T可能會(huì)忘記寫專門的代碼來(lái)處理null返回值。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/70055.html
摘要:只針對(duì)異常的情況才使用異常下面展示兩種遍歷數(shù)組元素的實(shí)現(xiàn)方式。第一種方式在訪問(wèn)數(shù)組邊界之外的第一個(gè)數(shù)組元素時(shí),用拋出捕獲忽略異常的手段來(lái)達(dá)到終止無(wú)限循環(huán)的目的。這種做法被稱為異常轉(zhuǎn)譯。 只針對(duì)異常的情況才使用異常 下面展示兩種遍歷數(shù)組元素的實(shí)現(xiàn)方式。 try { int i = 0; while(true) range[i++].climb(); } c...
摘要:學(xué)習(xí)編程的本最佳書籍這些書涵蓋了各個(gè)領(lǐng)域,包括核心基礎(chǔ)知識(shí),集合框架,多線程和并發(fā),內(nèi)部和性能調(diào)優(yōu),設(shè)計(jì)模式等。擅長(zhǎng)解釋錯(cuò)誤及錯(cuò)誤的原因以及如何解決簡(jiǎn)而言之,這是學(xué)習(xí)中并發(fā)和多線程的最佳書籍之一。 showImg(https://segmentfault.com/img/remote/1460000018913016); 來(lái)源 | 愿碼(ChainDesk.CN)內(nèi)容編輯 愿碼Slo...
摘要:來(lái)源前條來(lái)源一書英文版已經(jīng)出版,這本書的第二版想必很多人都讀過(guò),號(hào)稱四大名著之一,不過(guò)第二版年出版,到現(xiàn)在已經(jīng)將近年的時(shí)間,但隨著,,,甚至的發(fā)布,語(yǔ)言發(fā)生了深刻的變化。譯者在這里第一時(shí)間翻譯成中文版。供大家學(xué)習(xí)分享之用。 來(lái)源:sjsdfg/effective-java-3rd-chinese前 51 條來(lái)源:Effective Java, Third Edition 《Effec...
摘要:這本書是我第一次買的,從買來(lái)至今整本書還沒(méi)有看完,只看了一半,原因是個(gè)人比較懶,而且玩的心比較大,經(jīng)過(guò)這么多年的沉淀,終于可以偷點(diǎn)時(shí)間寫下對(duì)于這本書的觀后感了整本書給我的感覺(jué)不像是一個(gè)技術(shù)書,更多的是講解一些實(shí)用技巧,而對(duì)于我這個(gè)職場(chǎng)菜鳥來(lái) effective Java 這本書是我第一次買的, 從買來(lái)至今整本書還沒(méi)有看完, 只看了一半, 原因是個(gè)人比較懶,而且玩的心比較大,經(jīng)過(guò)這么多年...
摘要:三進(jìn)階階段這個(gè)階段主要是靠我們自己學(xué)習(xí)總結(jié),可以通過(guò)前輩們的博客或者自己研究源碼,這些非常有利于我們快速的成長(zhǎng)。讓自己保持永遠(yuǎn)學(xué)習(xí)的精神。五零基礎(chǔ)學(xué)習(xí)資料最后給大家準(zhǔn)備了一份不錯(cuò)的學(xué)習(xí)資源,里面有很多學(xué)習(xí)視頻和資料,后臺(tái)回復(fù)資源,即可獲取。 showImg(https://segmentfault.com/img/bVbauV8?w=1212&h=816); 前兩次給大家分享了關(guān)于 j...
閱讀 2124·2019-08-29 16:53
閱讀 2699·2019-08-29 16:07
閱讀 2042·2019-08-29 13:13
閱讀 3267·2019-08-26 13:57
閱讀 1331·2019-08-26 13:31
閱讀 2433·2019-08-26 13:22
閱讀 1221·2019-08-26 11:43
閱讀 2084·2019-08-23 17:14