优雅编程之这样编写方法,你就“正常”了(二十八)

开心一笑

【一大哥去医院看病。
医生问:你得了什么病?
大哥说: 我得了间接性失忆症。
医生问:具体什么症状?
大哥说:我一看到漂亮的姑娘就忘记自己已结婚了。
医生说:滚滚滚,这病我自己都没治好!】

提出问题

项目开发中,使用方法要注意的一些事项???

解决问题

以下来自《Effective Java》中的读书笔记:

检查参数有效性

应该在发生错误之后尽快检测出错误

例如:

public BigInteger mod(BigInteger m){
    //尽快检查错误
    if(m.signum() <= 0){
        throw new ArithmeticException("Modulus <= 0:" + m);
    }
    ....
}

非共有的方法通常应该使用断言来检查它们的参数,具体做法如下:

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
}
必要时进行保护性拷贝

对于构造器的每个可变参数进行保护性拷贝是必要的

例如:

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);
//注意问题出现在这里
end.setYear(78);

修改后:

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

注意,保护性拷贝是在检查参数有效性之前进行的,并且有效性检查是针对拷贝之后的对象,而不是针对原始的对象。

虽然替换构造器就可以成功避免上述攻击,但是改变Period实例仍然是有可能的,例如下面例子:

Date start = new Date();
Date end = new Date();
Period p = new Period(start,end);
//注意问题出现在这里
p.end().setYear(78);

进一步修改:

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

简而言之,如果类具有从客户端得到或者返回到客户端的可变组件,类就必须保护性的拷贝这些组件。如果拷贝的成本受到限制,并且内信任它的客户端不会不恰当的修改组件,就可以在文档中指明客户端的职责是不得修改受到影响的组件,以此来代替保护性拷贝。

谨慎设计方法签名
  • 谨慎地选择方法的名称。
  • 不要过于追求提供便利的方法。
  • 避免过长的参数列表。
    有三种方法可以缩短过长的参数列表,第一种是把方法分解成多个方法,每个方法只需要这些参数的一个子集。第二种方法是创建辅助类用来保存参数的分组。第三种是如果方法带有多个参数,尤其是当它们中有些是可选的时候,最好定一个对象来表示所有参数。(这些在之前文章都有提过了)
慎用可变参数

在重视性能的情况下,使用可变参数需要特别小心,可变参数方法的每次调用都会导致进行一次数组分配和初始化。如果凭经验确定无法承受这一成本,但又需要可变参数的灵活性,还有一种模式可以让你如愿以偿。假设确定对某个方法95%的调用会有3个或者更少的参数,就声明改方法的5个重载,每个重载方法带有0至3个普通参数,当参数的数目超过3个时,就使用一个可变参数方法:

例如:

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

这种方法可能不太恰当,但是一旦需要它时,它可就帮上大忙了。

总之,在定义参数数目不定的方法时,可变参数方法是一种很方便的方式,但是它们不应该被过度滥用。如果使用不当,会产生混乱的结果。

返回零长度的数组或集合,而不是null

例如:

public Cheese[] getCheeses(int size){

    if(size == 0){
        //这里返回null,是错误的,因为调用改方法,还需要判断这个null对象
        return null;
    }
}

正确做法:

private final List<Cheese> cheesesInStock = ...;
//定义final型的空数组
private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0];

public Cheese[] getCheeses(){
    //返回空数组而不是null
    return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY);
}

同样的对于集合(Collections.emptySet,emptyList,emptyMap等等)

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

有时候会有人认为:null返回值比零长度数组更好,因为他避免了分配数组所需要的开销。

为所有导出的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);
}

方法的文档注释中每个参数都有一个@param标签(后面应该是一个名词),以及一个@return标签(除非返回为空),以及@throws标签(后面应该是一个名词),@throws标签后应该包含单词"if",描述了异常将在什么样的条件下会被抛出。

文档注释还使用了HTML标签(<p>和<i>).Javadoc工具会把文档注释翻译成HTML

**{@code} **:表示该代码片段以代码字体进行呈现

{@literal}:可以把小于号,大于号等包起来,用来输出这些字符。

更多的文档注释,可以看《Effective Java》对它的描述。

读书感悟

来自亦舒《不易居》

  • 现在还有谁会照顾谁一辈子,那是多沉重的一个包袱。所以非自立不可。
  • 你要改是因为你自己愿意改,不要为任何人,怕只怕那人会令你失望,你又得打回原形。

其他

如果有带给你一丝丝小快乐,就让快乐继续传递下去,欢迎转载,点赞,顶,欢迎留下宝贵的意见,多谢支持!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,490评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,043评论 25 707
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,493评论 18 399
  • 近日读了一本书《娱乐至死》,虽然这本书初版于1985年,但对当下仍有现实意义。 这本书讽刺了当时美国在电报、电视等...
    平芜aay阅读 337评论 0 1
  • 三年级反思。 三年级的课程,是最基础的入门课程,我保持了讲课速度慢,注意前提的,这种策略 在开学到现在,这十几星期...
    lygly9阅读 226评论 0 0