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. 对于方法,简洁的描述它和客户端之间的约定,说明这个方法做了什么,而不是如何完全的,以前所有的前提条件、后置条件、副作用以及线程安全性
时间: 2024-10-13 23:31:53

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

Effective Java阅读笔记——创建和销毁对象(一)

类通常提供一个公有的构造器方法,以此来让客户端可以获取自己(类)的一个实例.但是在创建对象时,应该首先考虑利用静态工厂方法代替构造器来返回一个实例.利用静态工厂方法而不是公有的构造器有几个优势: 静态工厂方法有(不同的)名字 构造器方法都有相同的名字,就是类的名字.区分不同构造器的方法是通过观察方法的签名确定的.方法的签名包括方法名,方法参数的类型,数目以及顺序,方法的返回类型不是方法签名的一部分.无法通过构造器方法名区分不同的构造器,只能通过参数的不同区分,这种情况下(特殊情况下有可能参数类型

Effective Java 阅读笔记——并发

66:同步访问共享的可变数据 synchronized:1互斥,阻止线程看到的对象处于不一致的状态:2保证线程在进入同步区时能看到变量的被各个线程的所有修改 Java中,除了long或者double,“读”或者“写”一个变量是原子的.注意:是读或者写单个动作是源自的,而不是读写这两个动作整体是原子的. 由于虚拟机会对代码进行优化,所以可能会导致一些错误:可能你想的是在另一线程中改变done的值来终止while循环,但是优化之后却无法做到这样.要避免这样的优化错误,就必须对done同步. //优化

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

30:用enum代替int常量 当需要一组固定常量的时候,应该使用enum代替int常量,除了对于手机登资源有限的设备应该酌情考虑enum的性能弱势之外. 31:用实例域代替序数 应该给enum添加int域,而不是使用ordinal方法来导出与枚举关联的序数值.(几乎不应使用ordinal方法,除非在编写像EnumMap这样的基于枚举的通用数据结构) //WRONG public enum Fruit{ APPLE, PEAR, ORANGE; public int numberOfFruit(

Effective Java读书笔记(3对于所有对象都通用的方法)

3.1 覆盖equals时请遵守通用约定 什么时候应该覆盖Object.equals()方法呢? 如果类具有自己特有的"逻辑相等"概念(不同于对象等同的概念),而且超类还没有覆盖equals以实现期望的行为,这时我们就需要覆盖equals方法. Object.equals()方法具有自反性.对称性.传递性.一致性和与null比较返回false的特点. 实现高质量equals方法的诀窍: (1)使用==操作符检查"参数是否为这个对象的引用".如果是,则返回true,这

Effective Java 读书笔记(2创建和销毁对象)

第一章是引言,所以这里不做笔记,总结一下书中第一章的主要内容是向我们解释了这本书所做的事情:指导Java程序员如何编写出清晰.正确.可用.健壮.灵活和可维护的程序. 2.1考虑用静态工厂方法代替构造器 静态工厂方法与构造器相比有四大优势: (1)静态工厂方法有名称,具有适当名称的静态工厂方法易于使用.易于阅读: (2)不必每次在调用它们的时候都创建一个新的对象: (3)可以返回原返回类型的任何子类型的对象: (4)在创建参数化类型实例的时候,它们使代码变得更加简洁. 同时静态工厂方法也有两大缺点

Effective Java读书笔记(4 类和接口)

4.1 使类和成员的可访问性最小化 要区别设计良好的模块和设计不好的模块,最重要的因素在于,这个模块对于外部的其他模块而言,是否隐藏其内部数据和其他实现细节.设计良好的模块会隐藏所有的实现细节,把它的API与它的实现清晰的隔离开来,然后模块之间只通过API进行通信,一个模块不需要知道其他模块内部的工作情况,这个概念被称为信息隐藏或封装,是软件设计的基本原则之一. 4.2 在公有类中使用访问方法而非公有域 坚持面向对象程序设计思想:如果类可以在它所在的包的外部进行访问,就提供访问方法,以保留将来改

Effective C++ 阅读笔记_条款27 尽量少做转型动作

Effective C++ 阅读笔记_条款27 尽量少做转型动作 1.转型的三种形式,可以分为两大类. (1)旧式转型(old-style casts) (1.1) (T) expresstion (1.2) T (expression) (2) 新式转型(c++-style casts) (2.1)const_cast<T> (expression) (2.2)dynamic_cast<T> (expression) (2.3)reinterpret_cast<T>

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

避免使用终结方法(finalizer) 终结方法(finalizer)通常是不可预测的,也是很危险的,一般情况下是不必要的. 不要把finalizer当成C++中析构函数的对应物.java中,当对象不可达时(即没有引用指向这个对象时),会由垃圾回收器来回收与该对象相关联的内存资源:而其他的内存资源,则一般由try-finally代码块来完成类似的工作. 一.finalizer的缺点: 1. 终结方法的缺点在于不能保证会被及时地执行. 及时执行finalizer方法是JVM垃圾回收方法的一个主要功

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

这是Effective Java第2章提出的第一条建议: 考虑用静态工厂方法代替构造器 此处的静态工厂方法并不是设计模式,主要指static修饰的静态方法,关于static的说明可以参考之前的博文<java中final与static的使用场景总结>. 什么是静态工厂方法? 可以参考书中的例子(摘自JDK1.7 java.lang.Boolean) public final class Boolean implements java.io.Serializable, Comparable<