優(yōu)雅編程之這樣編寫方法沃暗,你就“正常”了(二十八)

開(kāi)心一笑

【一大哥去醫(yī)院看病何恶。
醫(yī)生問(wèn):你得了什么材踝丁?
大哥說(shuō): 我得了間接性失憶癥细层。
醫(yī)生問(wèn):具體什么癥狀惜辑?
大哥說(shuō):我一看到漂亮的姑娘就忘記自己已結(jié)婚了。
醫(yī)生說(shuō):滾滾滾疫赎,這病我自己都沒(méi)治好盛撑!】

提出問(wèn)題

項(xiàng)目開(kāi)發(fā)中,使用方法要注意的一些事項(xiàng)捧搞?抵卫??

解決問(wèn)題

以下來(lái)自《Effective Java》中的讀書筆記:

檢查參數(shù)有效性

應(yīng)該在發(fā)生錯(cuò)誤之后盡快檢測(cè)出錯(cuò)誤

例如:

public BigInteger mod(BigInteger m){
    //盡快檢查錯(cuò)誤
    if(m.signum() <= 0){
        throw new ArithmeticException("Modulus <= 0:" + m);
    }
    ....
}

非共有的方法通常應(yīng)該使用斷言來(lái)檢查它們的參數(shù)实牡,具體做法如下:

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;
    ....//dosomethiing
}
必要時(shí)進(jìn)行保護(hù)性拷貝

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

例如:

public final class Period{
    private final Date start;
    private final Date end;
    
    public Period(Date start,Date end){
        ....
        this.start = start;
        this.end = end;
    }
    
    public Date start(){
        return start;
    }
    
    public Date end(){
        return end;
    }
}

Date start = new Date();
Date end = new Date();
Period p = new Period(start,end);
//注意問(wèn)題出現(xiàn)在這里
end.setYear(78);

修改后:

public Period(Date start,Date end){
        ....
     this.start = new Date(start.getTime());
     this.end = new Date(end.getTime());
}

注意,保護(hù)性拷貝是在檢查參數(shù)有效性之前進(jìn)行的陌僵,并且有效性檢查是針對(duì)拷貝之后的對(duì)象轴合,而不是針對(duì)原始的對(duì)象创坞。

雖然替換構(gòu)造器就可以成功避免上述攻擊,但是改變Period實(shí)例仍然是有可能的受葛,例如下面例子:

Date start = new Date();
Date end = new Date();
Period p = new Period(start,end);
//注意問(wèn)題出現(xiàn)在這里
p.end().setYear(78);

進(jìn)一步修改:

public Date start(){
    return new Date(start.getTime());
}
    
public Date end(){
    return new Date(end.getTime());
}

簡(jiǎn)而言之题涨,如果類具有從客戶端得到或者返回到客戶端的可變組件,類就必須保護(hù)性的拷貝這些組件偎谁。如果拷貝的成本受到限制,并且內(nèi)信任它的客戶端不會(huì)不恰當(dāng)?shù)男薷慕M件纲堵,就可以在文檔中指明客戶端的職責(zé)是不得修改受到影響的組件巡雨,以此來(lái)代替保護(hù)性拷貝。

謹(jǐn)慎設(shè)計(jì)方法簽名
  • 謹(jǐn)慎地選擇方法的名稱席函。
  • 不要過(guò)于追求提供便利的方法铐望。
  • 避免過(guò)長(zhǎng)的參數(shù)列表。
    有三種方法可以縮短過(guò)長(zhǎng)的參數(shù)列表茂附,第一種是把方法分解成多個(gè)方法正蛙,每個(gè)方法只需要這些參數(shù)的一個(gè)子集。第二種方法是創(chuàng)建輔助類用來(lái)保存參數(shù)的分組营曼。第三種是如果方法帶有多個(gè)參數(shù)乒验,尤其是當(dāng)它們中有些是可選的時(shí)候,最好定一個(gè)對(duì)象來(lái)表示所有參數(shù)蒂阱。(這些在之前文章都有提過(guò)了)
慎用可變參數(shù)

在重視性能的情況下锻全,使用可變參數(shù)需要特別小心,可變參數(shù)方法的每次調(diào)用都會(huì)導(dǎo)致進(jìn)行一次數(shù)組分配和初始化录煤。如果憑經(jīng)驗(yàn)確定無(wú)法承受這一成本鳄厌,但又需要可變參數(shù)的靈活性,還有一種模式可以讓你如愿以償妈踊。假設(shè)確定對(duì)某個(gè)方法95%的調(diào)用會(huì)有3個(gè)或者更少的參數(shù)部翘,就聲明改方法的5個(gè)重載,每個(gè)重載方法帶有0至3個(gè)普通參數(shù)响委,當(dāng)參數(shù)的數(shù)目超過(guò)3個(gè)時(shí)新思,就使用一個(gè)可變參數(shù)方法:

例如:

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){}

這種方法可能不太恰當(dāng),但是一旦需要它時(shí)赘风,它可就幫上大忙了夹囚。

總之,在定義參數(shù)數(shù)目不定的方法時(shí)邀窃,可變參數(shù)方法是一種很方便的方式荸哟,但是它們不應(yīng)該被過(guò)度濫用。如果使用不當(dāng)瞬捕,會(huì)產(chǎn)生混亂的結(jié)果鞍历。

返回零長(zhǎng)度的數(shù)組或集合,而不是null

例如:

public Cheese[] getCheeses(int size){

    if(size == 0){
        //這里返回null肪虎,是錯(cuò)誤的劣砍,因?yàn)檎{(diào)用改方法,還需要判斷這個(gè)null對(duì)象
        return null;
    }
}

正確做法:

private final List<Cheese> cheesesInStock = ...;
//定義final型的空數(shù)組
private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0];

public Cheese[] getCheeses(){
    //返回空數(shù)組而不是null
    return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY);
}

同樣的對(duì)于集合(Collections.emptySet,emptyList,emptyMap等等)

public List<Cheese> getCheesesList(){
    if(cheesesInStock.isEmpty()){
        //返回不可變的空集合扇救,對(duì)于map
        return Collections.emptyList();
    }else{
        return new ArrayList<Cheese>(cheesesInStock);
    }
    
}

有時(shí)候會(huì)有人認(rèn)為:null返回值比零長(zhǎng)度數(shù)組更好刑枝,因?yàn)樗苊饬朔峙鋽?shù)組所需要的開(kāi)銷香嗓。

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

例如:

/**
 * <p> the method is ...<i>not<i>
 * Returns the element at the specified position in this list.
 *
 * @param  index index of the element to return
 * @return the element at the specified position in this list
 * @throws IndexOutOfBoundsException {@inheritDoc} if the index is out of..
 */
public E get(int index) {
    rangeCheck(index);

    return elementData(index);
}

方法的文檔注釋中每個(gè)參數(shù)都有一個(gè)@param標(biāo)簽(后面應(yīng)該是一個(gè)名詞),以及一個(gè)@return標(biāo)簽(除非返回為空)装畅,以及@throws標(biāo)簽(后面應(yīng)該是一個(gè)名詞)靠娱,@throws標(biāo)簽后應(yīng)該包含單詞"if",描述了異常將在什么樣的條件下會(huì)被拋出掠兄。

文檔注釋還使用了HTML標(biāo)簽(<p>和<i>).Javadoc工具會(huì)把文檔注釋翻譯成HTML

**{@code} **:表示該代碼片段以代碼字體進(jìn)行呈現(xiàn)

{@literal}:可以把小于號(hào)像云,大于號(hào)等包起來(lái),用來(lái)輸出這些字符蚂夕。

更多的文檔注釋苫费,可以看《Effective Java》對(duì)它的描述。

讀書感悟

來(lái)自亦舒《不易居》

  • 現(xiàn)在還有誰(shuí)會(huì)照顧誰(shuí)一輩子双抽,那是多沉重的一個(gè)包袱百框。所以非自立不可。
  • 你要改是因?yàn)槟阕约涸敢飧碾剐冢灰獮槿魏稳祟砦轮慌履侨藭?huì)令你失望,你又得打回原形慎菲。

其他

如果有帶給你一絲絲小快樂(lè)嫁蛇,就讓快樂(lè)繼續(xù)傳遞下去,歡迎轉(zhuǎn)載露该,點(diǎn)贊睬棚,頂,歡迎留下寶貴的意見(jiàn)解幼,多謝支持抑党!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市撵摆,隨后出現(xiàn)的幾起案子底靠,更是在濱河造成了極大的恐慌,老刑警劉巖特铝,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件暑中,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡鲫剿,警方通過(guò)查閱死者的電腦和手機(jī)鳄逾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)灵莲,“玉大人雕凹,你說(shuō)我怎么就攤上這事。” “怎么了请琳?”我有些...
    開(kāi)封第一講書人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)赠幕。 經(jīng)常有香客問(wèn)我俄精,道長(zhǎng),這世上最難降的妖魔是什么榕堰? 我笑而不...
    開(kāi)封第一講書人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任竖慧,我火速辦了婚禮,結(jié)果婚禮上逆屡,老公的妹妹穿的比我還像新娘圾旨。我一直安慰自己,他們只是感情好魏蔗,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布砍的。 她就那樣靜靜地躺著,像睡著了一般莺治。 火紅的嫁衣襯著肌膚如雪廓鞠。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,727評(píng)論 1 305
  • 那天谣旁,我揣著相機(jī)與錄音床佳,去河邊找鬼。 笑死榄审,一個(gè)胖子當(dāng)著我的面吹牛砌们,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播搁进,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼浪感,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了饼问?” 一聲冷哼從身側(cè)響起篮撑,我...
    開(kāi)封第一講書人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎匆瓜,沒(méi)想到半個(gè)月后赢笨,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡驮吱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年茧妒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片左冬。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡桐筏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拇砰,到底是詐尸還是另有隱情梅忌,我是刑警寧澤狰腌,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站牧氮,受9級(jí)特大地震影響琼腔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜踱葛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一丹莲、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧尸诽,春花似錦甥材、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至商蕴,卻和暖如春板鬓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背究恤。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工俭令, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人部宿。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓抄腔,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親理张。 傳聞我的和親對(duì)象是個(gè)殘疾皇子赫蛇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

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

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)雾叭,斷路器悟耘,智...
    卡卡羅2017閱讀 134,668評(píng)論 18 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,182評(píng)論 25 707
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法织狐,內(nèi)部類的語(yǔ)法暂幼,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法移迫,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,644評(píng)論 18 399
  • 近日讀了一本書《娛樂(lè)至死》厨埋,雖然這本書初版于1985年邪媳,但對(duì)當(dāng)下仍有現(xiàn)實(shí)意義。 這本書諷刺了當(dāng)時(shí)美國(guó)在電報(bào)、電視等...
    平蕪aay閱讀 339評(píng)論 0 1
  • 三年級(jí)反思雨效。 三年級(jí)的課程迅涮,是最基礎(chǔ)的入門課程,我保持了講課速度慢徽龟,注意前提的叮姑,這種策略 在開(kāi)學(xué)到現(xiàn)在,這十幾星期...
    lygly9閱讀 229評(píng)論 0 0