Effective Java 阅读笔记——方法

38:检查参数的有效性

每当编写方法或者构造器的时候,应该考虑它的参数有哪些限制,在方法的开头处对参数进行检查,并且把这些限制写入文档。

注意:

  1. 对于公有方法,应该使用@throws标签在文档中说明违反参数值限制会抛出的异常
  2. 对于非公有的方法,通常使用断言来检查他们的参数:断言如果失败,抛出AssertionError;如果没有起到作用,本质上也不会有成本开销
    private void sort(long a[]){
        assert a != null;
    }

     

  3. 对于构造函数中的,或者参数将会被保存,后面还会用到的,尤其应该注意检查有效性

39:必要时进行保护性拷贝

如果类具有从客户端得到或者返回到客户端的可变组件,类就必须保护性的拷贝这些组件。除非拷贝成本收到限制,且类信任他的客户端不会不恰当的修改组件,就可以在文档中指明客户端的不得不恰当的修改组件,以代替保护性拷贝。

注意:

  1. 从客户端获取的可变组件也应该保护性拷贝,尤其在给构造函数传参时,这点平时没有注意到。而且,保护性拷贝是在检查参数的有效性之前进行的,并且有效性检查是针对拷贝之后的对象,而不是原始对象,这样可以避免检查到真正拷贝期间,数据被其他线程更改。这也很好的体现了:安全大于效率
  2. 对于从客户端获取的可变组件,如若其是可以被子类化的,那么就不能使用clone函数进行拷贝,因为可能传进来的是个子类对象,而clone函数可以被子类重写,从而引发安全问题。传出去的可以使用clone,因为在类的内部可以确定它是非子类的
  3. 真正应该注意的是:只要有可能,都应该使用不可变对象作为内部组件,以避免保护性拷贝。比如,使用Date.getTime()得到的long对象作为内部的时间组件,而不是Date,因为Date可变,而long不可变

40:谨慎的设计方法签名

 设计签名方法的要点:

  1. 选择合理的方法名称:遵循标准习惯与含义清晰
  2. 避免过于追求提供便利的方法以导致方法过多,尤其对于接口。除非被经常用到的操作才考虑提供便捷方法
  3. 避免过长的参数列表
    1. 拆分方法,但应注意方法的正交性以减少方法的个数,即把方法A、B拆成a,b,c三个方法,a和c实现A,b和c实现B
    2. 创建辅助类用来保存参数的分组,一般为静态成员类。尤其当某些参数经常一起出现时
    3. 采用Builder模式:多次调用setter设置参数,然后执行方法

注意:

  1. 对于参数类型,若可以,优先使用接口而不是类,以避免昂贵的拷贝操作
  2. 对于boolean参数,优先使用两个元素的枚举类型,以使代码更易阅读和编写,且具有更好的扩展性

41:慎用重载

重载是静态的,编译时根据调用的参数决定使用重载函数的哪个版本;覆盖是动态的,运行时根据实例的实际类型决定使用哪个版本。

更加准确的说,应该避免的重载是指:参数个数相同,且不同版本的形参之间通过类型转换就可以适用不同的版本,这样就使得程序员不是很容易就确定到底调用哪个版本,从而可能导致错误。

如果需要重载,最好:

  1. 使不同版本具有不同的形参个数
  2. 如果具有相同的形参个数,那么不同版本的形参是“根本不同的类型”,让人一看就知道调用哪个,比如int和List<int>,而不是int与Integer,因为他们二者会自动拆装箱
  3. 如果还不行,那么应该尽量保证不同版本的重载函数对于相同的形参,所产生的行为一致
  4. 考虑不使用重载,而使用不同的命名,比如ObjectOutputStream有writeBoolean、writeLong、writeInt这些方法,也是一种很好的策略

42:慎用可变参数

可变参数可以接受0个或者多个指定类型的参数。可变参数机制通过先创建一个数组,数组的大小为在调用位置所传递的参数数量,然后将参数值传到数组中,最后将数组传递给方法。在定义参数数目不定的方法时,可变参数是一种很方便的方式,但不应该被滥用,以免造成混乱。造成混乱的原因可能大多出于:

  1. 可以传递0个参数,即不传参数
  2. 当参数类型定义为T时。比如如果参数定义为T... args,调用时传入的是int[],但是由于int是基本类型,而不能直接看做对象T,所以将会把int[]整体当做一个参数,而不是多个int参数

另外,当重视性能,认为可变参数的每次调用进行的数组分配和初始化可能难以满足性能要求时,可以考虑如下方式:如果对某个方法95%的调用会有三个或者更少的参数,就声明该方法的5个重载:

1 public void fun() {}
2 public void fun(int a1) {}
3 public void fun(int a1, int a2) {}
4 public void fun(int a1, int a2, int a3) {}
5 public void fun(int a1, int a2, int a3, int... rest) {}

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

这个是对于返回类型为数组或者集合的方法来说的,

好处:返回零长度的数组或者集合,就不要求客户端要有额外的代码来处理null返回值的情况。

对于返回null可能比零长度数组或集合的性能优势的反驳:

  1. 这样级别的性能担心是不明智的,除非确定造成了性能问题
  2. 因为零长度的数组或集合是不可变的,而不可变对象是有可能自由共享的,即多次调用也只会生成一个零长度的数组或集合

44:为所有导出的API元素编写文档注释

  1. 使用Javadoc进行注释,包括所有导出的类、接口、构造器、方法或域
  2. 对于方法,简洁的描述它和客户端之间的约定,说明这个方法做了什么,而不是如何完全的,以前所有的前提条件、后置条件、副作用以及线程安全性
posted @ 2015-12-26 02:07  willhua  阅读(329)  评论(0编辑  收藏  举报