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个重载:

 public void fun() {}
public void fun(int a1) {}
public void fun(int a1, int a2) {}
public void fun(int a1, int a2, int a3) {}
public void fun(int a1, int a2, int a3, int... rest) {}

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

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

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

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

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

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

  1. 使用Javadoc进行注释,包括所有导出的类、接口、构造器、方法或域
  2. 对于方法,简洁的描述它和客户端之间的约定,说明这个方法做了什么,而不是如何完全的,以前所有的前提条件、后置条件、副作用以及线程安全性

Effective Java 阅读笔记——方法的更多相关文章

  1. Effective Java阅读笔记——引言

    “我很希望10年前就拥有这本书.可能有人认为我不需要任何Java方面的书籍,但是我需要这本书.” ——Java之父 James Gosling 在图书馆找到这本java著作时,首先看到了这句话.   ...

  2. Effective Java 阅读笔记——并发

    66:同步访问共享的可变数据 synchronized:1互斥,阻止线程看到的对象处于不一致的状态:2保证线程在进入同步区时能看到变量的被各个线程的所有修改 Java中,除了long或者double, ...

  3. Effective Java 阅读笔记——枚举和注解

    30:用enum代替int常量 当需要一组固定常量的时候,应该使用enum代替int常量,除了对于手机登资源有限的设备应该酌情考虑enum的性能弱势之外. 31:用实例域代替序数 应该给enum添加i ...

  4. 《Effective Java》笔记45-56:通用程序设计

    将局部变量的作用域最小化,可以增强代码的可读性和可维护性,并降低出错的可能性. 要使用局部变量的作用域最小化,最有力的方法就是在第一次使用它的地方才声明,不要过早的声明. 局部变量的作用域从它被声明的 ...

  5. Effective Java 学习笔记之第七条——避免使用终结(finalizer)方法

    避免使用终结方法(finalizer) 终结方法(finalizer)通常是不可预测的,也是很危险的,一般情况下是不必要的. 不要把finalizer当成C++中析构函数的对应物.java中,当对象不 ...

  6. Effective Java 读书笔记(一):使用静态工厂方法代替构造器

    这是Effective Java第2章提出的第一条建议: 考虑用静态工厂方法代替构造器 此处的静态工厂方法并不是设计模式,主要指static修饰的静态方法,关于static的说明可以参考之前的博文&l ...

  7. Effective java读书笔记

    2015年进步很小,看的书也不是很多,感觉自己都要废了,2016是沉淀的一年,在这一年中要不断学习.看书,努力提升自己 计在16年要看12本书,主要涉及java基础.Spring研究.java并发.J ...

  8. Effective Java读书笔记完结啦

    Effective Java是一本经典的书, 很实用的Java进阶读物, 提供了各个方面的best practices. 最近终于做完了Effective Java的读书笔记, 发布出来与大家共享. ...

  9. Effective Java要点笔记

    第一章: 创建和销毁对象 类可以通过静态工厂方法来提供客户端,而不是通过构造器 优点: 自定义工厂名称,提高可读性 可以工厂里搞单例 控制实例类是哪种子类 总之是更加灵活,可读性更高 缺点: 有可能会 ...

随机推荐

  1. win7左ctrl和左alt键互换

    主要参考这篇文章: http://xyztony1985.blog.163.com/blog/static/3611782011752420104/ 感谢原博主 Windows Registry Ed ...

  2. Into concurrent LRU caching once again

    But this time, with a more product oriented point of view, instead of researching. http://openmymind ...

  3. ruby -- 进阶学习(十七)应用代码优化

    ROR开发,代码优化的方法下面这两项是比较重要的: link_to  Rails的link_to是非常慢的,它的代码实现过于复杂,特别是Rails1.2引入了REST以后,大量的命名路由被使用,这些命 ...

  4. asynchronous-logging-with-log4j-2--转

    原文地址:https://dzone.com/articles/asynchronous-logging-with-log4j-2 Log4J 2 is a logging framework des ...

  5. uboot命令及内核启动参数

        修改:mw [内存地址] [值] [长度] 例如:mw 0x02000000 0 128 表示修改地址为0x02000000~0x02000000+128的内存值为0. 显示:md [内存地址 ...

  6. sed用例

    文件空行处理 1. 在文件中的每一行后面添加一个空行. sed 'G' test.txt 解释: Get命令是将保留空间的内容取出,并添加到当前模式空间的内容之后(添加一行).当保留空间为空时,效果为 ...

  7. ACM中的浮点数精度处理

    在ACM中,精度问题非常常见.其中计算几何头疼的地方一般在于代码量大和精度问题,代码量问题只要平时注意积累模板一般就不成问题了.精度问题则不好说,有时候一个精度问题就可能成为一道题的瓶颈,让你debu ...

  8. 重构第9天:提取接口(Extract Interface)

    理解:提取接口的意思是,多于一个类共同使用某个类中的方法或属性,那么我们可以把这些方法和属性提出来,作为一个单独的接口.这样的好处是解除代码间的依赖,降低耦合性. 详解: 先看重构前的代码: publ ...

  9. C#设计模式——策略模式(Strategy Pattern)

    一.概述我们来实现一个企业的工资系统,该企业中不同级别的员工工资算法都不相同,针对该问题,最容易想到的莫过于在代码中堆积一大堆if…else…语句或者是switch…case…语句.如果该企业中不同级 ...

  10. 一个App的界面设计流程是怎么产生的

    作者:候佩雯链接:http://www.zhihu.com/question/27088793 完整的流程,分层次设计,自下而上去完成: 策略层,定义产品使命.价值.目标人群 愿景/功能层:定义核心场 ...