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

对于这一条,最常见的莫过于检查参数是否为null。

有时出现调用方未检查传入的参数是否为空,同时被调用方也没有检查参数是否为空,结果这就导致两边都没检查以至于出现null的值程序出错,通常情况下会规定调用方或者被调用方来检查参数的合法性,或者干脆规定都必须检查。null值的检查相当有必要,很多情况下没有检查值是否为空,结果导致抛出NullPointerException异常。

除了null,常见的还有对于方法参数的限制。一个典型的例子:

public BigInteger mod(BigInteger m) {
if (m.signum <= 0)
throw new ArithmeticException("BigInteger: modulus not positive"); BigInteger result = this.remainder(m);
return (result.signum >= 0 ? result : result.add(m));
}

总之,每当编写方法或构造器时,应该考虑它的参数有哪些限制,应该把这些限制写到文档中去,并且在这个方法的开头处,通过显示的检查来实施这些限制。

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

java是一门安全的语言,但即使在安全的语言中,如果不采取点措施,还是无法与其它的类隔离开来。假设类的客户端会尽其所能来破坏这个类的约束条件,因此必须保护性地设计程序。考虑下面的类,表示一段不可变的时间周期。

public final class Period {
private final Date start;
private final Date end;
public Period(Date start, Date end){
if(start.compareTo(end) > 0){
throw new IllegalArgumentException(start + " after " + end);
}
this.start = start;
this.end = end;
}
public Date start(){
return start;
}
public Date end(){
return end;
}
@Override
public String toString() {
return start + " -- " + end;
}
public static void main(String[] args){
Date start = new Date();
Date end = new Date();
Period p = new Period(start, end);
System.out.println(p);
end.setYear(78);
System.out.println(p);
}
}

从输出语句来看,这个“不可变的时间周期”变了。虽然私有域start和end的引用是不可变的,但其date类本身是可变的,因此很容易违反这个约束条件。因此对构造器和方法的保护性拷贝是很有必要的。

public final class Period2 {
private final Date start;
private final Date end;
public Period2(Date start, Date end){
this.start = new Date(start.getTime());
this.end = new Date(end.getTime());
if(this.start.compareTo(this.end) > 0){
throw new IllegalArgumentException(this.start + " after " + this.end);
}
}
public Date start(){
return new Date(start.getTime());
}
public Date end(){
return new Date(end.getTime());
}
@Override
public String toString() {
return start + " -- " + end;
}
public static void main(String[] args){
Date start = new Date();
Date end = new Date();
Period2 p = new Period2(start, end);
System.out.println(p);
end.setYear(78);
p.end().setYear(78);
System.out.println(p);
}
}

从代码的输出可以看出,无论我们怎么改,时间周期p都是不可变的。

第40条: 谨慎设计方法签名

使api更易于学习和使用的设计技巧:

1.谨慎地选择方法名称。

2.不要过于追求提供便利的方法。

3.避免过长的参数列表。三种方法可以缩短过长的参数列表:

  a.把方法分解成多个方法

  b.创建辅助类

  c.从对象的构建和方法调用都采用Builder模式。

对于参数类型,要优先使用接口而不是类。

对于boolean参数,要优先使用两个元素的枚举类型。

第41条: 慎用重载

考虑下面一个程序,它试图根据一个集合是Set还是list,还是其它集合类型,来对它进行分类:

public class CollectionClassifier {
/*public static String classify(Set<?> s){
return "Set";
}
public static String classify(List<?> s){
return "List";
}
public static String classify(Collection<?> s){
return "Unknown Collection";
} */
//避免滥用重载修正方案
public static String classify(Collection<?> s){
return s instanceof Set ? "Set" : s instanceof List ? "List" : "Unknown Collection";
}
public static void main(String[] args){
Collection<?>[] collections = {
new HashSet<String>(),
new ArrayList<BigInteger>(),
new HashMap<String, String>().values()
};
for(Collection<?> c : collections){
System.out.println(classify(c));
}
}
}

如果打开注释中的三个classify方法测试,我们会发现这个程序的行为有悖常理,它打印三次“Unknown Collection”。这是因为对于重载方法的选择是静态的,而对于被覆盖的方法的选择是动态的。我们可以用上面的修正方案来实现这个功能。下面是一个覆盖的例子:

public class Overriding {

    public static void main(String[] args) {
Wine[] wines = {
new Wine(),
new SparklingWine(),
new Champagne()
};
for(Wine wine : wines){
System.out.println(wine.name());
} } }
class Wine {
String name(){
return "wine";
}
}
class SparklingWine extends Wine {
@Override
String name() {
return "sparkling wine";
}
}
class Champagne extends SparklingWine {
@Override
String name() {
return "champagne";
}
}

可以看出,代码实现了正确的行为。简而言之,“能够重载方法”并不意味着“应该重载方法”,一般情况下,对于多个具有相同参数数目的方法来说,应该尽量避免重载方法。

第42条: 慎用可变参数

具有可变参数的方法可以传入0个或者多个参数,这种方法我相信自己写的可能在少数,用得最多的可能要属反射中的getDeclaredMethod方法:

public Method getDeclaredMethod(String name, Class<?>... parameterTypes)

为什么要慎用,其中有一个原因就是很有可能在没有传入参数的时候程序没有做任何保护而导致程序错误。另外有一个原因就是它会带来一定的性能问题,EnumSet类在传入少量参数的时候是直接调用具体的方法,只有在传入大量参数时才会调用可变参数的方法,这也是它在性能方面有优势的原因,因为可变参数的每次调用都会导致进行一次数组分配和初始化。

总之,“在定义参数数目不定的方法时,可变参数是一种很方便的方式,但是它们不应该被过度滥用。如果使用不当,会产生混乱的结果”。

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

使用和避免null:null是模棱两可的,会引起令人困惑的错误,有些时候它让人很不舒服。

例如对于一个Map,调用其get(key)方法,此时若返回null,可能表示这个key所对应的值本身就是null,或者表示这个Map中没有这个key值。这是两种截然不同的语义。

书中仅是说明对于零长度的数组或者集合不应该返回null,实际上对于所有的情况,都不要轻易返回null,特别是在语义不清的情况,更别说返回null时有的客户端程序并没有处理null的这种情况。如果一定要用到null,更好的办法是单独维护它。

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

现在的IDE很方便,只要轻轻敲几个快捷键就能方便的生成文档注释模板,所以不要觉得麻烦,基本的参数、返回值、用途的注释一定要写。

6.方法_EJ的更多相关文章

  1. 2.对于所有对象都通用的方法_EJ

    第8条: 覆盖equals时请遵守通用约定 我们在覆盖equals方法时,必须遵守它的通用约定: 1.自反性.对于任何非null的引用值x,x.equals(x)必须返回true: 2.对称性.对于任 ...

  2. javaSE27天复习总结

    JAVA学习总结    2 第一天    2 1:计算机概述(了解)    2 (1)计算机    2 (2)计算机硬件    2 (3)计算机软件    2 (4)软件开发(理解)    2 (5) ...

  3. mapreduce多文件输出的两方法

    mapreduce多文件输出的两方法   package duogemap;   import java.io.IOException;   import org.apache.hadoop.conf ...

  4. 【.net 深呼吸】细说CodeDom(6):方法参数

    本文老周就给大伙伴们介绍一下方法参数代码的生成. 在开始之前,先补充一下上一篇烂文的内容.在上一篇文章中,老周检讨了 MemberAttributes 枚举的用法,老周此前误以为该枚举不能进行按位操作 ...

  5. IE6、7下html标签间存在空白符,导致渲染后占用多余空白位置的原因及解决方法

    直接上图:原因:该div包含的内容是靠后台进行print操作,输出的.如果没有输出任何内容,浏览器会默认给该空白区域添加空白符.在IE6.7下,浏览器解析渲染时,会认为空白符也是占位置的,默认其具有字 ...

  6. 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

  7. [C#] C# 基础回顾 - 匿名方法

    C# 基础回顾 - 匿名方法 目录 简介 匿名方法的参数使用范围 委托示例 简介 在 C# 2.0 之前的版本中,我们创建委托的唯一形式 -- 命名方法. 而 C# 2.0 -- 引进了匿名方法,在 ...

  8. ArcGIS 10.0紧凑型切片读写方法

    首先介绍一下ArcGIS10.0的缓存机制: 切片方案 切片方案包括缓存的比例级别.切片尺寸和切片原点.这些属性定义缓存边界的存在位置,在某些客户端中叠加缓存时匹配这些属性十分重要.图像格式和抗锯齿等 ...

  9. [BOT] 一种android中实现“圆角矩形”的方法

    内容简介 文章介绍ImageView(方法也可以应用到其它View)圆角矩形(包括圆形)的一种实现方式,四个角可以分别指定为圆角.思路是利用"Xfermode + Path"来进行 ...

随机推荐

  1. Jenkins可用环境变量列表以及环境变量的使用(Shell/Command/Maven/Ant)

    一.可用环境变量列表(以下来自google翻译): BRANCH_NAME 对于多分支项目,这将被设置为正在构建的分支的名称,例如,如果您希望从而master不是从特征分支部署到生产. CHANGE_ ...

  2. C++ Opencv HoughLines()用霍夫变换在二元图像中寻线

    一.霍夫变换简介 参考http://homepages.inf.ed.ac.uk/rbf/HIPR2/hough.htm 二.HoughLines()函数详解 该函数接受的输入矩阵只能是8位单通道的二 ...

  3. H5内联视频总结

    概述 之前写过h5内联视频,总结了一下当时做微信视频类h5的心得,随着工作中越来越多的接触h5,我有了更多的心得与经验,记下来供以后开发时参考,相信对其他人也有用. 内联视频的播放 内联视频需要用户主 ...

  4. [Postman]发送第一个请求(1)

    通过API请求,您可以与具有要访问的API端点的服务器联系,并执行某些操作.这些操作是HTTP方法. 最常用的方法是GET,POST,PUT和DELETE.方法的名称是不言自明的.例如,GET使您可以 ...

  5. CSS Modules 与 scoped 的不一样

    What ? css 的作用域表现. Css modules 是一个CSS文件,其中所有类名和动画名称默认为局部作用域. 使用JS编译原生的CSS文件,使其具备模块化的能力,该文件需要import使用 ...

  6. 将本地代码上传到github走过的坑

    1.因为github是服务端,需要自己在自己的电脑上安装一个客户端git 2.配置SSH(配置一次就好了) github是不能随便上传代码上去的,而是通过一种网络协议---SSH授权的.SSH是一种网 ...

  7. Vue -- 基础语法和使用

    Vue 渐进式 JavaScript 框架 通过对框架的了解与运用程度,来决定其在整个项目中的应用范围,最终可以独立以框架方式完成整个web前端项目 一.走进Vue 1.what -- 什么是Vue ...

  8. Android Studio下HierarchyViewer的使用

    什么是HierarchyViewer Hierarchy Viewer是随AndroidSDK发布的工具,位置在tools文件夹下,名为hierarchyviewer.bat.它是Android自带的 ...

  9. ACM--string常见用法

    在ACM竞赛中,常常需要将读入的数字的每位分离出来,如果采用取余的方法,花费的时间就会太长,这时候,我们可以将读入的数据当成字符串来处理,这样就方便.省时多了.下面这个程序演示了求一个整数各位的和: ...

  10. ELK实践(一):基础入门

    虽然用了ELK很久了,但一直苦于没有自己尝试搭建过,所以想抽时间尝试尝试.原本打算按照教程 <ELK集中式日志平台之二 - 部署>(作者:樊浩柏科学院) 进行测试的,没想到一路出了很多坑, ...