[读书笔记]Effective Java 第7章 方法

Item 38: 检查参数的有效性

比较推荐的做法是,在方法体的开头或者在构造函数之中就对参数进行有效性检查。尽早的检查,对调试的帮助越大。

在文中出现提到了java的断言(Assertions)机制。

正确使用Assert的方法应该是:

  1. 可以在方法体的开头的地方进行有效性检查
  2. 普通的运行方法,断言不会起作用(会直接略过)。除非在运行的时候加上参数:-ea / -enableassertaions
  3. 通常Assert的代码不应该在Production代码之中,只会出现在debug/test/dev的阶段。

除非参数检查的逻辑跟原本的逻辑就重复,或者参数检查逻辑非常消耗资源,一般建议都进行参数检查。

本文原创,原文链接:https://www.flyml.net/2017/03/13/effective-java-ch7-method/

Item 39: 必要时进行保护性拷贝(defensive copy)

保护性拷贝能防止调用者有意或者无意之中破坏类的内部逻辑。比如下面的代码:

 

虽然在初始化的时候,已经限制了start <= end, 但是并不能防止可变类Date在后期被改变。如下面的操作方式:

 

可以看到, 最终的输出结果是1978年。

解决方法:在构造函数之中,新new一个Date对象,而不是直接使用。

 

Q: 在构造函数之中,是否可以用Date.clone()的方式呢?

A:不推荐。 因为Date并不是final类型, 可以被子类化。 在被子类化的同时, clone()有可能已经被恶意修改。 因此是不可以被信任的。

另外,可以尝试使用Joda Time。Joda Time 里面的时间类型基本都是final,可以放心大胆的调用clone() 方法。

同时,Java8的java.time做了很多改进, 可以学习看看。

上面的代码还有一个可以被攻击的地方:

 

修改的方法类似构造函数那部分:返回的时候也new一把

 

PS: JAVA默认的Date在安全性上面真心蛋疼。切换到Joda Time 应该会好很多。

不过其他可变类也同样要面对这种情况。

那么是不是所有可变类都要这么干?

一般来说,如果无法容忍约束条件不能被破坏,那么在设计这个类的时候,就应该考虑进行保护性拷贝。

比较方便的是使用不可变类作为内部组件。

本文原创,原文链接:https://www.flyml.net/2017/03/13/effective-java-ch7-method/

Item 40: 谨慎设计方法签名(method signatures)

本条目是一些设计技巧的总结。

  1. 选择容易理解的方法名称
  2. 方法不要太碎片化 (PS:这个感觉非常依赖个人经验)
  3. 避免太长的参数列表

    书中推荐不超过4个。如果有的功能确实需要好几个参数, 可以尝试Builder模式

  4. 参数类型优先使用接口而不是类(Item 52)

    比如使用Map而不是使用HashMap作为输入的类型。这样客户端输入比如Hashtable / HashMap / TreeMap 等都可以正常进行工作。

  5. 对于boolean参数, 优先使用两个元素的枚举

    除非参数名称比较长, 否则true/false 确实很容易不清楚具体true/false各是什么

Item 41: 慎用重载(overloading)

反例:

 

输出的结果是三个Unknown Collection

重载最大的问题在于具体调用哪个版本有可能在运行时才知道。 一个解决方法就是,参数列表要不太一样。比如个数、名字等等。

推荐的方式是:使用多个方法名不一样的方法,而不是重载。特别是参数数目都一样的情况之下。即使一定要这么做,也不要想上面的反例这样,因为List/Set都是Collection的子类。

PS:感觉校招题特别喜欢在重载上面做文章。

我感觉在实际coding之中, 谁瞎用或者乱用重载、继承,要被喷死的节奏。。。

Item 42: 谨慎使用可变参数

PS: 不知道这标题是不是有点吓人, 但是可变参数在现代JDK以及很多Java SDK之中的应用场景还是很多的。 最常见的常见就是在写数据库的时候,PreparedStatement 里面一般都是用的可变参数。

至少有一个可用参数时

当传入的参数至少有一个需要存在的时候, 书中提倡这样使用可变参数:

 

即设置两个参数, 强制第一个参数有效。 这样做的好处是不需要在方法体之中检查是否至少有一个参数了。

可变参数对性能有一些影响

可变参数方法的调用会进行一次数组的分配与初始化。如果凭经验觉得数组的分配与初始化性能消耗比较严重的时候, 可以参考下面的做法:

前提:确认参数绝大部分情况下参数的个数不超过3个。

[书中还对JDK从1.4升级到1.5的一些不妥做法做了一些说明, 主要火力是说Arrays.asList()的改造不太靠谱, 会让编译器的类型检查失效。在这里就不详细讨论了。]

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

只要有可能,返回0长度的数组/集合。这样在客户端、调用者的代码之中, 逻辑就很自然, 而不是单独写几行判断是不是null的检查代码。

Item 44:为所有导出的API元素编写文档注释(doc comment)

文中提倡:

为了正确的编写API文档, 需要在每个被导出的类、接口、构造函数、方法、域声明之前增加文档注释。

但是比如Spark,很多方法级别的说明, 都是在类前面写了一大篇说明文档。

文档注释的一些简单语法规则

具体可以参考极客学院的文章:

http://wiki.jikexueyuan.com/project/java/documentation.html

注意: 不知道为什么,在很多文章之中都没有出现书中提到的@code@literal 以及this 在文档注释之中的特殊作用。可能使用的姿势不对, this 确实好像没有特殊作用, 而@code @literal等确实是可以起作用的。

包级别的文档注释

应该放在package-info.java 之中。package-info.java也可以包含声明与注解。我们可以实际参考guava之中是如何使用package-info.java的:

https://github.com/google/guava/blob/master/guava/src/com/google/common/io/package-info.java

https://github.com/google/guava/blob/master/guava/src/com/google/common/primitives/package-info.java

注:Guava在很多包里面都有这个package-info.java. (人工看的, 并没有一个个的去检查。。。)。因此这个应该是一个不错的实践方法。

Html validity checker

现代IDE通常可以直接显示编写出来的文档注释是否语法正确有效。

新增:关于换行的思考

之前在写文档注释的时候, 经常需要手写换行符。 参考Guava等开源项目的做法, 他们一般使用<p></p>标签划分段落。

感觉JavaDoc 如果能支持Markdown的格式就很爽了。这个貌似可以通过第三方支持:https://dzone.com/articles/using-markdown-syntax-javadoc

如果能原生直接支持就更好了。

本文原创,原文链接:https://www.flyml.net/2017/03/13/effective-java-ch7-method/

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注