第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}标签从超类型中继承文档注释的部分内容。这意味着,不说别的,类还可以重用它所实现的接口的文档注释,而不需要拷贝这些注释。

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

  1. [Effective Java]第七章 方法

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  2. EFFECTIVE JAVA 第十一章 系列化

    EFFECTIVE  JAVA  第十一章  系列化(将一个对象编码成一个字节流) 74.谨慎地实现Serializable接口 *实现Serializable接口付出的代价就是大大降低了“改变这个类 ...

  3. effective java 第2章-创建和销毁对象 读书笔记

    背景 去年就把这本javaer必读书--effective java中文版第二版 读完了,第一遍感觉比较肤浅,今年打算开始第二遍,顺便做一下笔记,后续会持续更新. 1.考虑用静态工厂方法替代构造器 优 ...

  4. Effective Java —— 用静态工厂方法代替构造器

    本文参考 本篇文章参考自<Effective Java>第三版第一条"Consider static factory methods instead of constructor ...

  5. [Effective Java]第三章 对所有对象都通用的方法

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  6. 对于所有对象都通用方法的解读(Effective Java 第三章)

    这篇博文主要介绍覆盖Object中的方法要注意的事项以及Comparable.compareTo()方法. 一.谨慎覆盖equals()方法 其实平时很少要用到覆盖equals方法的情况,没有什么特殊 ...

  7. 【Effective Java】第二章-创建和销毁对象——1.考虑用静态工厂方法代替构造器

    静态工厂方法的优点: 可以赋予一个具有明确含义的名称 可以复用唯一实例,不必每次新建 可以返回原实例类型的子类对象 可以在返回泛型实例时更加简洁 缺点: 类如果不含有共有的或者受保护的构造器,就不能被 ...

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

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  9. [Effective Java]第五章 泛型

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  10. [Effective Java]第四章 类和接口

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

随机推荐

  1. HNOI2004 宠物收养所 (平衡二叉树)

    题目链接 平衡树基础题,用于测试各种平衡树的性能(雾) treap: #include<bits/stdc++.h> typedef long long ll; using namespa ...

  2. 5、Selenium+Python自动登录163邮箱发送邮件

    1.Selenium实现自动化,需要定位元素,以下查看163邮箱的登录元素 (1)登录(定位到登录框,登录框是一个iframe,如果没有定位到iframe,是无法定位到账号框与密码框) 定位到邮箱框( ...

  3. 关于Intellij IDEA导入jdk出现异常

    目前JDK出现的情况如下: 这里JavaJDK是有问题的,因为只有jre而没有JDK.正确做法是重新安装新的JDK. 正常的JDK下的目录是这样的: 选择jdk开头的文件夹就可以了.

  4. vim 显示行号

    set nu https://blog.csdn.net/lwj103862095/article/details/8122316

  5. Docker资源

    1.Docker入门教程 http://www.code123.cc/docs/docker-practice/repository/config.html 2.Docker入门教程 http://w ...

  6. Maven Build错误。

    错误: No goals have been specified for this build. You must specify a valid lifecycle phase or a goal ...

  7. -3dB的理解

    -3dB到底是什么?集成运放-3dB带宽又是什么? 以无源高通电路为例,介绍-3dB的意义. 输出与输入只比: Au=Uo/Ui=R/(R+1/j*2*PI*f*C)=1/(1+1/j*2*PI*f* ...

  8. Day2-VIM(五):复制

    粘帖 p 粘帖 复制的形式可以很多,但是粘帖的形式却没多少 无非就是加数字达到多次粘帖什么的 其实准确的说,p应该是放置的意思 不过我也搞不清是paste还是put的缩写 单词和字符复制 ynl 向右 ...

  9. EMIPLIB简介

    EMIPLIB(http://research.edm.uhasselt.be/emiplib)的全称是'EDM Media over IP libray' .EDM是Hasselt Universi ...

  10. L3-001. 凑零钱(dfs或者01背包)

    L3-001. 凑零钱 时间限制 200 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 韩梅梅喜欢满宇宙到处逛街.现在她逛到了一家火星店里,发现 ...