EffectiveJava第7章-方法

本章討論方法(構(gòu)造方法趁怔、普通方法)設(shè)計(jì)的幾個(gè)方面:

如何處理參數(shù)和返回值
如何設(shè)計(jì)方法簽名
如何為方法編寫文檔

第38條:檢查參數(shù)的有效性

絕大多數(shù)方法和構(gòu)造器對(duì)于傳遞給它們的參數(shù)值都會(huì)有某些限制。應(yīng)該在文檔中清楚的指明所有這些限制甲棍,并在方法體的開頭處檢查參數(shù)室谚,以強(qiáng)制施加這些限制傻昙。這樣就遵照了應(yīng)該在發(fā)生錯(cuò)誤之后盡快檢測(cè)出錯(cuò)誤這個(gè)原則锄开。

傳遞無(wú)效的參數(shù)值給方法古沥,這個(gè)方法在執(zhí)行之前先對(duì)參數(shù)進(jìn)行校驗(yàn)瘸右,那么就能很清晰的拋出異常;如果跳過(guò)了這個(gè)步驟岩齿,可能會(huì)導(dǎo)致令人費(fèi)解的異常太颤、返回錯(cuò)誤的結(jié)果、或者返回正確的結(jié)果并在某個(gè)狀態(tài)下崩潰盹沈。

對(duì)于公有的方法龄章,對(duì)參數(shù)進(jìn)行檢驗(yàn)并拋出異常,同時(shí)在文檔中使用@throws標(biāo)簽聲明如果傳入?yún)?shù)違反了相關(guān)規(guī)定就會(huì)拋出異常乞封,這樣的寫法是完善的做裙。讓調(diào)用API者很清晰的就能了解。

   /**
     * @throws NullPointerException if params is null
     */
    private void testOne(String s) {
        if (s == null) {
            throw new NullPointerException("params s can not be null");
        }
        System.out.print(s.toCharArray());
    }

對(duì)于非公有的方法肃晚、即未向外導(dǎo)出的锚贱,因?yàn)槭浅绦騿T自己調(diào)用并使用的,也確認(rèn)參數(shù)的合法性陷揪,可以使用斷言(assertion)來(lái)檢查它們的參數(shù)惋鸥。
這個(gè)看情況吧,因?yàn)閍ssert語(yǔ)句只有在編譯器開啟了-ea后才會(huì)起作用悍缠,在團(tuán)隊(duì)開發(fā)時(shí)并不是好事卦绣;所以還是要做參數(shù)校驗(yàn)并拋出異常。

對(duì)于有些參數(shù)飞蚓,方法本身沒有用到滤港,卻被保存起來(lái)以后使用,檢驗(yàn)這類參數(shù)的有效性尤為重要趴拧。
例如在構(gòu)造方法中對(duì)屬性進(jìn)行初始化時(shí)溅漾,一定要進(jìn)行參數(shù)檢驗(yàn),避免違法了這個(gè)類的約束條件著榴。

在有些情況下添履,有效性檢驗(yàn)非常昂貴,或者是不切實(shí)際的脑又,并且該檢驗(yàn)已隱含在計(jì)算過(guò)程中了暮胧,在這種情況下進(jìn)行檢驗(yàn)就沒必要了锐借。
例如Collection.sort()方法。

對(duì)參數(shù)的進(jìn)行任何限制并不是一件好事往衷。在設(shè)計(jì)方法時(shí)钞翔,要盡可能的通用、并符合實(shí)際需要席舍。
假如方法對(duì)于它能接受的所有參數(shù)值都能夠完成工作布轿,對(duì)參數(shù)的限制就應(yīng)該越少越好。

總之来颤,在寫方法或者構(gòu)造器時(shí)汰扭,應(yīng)該考慮它的參數(shù)有哪些限制。
應(yīng)該把這些限制寫到文檔中脚曾,并且在這個(gè)方法開頭處东且,通過(guò)顯示的檢查來(lái)實(shí)施這些限制。

第39條:必要時(shí)進(jìn)行保護(hù)性拷貝

Java是一門安全的語(yǔ)言本讥,不必考慮緩沖區(qū)溢出珊泳、數(shù)組越界、非法指針等其他的內(nèi)存破壞錯(cuò)誤拷沸,而這些在C色查、C++中確實(shí)要考慮的; 假設(shè)類的客戶端會(huì)盡其所能的來(lái)破壞這個(gè)類的約束條件撞芍,因?yàn)槟惚仨毐Wo(hù)性的設(shè)計(jì)程序秧了。因?yàn)榧幢闶穷愋桶踩恼Z(yǔ)言,也無(wú)法與其他類隔離開來(lái)序无。

// 該類的約束為: start < end
public class Period {
    private final Date mStart;
    private final Date mEnd;

    public Period(Date start, Date end) {
        // 對(duì)構(gòu)造器的每個(gè)可變參數(shù)進(jìn)行保護(hù)性拷貝
        this.mStart = new Date(start.getTime());
        this.mEnd = new Date(end.getTime());

        //檢查參數(shù)有效性
        if (mStart.compareTo(mEnd) > 0) {
            throw new IllegalArgumentException("start must be ahead of end");
        }
    }

    public Date getmStart() {
        return new Date(mStart.getTime()); //返回可變內(nèi)部域的保護(hù)性拷貝验毡,可以使用clone方法,因?yàn)閙Start就是Date類型的帝嗡,這是我們自己知道的
    }

    public Date getmEnd() {
        return new Date(mEnd.getTime());
    }
}

對(duì)于構(gòu)造器的每個(gè)可變參數(shù)進(jìn)行保護(hù)性拷貝是必要的

getter方法必須返回可變內(nèi)部域的保護(hù)性拷貝

保護(hù)性拷貝是在檢查參數(shù)有效性之前進(jìn)行的晶通,并且有效性檢查是針對(duì)拷貝后的對(duì)象進(jìn)行的。
這樣做可以在危險(xiǎn)階段期間另一個(gè)線程來(lái)改變類的參數(shù)哟玷。危險(xiǎn)階段:從檢查參數(shù)開始狮辽,直到拷貝參數(shù)之間的時(shí)間段。

對(duì)于參數(shù)類型可以被不可信任的子類化的參數(shù)巢寡,不要使用該對(duì)象的clone方法進(jìn)行保護(hù)性拷貝喉脖。
不要使用Date的clone方法來(lái)進(jìn)行保護(hù)性拷貝,因?yàn)閰?shù)Date是非final的抑月,不能保證clone方法一定返回Date對(duì)象:可能是出于惡意目的而設(shè)計(jì)的不可信任的子類树叽。

參數(shù)的保護(hù)性拷貝并不僅僅針對(duì)不可變類。

當(dāng)編寫方法或構(gòu)造方法時(shí)谦絮,如果它要允許客戶端提供的對(duì)象進(jìn)入內(nèi)部數(shù)據(jù)結(jié)構(gòu)時(shí)题诵,就要考慮客戶提供的對(duì)象是否有可能是可變的:如果是不可變的须误,就直接提供保護(hù)性拷貝;如果是可變的仇轻,就要考慮是否能夠容忍對(duì)象進(jìn)入數(shù)據(jù)結(jié)構(gòu)之后發(fā)生的變化:如果不能忍,就提供保護(hù)性拷貝奶甘。

長(zhǎng)度非零的數(shù)組總是可變的篷店。因此把內(nèi)部數(shù)據(jù)返回給客戶端的時(shí)候一定要進(jìn)行保護(hù)性拷貝。

在內(nèi)部組件被返回給客戶端之前臭家,對(duì)它們進(jìn)行保護(hù)性拷貝也是同樣的道理疲陕。認(rèn)真考慮是否應(yīng)該把一個(gè)指向內(nèi)部可變的引用返回給客戶端。
但其實(shí)钉赁,只要有可能蹄殃,都應(yīng)該使用不可變的對(duì)象作為對(duì)象內(nèi)部的組件,這樣就不必考慮保護(hù)性拷貝的問(wèn)題你踩。
例如Period那個(gè)例子诅岩,可以使用long值作為內(nèi)部屬性。

可以在類的文檔中清楚的講明带膜,調(diào)用者不能修改傳入的參數(shù)值或返回值吩谦。這樣可不用使用保護(hù)性拷貝,但這僅僅使用于代碼都是自己寫的的情形下膝藕。

總之式廷,如果類具有可變參數(shù)、并且是可以從外界傳入或返回的(并且在外界修改后違反類的約束的)芭挽,類就必須提供保護(hù)性拷貝滑废;
如果拷貝成本受到限制,并且信任它的客戶端不會(huì)不恰當(dāng)?shù)男薷慕M件袜爪,就可以在文檔中聲明客戶端不能修改類內(nèi)部的組件蠕趁,以此來(lái)替代保護(hù)性拷貝。

第40條:謹(jǐn)慎地設(shè)計(jì)方法簽名

謹(jǐn)慎的選擇方法名 易于理解的饿敲、與同一個(gè)包中其他名稱風(fēng)格一致的妻导、大眾認(rèn)可的

不要過(guò)于追求提供便利的方法 每個(gè)方法都應(yīng)該盡其所能。 方法太多會(huì)使類難以學(xué)習(xí)怀各、使用倔韭、文檔化、測(cè)試和維護(hù)瓢对。接口尤其如此寿酌。

避免過(guò)長(zhǎng)的參數(shù)列表,最多四個(gè) 盡量使用簡(jiǎn)短的參數(shù)列表硕蛹; 相同類型的長(zhǎng)參數(shù)列表格外有害醇疼,弄反了程序依然運(yùn)行硕并,排查困難很難; 有幾個(gè)方法可以嘗試:

把一個(gè)方法分解成多個(gè)方法秧荆,每個(gè)方法只需要這些參數(shù)的一個(gè)子集倔毙。通過(guò)提升這些子方法的正交性來(lái)避免產(chǎn)生的子方法過(guò)多。

創(chuàng)建輔助類乙濒,用來(lái)保存參數(shù)的分組陕赃。 如果一個(gè)頻繁出現(xiàn)的參數(shù)序列可以被看作是代表了某個(gè)獨(dú)特的實(shí)體,則建議使用這種方法颁股。

Build模式

對(duì)于參數(shù)類型么库,優(yōu)先使用接口而不是類 只要有適當(dāng)?shù)慕涌诳捎脕?lái)定義參數(shù),就優(yōu)先使用接口甘有,而不是該接口的實(shí)現(xiàn)類诉儒。

對(duì)于boolean參數(shù),優(yōu)先使用兩個(gè)元素的枚舉類型

第41條:慎用重載

public class CollectionClassifier{
   public static String classify(Set<?> s){
       return "Set";
   }
   public static String classify(List<?> list){
       return "Set";
   }
   public static String classify(Collection<?> c){
       return "Collection";
   }
   public static void main(String[] args){
       Collection<?>[] collections = {new HashSet<>(), new ArrayList<>(), HashMap<String,String>().values()};
   
       for(Collection<?> c : collections){
             System.out.println(classify(c));
       } 
   }
   //返回三次Collection
}

程序運(yùn)行時(shí)亏掀,對(duì)于重載方法overloaded method的選擇是靜態(tài)的忱反,即根據(jù)編譯時(shí)期的類型,而對(duì)于復(fù)寫方法overriden method的選擇是動(dòng)態(tài)的滤愕,即根據(jù)運(yùn)行時(shí)期的類型缭受。要注意的是,當(dāng)一個(gè)子類包含的方法聲明與其祖先類中的方法聲明具有同樣的簽名時(shí)该互,方法就被覆蓋了米者。如果實(shí)例方法在子類中被覆蓋了,并且這個(gè)方法是在該子類的實(shí)例上被調(diào)用的宇智,那么子類中的覆蓋方法將會(huì)執(zhí)行蔓搞,而不管子類實(shí)例編譯時(shí)的類型到底是什么。

避免胡亂使用重載機(jī)制
如果用戶根本不知道對(duì)于一組給定的參數(shù)随橘,其中的哪個(gè)重載方法將會(huì)被調(diào)用喂分,那么這樣的API就很可能導(dǎo)致錯(cuò)誤。這些錯(cuò)誤很隱蔽且難以排查机蔗。

怎么避免胡亂使用重載機(jī)制
永遠(yuǎn)不要導(dǎo)出兩個(gè)具有相同參數(shù)數(shù)目的重載方法蒲祈;
也不要重載具有可變參數(shù)的方法;
使用不同的方法名萝嘁;對(duì)于構(gòu)造器梆掸,可以使用靜態(tài)工廠

確定選擇哪個(gè)重載方法的規(guī)則是非常復(fù)雜的,大多數(shù)情況下盡可能謹(jǐn)慎的使用牙言,盡量不用酸钦,除非參數(shù)類型是不相關(guān)的。

總之,能夠重載方法并不意味著應(yīng)該重載方法厕宗。

  1. 對(duì)于多個(gè)具有相同參數(shù)數(shù)目的方法來(lái)說(shuō),應(yīng)該盡量避免重載方法眨攘。
  2. 第一條做不到時(shí)欢伏,避免同一組參數(shù)只需經(jīng)過(guò)類型轉(zhuǎn)換就可以傳遞給不同的重載方法入挣;
  3. 第二條做不到時(shí),就要保證當(dāng)傳遞同樣的參數(shù)時(shí)硝拧,所有重載方法的行為必須一致财岔;

第42條:慎用可變參數(shù)

可匹配不同長(zhǎng)度的變量的方法:variable arity method。
可變參數(shù):接收0個(gè)或多個(gè)指定類型的參數(shù)河爹。
可變參數(shù)機(jī)制通過(guò)先創(chuàng)建一個(gè)數(shù)組,數(shù)組的大小為調(diào)用位置所傳遞的參數(shù)數(shù)量桐款,然后將參數(shù)值傳到數(shù)組中咸这,最后將數(shù)組傳遞給方法。

可變參數(shù)方法的每次調(diào)用都會(huì)導(dǎo)致進(jìn)行一次數(shù)組分配和初始化魔眨。所以在重視性能的情況下媳维,可以考慮在4個(gè)參數(shù)以為不使用可變參數(shù)。
在定義參數(shù)數(shù)目不定的方法時(shí)遏暴,可以使用可變參數(shù)侄刽;但不應(yīng)該過(guò)渡濫用。

第43條:返回零長(zhǎng)度的數(shù)組或者集合朋凉,而不是null

返回null而不是零長(zhǎng)度數(shù)組或空集合會(huì)使調(diào)用者在幾乎用到該方法時(shí)都要進(jìn)行判空操作州丹,相對(duì)來(lái)說(shuō)比較麻煩;而且忘記該操作的話杂彭,就會(huì)出錯(cuò)墓毒。

性能方面
在這個(gè)級(jí)別上擔(dān)心性能問(wèn)題是不明智的,除非分析表明這個(gè)方法正是性能問(wèn)題的真正源頭亲怠。

返回一個(gè)零長(zhǎng)度數(shù)組或空集合是可能的所计,因?yàn)榱汩L(zhǎng)度數(shù)組是不可變的,而不可變對(duì)象是可以被共享的团秽。
Collections.emptyList()等主胧。

可以考慮返回空數(shù)組或空集合,看情況而定吧习勤。

第44條:為所有導(dǎo)出的API元素編寫文檔注釋

使用Javadoc來(lái)根據(jù)源碼的文檔注釋生成相應(yīng)的文檔踪栋。

為了正確的編寫API文檔,必須在每個(gè)被導(dǎo)出的類图毕、接口己英、構(gòu)造器、方法和域聲明之前增加一個(gè)文檔注釋吴旋。

方法的文檔注釋應(yīng)該簡(jiǎn)介的描述出它和客戶端之間的約定

對(duì)于類损肛、接口厢破、域,概要描述應(yīng)該是一個(gè)名詞短語(yǔ)治拿,它描述了該類或者接口的實(shí)例摩泪,或者域本身所代表的事物。

對(duì)于泛型劫谅、枚舉见坑、注解,確保要在文檔中說(shuō)明所有的類型參數(shù)捏检。

對(duì)于枚舉荞驴,一定要確保在文檔中說(shuō)明常量。
對(duì)于注解贯城,要確保在文檔中說(shuō)明所有成員熊楼,以及類型本身。

注意描述類的線程安全性能犯,如果類是可序列化的鲫骗,要說(shuō)明序列化形式

可以查看《How to Write Doc Comments》這本書。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末踩晶,一起剝皮案震驚了整個(gè)濱河市执泰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌渡蜻,老刑警劉巖术吝,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異茸苇,居然都是意外死亡顿苇,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門税弃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)纪岁,“玉大人,你說(shuō)我怎么就攤上這事则果♂:玻” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵西壮,是天一觀的道長(zhǎng)遗增。 經(jīng)常有香客問(wèn)我,道長(zhǎng)款青,這世上最難降的妖魔是什么做修? 我笑而不...
    開封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上饰及,老公的妹妹穿的比我還像新娘蔗坯。我一直安慰自己,他們只是感情好燎含,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開白布宾濒。 她就那樣靜靜地躺著,像睡著了一般屏箍。 火紅的嫁衣襯著肌膚如雪绘梦。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天赴魁,我揣著相機(jī)與錄音卸奉,去河邊找鬼。 笑死颖御,一個(gè)胖子當(dāng)著我的面吹牛榄棵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播郎嫁,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼祈噪!你這毒婦竟也來(lái)了泽铛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤辑鲤,失蹤者是張志新(化名)和其女友劉穎盔腔,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體月褥,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡弛随,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了宁赤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舀透。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖决左,靈堂內(nèi)的尸體忽然破棺而出愕够,到底是詐尸還是另有隱情,我是刑警寧澤佛猛,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布惑芭,位于F島的核電站,受9級(jí)特大地震影響继找,放射性物質(zhì)發(fā)生泄漏遂跟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望幻锁。 院中可真熱鬧凯亮,春花似錦、人聲如沸越败。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)究飞。三九已至置谦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間亿傅,已是汗流浹背媒峡。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留葵擎,地道東北人谅阿。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像酬滤,于是被迫代替她去往敵國(guó)和親签餐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,521評(píng)論 1 51
  • 今天盯串,我知道因?yàn)槲业纳矸荨钪嬷勇乳埽抑档脫碛兴杏钪娴亩Y物。它們是我繼承自宇宙的体捏。當(dāng)我敞開心靈和心扉時(shí)冠摄,我看到...
    英杰旺姆閱讀 138評(píng)論 0 0
  • 目 錄 ·青春巷子 上 一 章 ·躁動(dòng)的夏天 文 / 水木刅 故事簡(jiǎn)介 李想的心思 1. 李想回來(lái)幾天了,我倆一直...
    清和青時(shí)閱讀 525評(píng)論 0 4
  • 01 如果想要賺大錢,我們首先要具備什么能力年栓?最流行的說(shuō)法是拆挥,想賺大錢,一定要有冒險(xiǎn)精神某抓。因?yàn)槲覀円恢币杂赂腋偷蟆?jiān)強(qiáng)...
    默默不然閱讀 767評(píng)論 0 7
  • 依我看,國(guó)慶和中秋這樣的節(jié)日搪缨,畢竟最像節(jié)日食拜。也是眾望所歸的時(shí)刻,這樣的時(shí)刻總是讓人翹首以盼副编「旱椋看看所謂的客流高峰和旅...
    安言靜語(yǔ)閱讀 132評(píng)論 0 0