《Effective Java》第7章 方法

第38条:检查参数的有效性

对于公有的方法,要用javadoc的@throws标签(tag)在文档中说明违反参数值限制时会抛出的异常。这样的异常通常为IllegalArgumentException, IndexOutOfBoundsException或NullPointerException.

非公有的方法通常应该使用断言(assertion)来检查它们的参数,具体做法如下所示:

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

没有对象的帮助时,虽然另个类不可能修改对象的内部状态,但是对象很容易在无意识的情况下提供这种帮助。例如,考虑下面的类,它声称可以表示一段不可变的时间周期:

乍一看,这个类似乎是不可变的,并且强加了约束条件:周期的起始时间(start)不能在结束时间(end)之后。然而,因为Date类本身是可变的,因此很容易违反这个约束条件:

为了保护Period实例的内部信息避免受到这种攻击,时于构造器的每个可变参数进行保护性烤贝( defensive copy)是必要的,并且使用备份对象作为Period实例的组件,而不使用原始的对象:

注意,保护性拷贝是在检查参数的有效性之前进行的,并且有效性检查是针时拷贝之后的对象,而不是针时原始的时象。虽然这样做看起来有点不太自然,却是必要的。

第41条:慎用重载

一个反例:

你可能期望这个程序会打印出"Set" ,紧接着是“List",以及“Unknown Collection" ,但实际上不是这样。它是打印“Unknown Collection"三次。

classify方法被重载(overloaded)了,而要调用哪个重载(overloading)方法是在编译时做出决定的。对于for循环中的全部三次迭代,参数的编译时类型都是相同的:Collections<>。每次迭代的运行时类型都是不同的,但这并不影响对重载方法的选择。因为该参数的编译时类型为Collection<>.

这个程序的行为有悖常理,因为对于重载方法(overloaded method ]的选择是静态的,而对于被覆盖的方法(overridden method)的选择则是动态的。选择被覆盖的方法的正确版本是在运行时进行的,选择的依据是被调用方法所在对象的运行时类型。

name方法是在类Wine中被声明的,但是在类SparklingWine和Champagne中被覆盖。正如你所预期的那样,这个程序打印出“wine, sparkling wine和champagne" ,尽管在循环的每次迭代中,实例的编译时类型都为Wine。当调用被覆盖的方法时,对象的编译时类型不会影响到哪个方法将被执行; “最为具体的(most specific)”那个覆盖版本总是会得到执行。

对classify方法的最佳修正方案是,用单个方法来替换这三个重载的classify方法,并在这个方法中做一个显式的instanceof测试:

因为覆盖机制是规范,而重载机制是例外,所以,覆盖机制满足了人们对于方法调用行为的期望。正如CollectionClassifier例子所示,重载机制很容易使这些期望落空。

到底怎样才算胡乱使用重载机制呢?这个问题仍有争议. 安全而保守的策略是,永远不要导出两个具有相同参数数目的重载方法。如果方法使用可变参数(varargs),保守的策略是根本不要重载它.

你始终可以给方法起不同的名称,而不使用重载机制。

对于构造器,你没有选择使用不同名称的机会;一个类的多个构造器总是重载的。在许多情况下,可以选择导出静态工厂,而不是构造器.

JDK一个反例

在Java 1.5发行版本之前,所有的基本类型都根本不同于所有的引用类型。但是当自动装箱出现之后,就不再如此了,它会导致真正的麻烦。考虑下面这个程序:

如果像大多数人一样。希望程序从集合和列表中去除非整数值(0, 1和2),并打印出[-3, -2, -1] [-3, -2, -1]。事实上,打印出[-3, -2, -1] [-2, 0, -2] .

实际发生的情况是: set.remove(i)调用选择重载方法remove(E),这里的E是集合(Integer)的元素类型,将i从int自动装箱到Integer中。这是你所期待的行为,因此程序不会从集合中去除正值。另一方面,list.remove(i)调用选择重载方法remove(int i),它从列表的指定位置上去除元素。

第42条:慎用可变参数

可变参数方法接受0个或者多个指定类型的参数。可变参数机制通过先创建一个数组,数组的大小为在调用位置所传递的参数数量,然后将参数值传到数组中,最后将数组传递给方法。

printf和反射机制都从可变参数中极大地受益。

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

对于一个返回null而不是零长度数组或者集合的方法,几乎每次用到该方法时都需要这种曲折的处理方式。这样做很容易出错,因为编写客户端程序的程序员可能会忘记写这种专门的代码来处理null返回值。

有时候会有人认为:null返回值比零长度数组更好,因为它避免了分配数组所需要的开销。这种观点是站不住脚的,原因有两点口第一,在这个级别上担心性能问题是不明智的,除非分析表明这个方法正是造成性能问题的真正源头(见第55条)。第二,对于不返回任何元素的调用,每次都返回同一个零长度数组是有可能的,因为零长度数组是不可变的,而不可变对象有可能被自由地共享(见第15条).

比如可以这样做:

同样地,集合值的方法也可以做成在每当需要返回空集合时都返回同一个不可变的空集合。Collections.emptySet, emptyList和emptyMap方法提供的正是你所需要的,如下所示:

简而言之,返回类型为数组或集合的方法没理由返回null,而不是返回一个零长度的数组或者集合。

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

为了正确地编写API文档,必须在每个被导出的类、接口、构造器、方法和域声明之前增加一个文档注释。

多行的代码示例前使用字符

{ @code ,然后在代码后面加上}

不要忘记,为了产生包含HTML元字符的文档,比如小于号(<)、大于号(>)以及“与”号(&),必须采取特殊的动作。让这些字符出现在文档中的最佳办法是用{@literal}标签将它们包围起来,这样就限制HTML标记和嵌套的Javadoc标签的处理。

Java 1.5发行版本中增加的三个特性在文档注释中需要特别小心:泛型、枚举和注解. 当为泛型或者方法编写文档时,确保要在文档中说明所有的类型参数。

当为枚举类型编写文档时,要确保在文档中说明常童,以及类型,还有任何公有的方法。注意,如果文档注释很简短,可以将整个注释放在一行上:

为注解类型编写文档时,要确保在文档中说明所有成员,以及类型本身。

从Java 1.5发行版本开始,包级私有的文档注释就应该放在一个称作package-info.java的文件中,而不是放在package.html中。除了包级私有的文档注释之外,package-info.java也可以(但并非必需)包含包声明和包注解。

Javadoc具有“继承”方法注释的能力。如果API元素没有文档注释,Javadoc将会搜索最为适用的文档注释,接口的文档注释优先于超类的文档注释。

也可以利用{@inheritDoc}标签从超类型中继承文档注释的部分内容。这意味着,不说别的,类还可以重用它所实现的接口的文档注释,而不需要拷贝这些注释。

时间: 2024-10-11 10:23:11

《Effective Java》第7章 方法的相关文章

[Effective Java]第七章 方法

第七章      方法 38.      检查参数的有效性 绝大多数方法和构造器对于传递给它们的参数值都会有某些限制.例如,索引值必须是非负的,对象引用不能为null等,这些都是常见的.你应该在文档中清楚地指明所有这些限制,并且在方法体的开头处检查参数,以强制施加这些限制. 应该在方法和构造器体前进行了参数的有效性检查,并且及时向外抛出适当的异常.如果方法没有检查它的参数,就有可能发生几种情形.该方法可能在处理过程中失败,并且产生令人费解的异常,更有可能,该方法可以正常返回,但是会悄悄地计算出错

Effective java 第三章对于所有对象都通用的方法(一) 读书笔记

对于所有对象都通用的方法 覆盖equals时请遵守通用约定 类的每个实例本质上都是唯一的. 不关心类是否提供了逻辑相等的测试功能 超类已经覆盖了equals,从超类继承过来的行为对于子类也是合适的. 类是私有的或是包级私有的,可以确定它的equals方法永远不会被调用. throw new AssertionError() 一般覆盖Object.equals都是值类 但有一种值类不需要覆盖equals方法,即实例受控,确保每个值至多只存在一个对象的类.如枚举 覆盖equals方法,通用约定. 自

[Effective Java]第六章 枚举和注解

第六章      枚举和注解 30.      用enum代替int常量 枚举类型是指由一组固定的常量组成合法值的类型,例如一年中的季节或一副牌中的花色.在没引入枚举时,一般是声明一组int常量,每个类型成员一个常量: public static final int APPLE_FUJI = 0; public static final int APPLE_PIPPIN = 1; public static final int APPLE_GRANNY_SMITH = 2; public sta

[读书笔记]Effective Java 第四章

使类和成员的可访问性最小化 规则很简单:尽可能地使每个类或者成员不被外界访问.实例域(非final)决不能是公有的.当需要暴露出不可变的实例时通常会把这个实例做成不可变或者是把这个实例变成私有,同时提供该实例的备份. 在公有类中使用访问方法而非公有域 这就是常说的getter和setter方法,提供给包外访问时提供必要的方法,限制客户端的行为,以便于将来可以在内部改变表示方法. 使可变性最小化 不可变的类比可变类更加易于设计.实现和使用.它们不容易出错,且更加安全.为了使类成为不可变,要遵循下面

Effective java经验之谈,通用方法

对于通用方法,其实应用的场景是比较多的,我们在写一个类的时候,就要考虑是否要编写该方法的通用方法.这使得我们为以后该类的扩展性与使用方面提供很大的便利. 1.      覆盖equals时请遵守通用约定.自反性,对称性,传递性,一致性,非空性.编写子类equals的方法的时候,可以考虑是否可以用复合,不使用继承来解决问题.Instanceof进行参数检测,如果参数null,也将返回false. 2.      覆盖equals时总要覆盖hashCode. 1.对象属性不变化,返回的hashcod

Effective Java 读书笔记之六 方法

一.检查参数的有效性 1.考虑参数有哪些限制,把限制写到文档中,在方法的开头处通过显式地检查来实施这些限制. 二.必要时进行保护性拷贝 1.如果类具有从客户端得到或者返回的可变组件,类就必须考虑保护性拷贝这些组件. 2.如果拷贝成本比较高,类又可以信任他的客户端,不进行拷贝保护,但要进行文档的说明. 三.谨慎设计方法签名 1.谨慎地选择方法的名称:遵守规范,保持风格一致. 2.不要过于追求提供便利的方法. 3.编码过长的参数列表 a.一个方法分解成多个方法 b.创建辅助类,保存参数的分组. c.

[读书笔记]Effective Java 第三章

覆盖equals方法时请遵守通用约定 这种说法的大意是要说明,Object类中定义的很多默认准则是被许多工具类或是第三方框架共同遵守的标准,一旦改动这样的标准机制,会在后续的使用中产生不稳定因素.equals方法常见用来做以下用途时,不建议对equals方法进行覆盖: 1.判断实例的唯一性 2.提供某种程度的逻辑相等 equals方法满足自反性,对称性,传递性,一致性,非空性.当需要覆盖equals方法时,需要注意以下三点: 1.覆盖equals时总要覆盖hashCode 2.不要企图让equa

java 第五章 方法定义及调用

1.方法的定义 什么是方法 方法是完成某个功能的一组语句,通常将常用的功能写成一个方法 方法的定义 [访问控制符] [修饰符] 返回值类型 方法名( (参数类型 形式参数, ,参数类型 形式参数, ,-) ) { 方法体 } 修饰符:public .static 被称为修饰符(后续会详细讲解它们): 返回值类型:用来说明该方法运算结果的类型.如果返回其他类型,编译就可能出错: 方法名:它作为调用时引用方法的标识: 参数列表:方法的参数个数可以是0个到多个,每个参数前面要声明参数的数据类型:每个参

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

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

effective java —— 终结方法守卫者

目录: effective java —— 终结方法守卫者 effective java 第2章:创建和销毁对象.第7条 : 避免使用终结方法.最后的“终结方法守卫者 (finalizer guardian)”的例子,以加深理解. 1 /** 2 * chapter 2——终结守卫者 3 * @ClassName: Parent 4 * TODO 5 * @author xingle 6 * @date 2015-3-11 下午3:49:47 7 */ 8 public class Parent