《Effective java 第2版》读书笔记--通用程序设计

xiaoxiao2021-02-28  26

第七章 方法

第三十八条:检查参数的有效性

1.注意

(1)必须在文档中指明所有的限制,并且在方法体的开头检查限制。

(2)如果传递了无效的参数,那么方法应该很快失败并且抛出适当的异常

(3)对于公有的方法,要用JavaDoc的@throws标签在文档中说明违反参数限制会抛出的异常;对于未导出的方法,应该抛出断言

(4)如果方法并没有使用到参数,而是保存了参数,那么参数检查格外重要。例子:构造器,这样可以避免构造出来的对象违反类的约束条件

(5)例外:如果检查工作非常昂贵,或者是不切实际的,或者有效性检查已经在计算过程中完成。

第三十九条: 必要时进行保护性拷贝

1.保护性拷贝

(1)有些程序虽然看上去是final不可变的,但是date本身可变,而且只有引用,所以不安全。

例:

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;//改成return (Date) start.clone()可防御p.end.setYear(?) } public Date end(){ return end; } }

(2)对于构造器的每个可变参数进行保护性拷贝是必要的,并且使用备份对象作为实例组件,而不是原始对象

public Period(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(start + "after "+ end); } }

注意:保护性拷贝应该在参数检查之前,并且应该检查拷贝得到的备份

(3)对于参数类型可以被不信任方子类化的参数,不要调用clone进行拷贝,因为clone可能已经被恶意代码覆盖

第四十条:谨慎的设计方法签名

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

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

3.避免过长的参数列表

(1)分解方法。每个方法只需要参数的子集。可能有助于提升正交性

(2)创建辅助类,用来保存参数的分组。一般静态类。

(3)从对象创建到方法调用都使用builder模式

4.参数类型优先考虑接口而不是类。

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

6.要考虑API的性能后果。

使公有类变成可变的会导致大量的保护性拷贝

第四十一条:慎用重载

1.注意

(1)重载是在编译时做出的决定。

(2)重载的方法是静态选择,而被覆盖的方法的选择则是动态的。

(3)应该避免胡乱地使用重载机制。

安全而保守的策略是:永远不要导出两个具有相同参数数目的重载方法。如果方法使用可变参数,保守的策略是根本不要重载它。

例子:ObjectOutputStream类,并没有重载write方法,而是使用了writeBoolean(),writeInt()等方法签名。

(4)构造器只能重载。所以我们可以选择静态工厂。

第四十二条: 慎用可变参数

1.可变参数的概念:

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

2.注意 在重视性能的情况, 使用可变参数要特别小心, 因为可变参数方法每一次调用都会进行数组分配和初始化.

第四十三条:返回0长度的数组或者集合,而不是null 1.注意

(1)返回null而不是零长度的数组也会使返回数组或者集合的方法本身变得更加复杂

(2)返回0长度的数组在性能上会有少许损失,但是有两个优点

在这个级别上担心性能问题是不明智的,除非这个这个方法是造成问题的源头

对于不返回任何元素的调用,每次都返回同一个零长度数组是可能的,因为零长度的数组是不可变的,而不可变对象有可能被自由地共享。

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

1.注意

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

(2)方法的文档注释应该简洁的描述它和客户端之间的约定。

(3)方法的文档注释应该让每个参数都带有@param标签,以及一个@return标签。

(4)应该使用Javadoc{@code}标签, 以避免转义HTML元字符

(5)当”this“被用在实例方法的文档注释中时,它应该始终是指方法调用所在对象

(6)同一个类或接口中的两个成员或者构造器,不应该具有同样的概要描述

(7)为枚举类型编写文档时,要确保在文档中说明常量,类型和任何公有的方法。

(8)包级私有的文档注释就应该放在一个称作package-info.java的文件中。

(9)对于有多个互相关联的类组成的复杂API,通常有必须要有一个外部文档来描述该API的总体结构,对文档注释进行补充。相关的类或者包文档注释就应该包含一 个对这个文档的注释

第八章 通用程序设计

第四十五条:将局部变量的作用于最小化

1.注意

(1)要让局部变量的作用域最小化,最有力的方法就是在第一次使用它的地方声明。

(2)几乎每个局部变量的声明都应该包含一个初始化表达式。(try-catch块例外)。

(3)for循环就优先于while循环,因为while循环可能会用到循环外的变量。

(4)应该让方法小而集中。

第四十六条:For-each循环优先于传统的for循环

1.Foreach循环的优点

(1)可以用来遍历任何实现了Iterable接口的对象。

(2)若编写一个类Collection,最好让它实现Iterable。就能够用foreach遍历整个数组

(3)具有简洁性,还可以预防bug

2.不能使用for-each循环的三个情景

(1)过滤:如果需要遍历集合并删除元素,则需要显式迭代器

(2)转换/取代:

(3)平行迭代:需要显式迭代器控制同步前移

第四十七条:了解并使用类库

1.使用标准类库的优点

(1)可以充分利用这些编写标准类库的专家的知识,以及前人使用经验

(2)不必浪费时间为那些与工作不太相关的问题提供特别的解决方案

(3)可以让自己的代码融入主流,更易读易维护。

(4)在每个重要的发行版本,都有很多新的特性加入类库。与新特性保持同步是很重要的。

第四十八条: 如果需要精确的答案,请避免使用float和double

1.注意

(1) 如果你想系统的记录十进制小数点, 并不介意因为不是用基本类型带来的不便, 请使用BigDecimal,

(2)使用BigDecimal有一个好处, 就是允许你完全控制舍入.

(3)使用bigDecimal有两个缺点: 不方便 很慢

第四十九条:基本类型优先于装箱基本类型

1.基本类型与装箱基本类型的区别

(1)基本类型只有值,装箱基本类型除了值,还有同一性。两个装箱基本类型可以具有相同的值和不同的同一性

(2)基本类型只有功能完备的值。而装箱基本类型除了功能值以外,还有一个非功能值null

(3)基本类型通常比装箱基本类型更节省时间和空间

2.装箱基本类型的性质

(1)对装箱基本类型运用==操作符几乎总是错误的

(2)当在一项操作中混合使用基本类型和装箱基本类型时,装箱基本类型就会自动拆箱。Null引用被拆箱时,就会得到空指针异常,

(3)使用装箱基本类型,会导致明显性能下降。

3.适合使用装箱基本类型的时候

(1)作为集合中的元素,键和值。

(2)使用反射调用时,必须使用装箱基本类型。例子:Hibernate和Servlet的反射

(3)用作参数化类型。

第五十条: 如果其他类型更合适,则避免使用字符串

1.注意

(1)字符串不适合代替其他的值类型。

(2)字符串不适合代替枚举类型。

(3)字符串不适合代替聚集类型。更好的做法是:使用私有静态类类收集数据。

(4)字符串也不适合代替能力表。字符串无法保证一个惟一的键。

第五十一条: 当心字符串连接的性能

1.注意

(1)为连接n个字符串而重复地使用字符串连接操作符,需要O(n^2)的时间。这是由于字符串不可变,当两个字符串被连在一起时,它们的内容都要拷贝。

(2)为了获得可以接受的性能,请使用StringBuilder代替String,来储存字符串。StringBuilder需要O(n)的时间

第五十二条: 通过接口引用对象

1.注意

(1)如果有合适的接口类型存在,那么对于参数,返回值,变量和域来说,都应该使用接口类型进行声明。

例:

List<subscriber> subscribers = new Vector<subscriber>();

(2)如果原来的实现提供了某种特殊的类型,而这种功能并不是这个接口的约定所要求的,并且代码又依赖于这种功能,新的功能也要提供同样的功能。

第五十三条: 接口优先于反射机制

1.注意

(1)核心反射机制提供了“通过程序来访问关于已装载的类的信息”的能力。

(2)反射机制允许一个类使用另一个类,但付出了代价

丧失了编译时类型检查的好处,包括异常检查

执行反射访问所需要的代码非常笨拙和冗长

性能损失。反射调用比普通方法慢很多,具体受多个因素的影响。

(3)普通应用程序在运行时不应该以反射访问对象

第五十四条: 谨慎地使用本地方法

1.本地方法的定义

指的使用本地程序设计语言来编写的特殊方法。

2.本地方法的用途

(1)提供访问特定平台的功能。例如注册表或者文件锁

(2)提供访问遗留代码库的能力。

(3)用本地语言编写应用程序中注重性能的部分,以提高系统性能

(4)使用本地方法来提高性能的做法不值得提倡。

3.本地语言不是安全的

使用本地语言无法保证不出现内存毁坏

4.本地语言降低了可移植性,更难调试。

一个bug就可能破坏整个应用程序

第五十五条:谨慎的进行优化

1.注意

(1)不要为了计较性能而牺牲了合理的结构。努力写好的成熟的程序,而不是快的程序

(2)努力避免限制性能的设计决策。多用复合,少用继承。

(3)为了获得好的性能而对API进行包装,是非常不好的想法:API的性能会在后续 得到修复,但是包装API问题会永久存在

(4)每次做优化之前和之后,要对性能进行测量。要善用性能剖析工具,改善平方级算法

(5)在Java平台上测量性能更重要,因为Java平台没有很强的性能模型,各种基本操作的相对开销也没有明确定义。要在不用的JVM或者硬件上进行测试。

第五十六条: 遵守普遍接受的命名惯例

1.注意

(1)包的层次应该用句号分割,每个部分包括小写字母和数字。用户创建的包应该用组织的Internet域名作为顶级域名。决不能使用Java和Javax。

(2)包的名称的其余部分应该比较简短,通常不超过8个字符,使用有意义的缩写。

(3)大型工具包可以使用非正式的层次结构

(4)类和接口的名称应该包含一个或多个单词,首字母大写。应该尽量避免缩写。

(5)方法的第一个单词应该小心

(6)常量的所有字母都应该大写,并且用下划线区分。枚举域也是常量

(7)类型参数名称通常由单个字母组成。T表示类型,E表示元素集合,K和V表示键和值,X表示异常

(8)返回boolean值的方法,其名称应该用is开头

(9)如果方法返回被调用对象的一个非boolean的属性,应该使用名词/名词短语/带get的名词来做名称。JavaBean强制使用第三种方法

(10)转换对象的方法应该使用toType类型;返回视图的方法应该是AsType

(11)域的名称不太重要。最好不要暴露域

本人才疏学浅,若有错误,请指出 谢谢!

转载请注明原文地址: https://www.6miu.com/read-46539.html

最新回复(0)