Effictive Java

最近看了一本书,叫作《Effictive Java中文版》,是Joshua Bloch著,潘爱民翻译的。其实下载这本书已经好久了,可是因为本身一直忙于其余事情,就一直没有看。有一天忽然又翻到,因此就从头看了一下。java

我的感受这本书和Effictive C++很是的类似,都是从实例的角度出发,为编程者提供了不少高效,或者说规范编程的建议。刚开始看这本书的时候感受有种醍醐灌顶的感受,毕竟本身是个学生,实际项目经验不是很丰富,但依旧感受收益匪浅。以后看下去的时候,感受就是有种解读java设计思想的感受了,由于后面提到的好多东西,其实在缺少开发经验的前提下并非能有很大的收获。本人日常看书有总结的习惯,老是喜欢用几句话来总结本身看过的内容,因此在这里将本身写的东西记录一下,以供后期再次回味。程序员

第二章 建立和销毁对象
1. 静态工厂和构造函数,在设计的时候能够考虑用静态工厂方法来替代构造函数编程

2. singleton从大的方面能够分为饿汉式和饱汉式两种方式
饿汉式确保了该类永远是一个singleton,而饱汉式其实还保留了一点余地
singleton在序列化的时候须要提供一个readResolve,不然在每次反序列化的时候,都会建立一个新的实例数组

3. 有些类并不想被实例化,而是仅仅做为工具类的存在,好比Arrays,或者Collections等
这种类在编写的时候能够用私有构造函数来强化不可实例化的能力
例如:Arrays的构造函数是 private Arrays() {}浏览器

4. 避免建立重复的对象,若是一个对象是不可变的,并且建立时耗时或者消耗内存的,应该尽可能去复用,而不是建立安全

5. 消除过时的对象引用,当一个对象再也不被使用时,应该尽可能清楚他内部全部对象的引用多线程

6. 避免使用终结函数,终结函数(finalize)的执行时间以及是否执行是不可肯定的
在须要释放某些资源的时候,应该尽可能使用try-finally结构来处理,而不能寄但愿于finalize函数并发

第三章 对于全部对象都通用的方法
7. 在改写equals方法的时候要注意该方法的一些特性:自反性,对称性,传递性,一致性等 框架

8. 改写equals时老是要改写hashCode,由于Object类规定,相等的对象必须具备相等的散列码(hash code)函数

9. 老是要改写toString,建议全部的子类都改写这个方法,从而获取本身想要的信息

10. 谨慎地改写clone, object的clone方法是protected的,是被保护的。
实现克隆接口的时候或者进行拷贝的时候,要注意深度拷贝和浅拷贝的区别
还能够实现一个拷贝构造函数或者静态工厂方法的变形

11. 考虑实现Comparable接口,与equals方法很是类似,但也有一些不一样
不须要检查实参的类型,若是类型不合适,会抛出ClassCastException异常
若是实参是null,会抛出NullPointerException异常

第四章 类和接口
12. 使类和成员的可访问能力最小化,尽量地下降可访问性

13. 支持非可变性:一个非可变类是一个简单的类,它的实例不能被修改
坚定不要为每个get方法编写相应的set方法,除非有很好的理由要让一个类变成可变类
即便一个类不是非可变类,那么也应该尽可能限制它的可变性

14. 复合优先于继承,与方法调用不一样,继承打破了封装性,子类依赖于超类的功能实现,超类变了,子类就必需要变化
继承:在一个包的内部使用继承是很是安全的
或者专门为了继承而设计,而且具备良好文档说明的类,使用继承也是很好的
复用:不是扩展一个已有的类,而是在新的类中增长一个私有域,引用了已有类的一个实例。其实相似于一个包装类

在java中,stack并非vector,所以应该使用复用,而不是继承
一样的,属性列表也不是一个散列表,所以Properties不该该扩展Hashtable

继承会把超类中的缺陷传播到子类中,而复合能够设计新的API,从而隐藏这些缺陷

15. 要么专门为继承而设计,并给出文档说明,要么禁止继承
为了继承而设计一个类,要求对这个类有一些实质性的限制,这并非轻松能够决定的

防止类被子类化:1——把这个类声明成final的
2——把全部的构造函数声明为私有的,或者说是包级私有的

16. 接口优于抽象类:接口使得咱们能够构造出非层次结构的类型框架
接口使得安全地加强一个类的功能成为可能,经过包装类的模式
抽象类能够用来作为一个骨架实现类,而后用各类接口来进行具体功能的实现和扩展
抽象类的演化比接口的演化要容易得多,能够更加方便地增长新的方法

17. 接口只是被用于定义类型的:一个类实现了一个接口,这个接口就是一个类型,经过此类型能够引用这个类的实例
常量接口,包含了不少静态的final域,实现该接口的类均可以使用这些常量(不建议这样用)
接口应该只是被用来定义类型的,不该该被用来导出常量

18. 优先考虑静态成员类,嵌套类有四种:静态成员类,非静态成员类,匿名类,局部类

静态成员类:外围类的一个成员,能够访问外围类的全部成员
非静态成员类:每个实例都隐含着与外围类的一个外围实例紧密关联在一块儿,会浪费时间和空间
局部类:在能够声明局部变量的地方,能够声明局部类。与成员类同样,局部类有名字,可被重复使用
匿名类:没有名字,不是外围类的一个成员。在被使用的点上被声明和实例化,能够出如今任何容许代码出现的地方,好比Thread,Runnable实例

第五章 C语言结构的替代
19. 用类代替结构,类能够把数据封装到一个对象中,而且只有对象的方法才能够访问这些数据。
公有类不该该直接暴露数据域,而应该使用get和set函数等

20. 用类层次来代替联合(union),能够定义一个抽象类,而后扩展其余类来实现功能
类层次提供了类型安全性,并且代码更加简洁,容易扩展,反映类型之间本质上的层次关系

21. 用类来代替enum结构,java中存在一种类型安全枚举,定义一个类来表明枚举类型的单个元素
对于任何枚举类型的需求都应该相对较少,由于随着子类化技术的推广,这些类型的主要用途已通过时了。

22. 用类和接口来代替函数指针
C语言的函数指针主要用途是实现Strategy(策略)模式。java中能够用一个接口表示该策略,并声明一个实现该接口的类

第六章 方法
23. 检查参数的有效性
应该在方法体的起始处对参数进行检查,非公有的方法一般应该使用assertions(断言)来检查它们的参数
编写方法或者构造函数的时候,应该考虑参数有哪些限制

24. 须要时使用保护性拷贝
保护性拷贝动做是在检查参数的有效性以前进行的,并且有效性检查是针对拷贝以后的对象,而不是原始的对象

25. 谨慎设计方法的原型
1——谨慎选择方法的名字
2——不要过于追求提供便利的方法:每个方法都应该提供其应具有的功能点。不要定义太多的方法
3——避免长长的参数列表:三个参数应该被看做实践中的最大值,参数应该越少越好
4——对于参数类型,优先使用接口而不是类:这样方便后来的扩展
5——谨慎地使用函数对象

26. 谨慎地使用重载
重载方法的选择是静态的,并不能保证重载的函数执行的是本身逻辑上想要他执行的那一个
一个安全保守的策略是,永远不要导出两个具备相同参数数目的重载方法

27. 返回零长度的数组而不是null
返回null会致使程序员须要额外的代码来处理这种状况

28. 为全部导出的API元素编写文档注释
java语言环境提供了一个被称为javadoc的实用工具,可根据源代码自动产生API文档
在文档注释内部能够出现任意的HTML标签,可是HTML元字符必需要通过转义

第七章 通用程序设计

29. 局部变量的做用域最小化
c程序设计语言要求局部变量必须被声明在一个代码块的开始处
java程序语言容许在任何能够出现的地方声明变量,提升代码的可读性

30. 了解和使用库
了解标准库的函数和使用方法,会让你的代码变得更加简单
不要从头发明轮子,若是你要作的事情看起来很常见,可能库中已经封装了相应的函数

31. 若是要求精确的答案,请避免使用float和double
float和double类型的主要设计目标是科学计算和工程计算,对于货币计算很是不合适
精确计算应该更多的使用BigDecimal,int或者long等进行货币计算

32. 若是其余类型更加适合,则尽可能避免使用字符串
字符串不适合代替枚举类型,汇集类型,能力表等

33. 了解字符串链接的性能
字符串链接符并不适合规模比较大的情形,尽可能使用StringBuffer

34. 经过接口引用对象
例如:List subscribers = new Vector() 使用接口转变会更加的灵活,特别是工厂模式

35. 接口优先于反射(reflection)机制
反射机制能够经过程序来访问已装载的类的信息
复杂的应用程序须要使用反射机制,包括浏览器,对象监视器,代码分析工具,内嵌的解释器等

反射机制的缺点:1. 损失了编译时类型检查的好处,可能会调用不存在的方法
2. 要求执行映像访问的代码很是笨拙和冗长
3. 性能损失,耗时多是普通方法的几十倍

36. 谨慎地使用本地方法(JNI)
使用本地方法主要有几种好处:1. 访问与平台相关的设施,好比注册表和文件锁等
2. 访问老式代码库以及老式数据等
3. 使用本地的方法,从而提升性能等
使用本地方法的缺点:1. 使用本地的语言多是不安全的
2. 因为本地方法是平台相关的,所以使用了本地的方法,程序也再也不是可自由移植的
3. 对于每个平台,本地代码须要从新编译
4. 在进入和退出本地代码时,须要较高的程序开销

37. 谨慎地进行优化
在每次试图作优化以前和以后,应该对性能进行测量

38. 遵照广泛接受的命名惯例
字面的:主要涉及到包,类,接口,方法和域等的命名,如大小写习惯等
语法的:类一般用一个名词或者名词短语命名(接口相似)
执行某个动做的方法一般用一个动词或者动词短语来命名

第八章 异常
39. 只针对不正常的条件才使用异常(永远不该该被用于正常的控制流)
异常的设计初衷是用于不正常的情形。因此建立,抛出和捕获异常的开销都是很昂贵的

40. 对于可恢复的条件使用被检查的异常,对于程序错误使用运行时异常
java中有三种可抛出结构:1.被检查的异常(checked exception),可恢复的条件
2.运行时异常(run-time exception),程序错误
3.错误(error),每每是不可恢复的情形

41. 避免没必要要地使用被检查的异常

42. 尽可能使用标准的异常
重用现有的异常:1.使得你的API更加易于学习和使用
2.对于用到这些API的程序而言,它们的可读性更好
3.异常类越少,意味着内存占用越小,装载类的开销也越小

43. 抛出的异常要适合于相应的抽象
底层异常的处理:1.可用使用异常转译来处理
异常转译:高层的实现应该捕获底层的异常,同时抛出一个能够按照高层抽象进行解释的异常
2.若是能够的话,应该在处理来自底层异常的时候,确保它们会成功地执行

44. 每一个方法抛出的异常都要有文档
使用javadoc的@throws标记,准确地记录下每一个异常被抛出的条件
若是一个类中的许多方法因为一样的缘由而抛出同一个异常,那么在该类的文档注释中作文档是能够的

45. 在细节消息中包含失败-捕获信息
异常类型的toString方法应该尽量多地返回有关失败缘由的信息
一个异常的字符串表示应该包含全部“对该异常有贡献”的参数和域的值
栈轨迹一般包含了异常被抛出的确切文件和行数

46. 努力使失败保持原子性
一个失败的方法调用应该使对象保持在“它被调用以前的状态”(失败原子性)
失败原子性得到的办法:1.设计一个非可变的对象,操做失败将会阻止建立新的对象
2.对计算处理过程调整顺序,使得任何可能失败的计算部分都发生在对象状态被修改以前
3.编写一段恢复代码,使对象回滚到操做开始以前的状态
4.在对象的一份临时拷贝上执行操做,操做完以后再复制给原来的对象

47. 不要忽略异常
空的catch块会使异常达不到应有的目的。忽略异常会致使程序在遇到错误的时候继续悄然地执行下去

第九章 线程

48. 对共享可变数据的同步访问
在使用多个线程共享可变数据的时候,每一个读或者写数据的线程必须得到一把锁


49. 避免过多的同步
过多的同步可能会致使性能下降,死锁,甚至不肯定的行为
一般,在同步区域内应该作尽可能少的工做。得到锁,检查共享数据,变换数据,释放锁。
为了不死锁和数据破坏,千万不要在同步区域内部调用外来方法

50. 永远不要再循环的外面调用wait
object.wait方法是使一个线程等待某个条件,必定是在同步区域中被调用的
在选择notify仍是notifyAll的时候,老是建议使用notifyAll唤醒全部线程,让线程去检测是否知足条件
老是在一个while循环中调用wait,而且使用标准模式

51. 不要依赖于线程调度器
线程调度器决定哪一个线程运行以及运行时间。但不一样的JVM可能会有区别,依赖于调度器的多线程多是不可移植的
健壮的,相应良好的,可移植的多线程调度器应该确保在任什么时候刻只有少许的可运行线程
让每一个线程作少许的工做,而后使用object.wait等待条件或者Thread.sleep睡眠一段时间
线程优先级是java平台上最不可移植的特征,所以尽可能不要使用这个方法来改善应用程序
不要试图经过Thread.yield来修正程序,由于不一样的JVM对于yield有不一样的反映

52. 线程安全性的文档化
一个类可能支持的线程安全性级别:1.非可变的,类的实例是不变的,如String,Integer,BigInteger等
2.线程安全的,类的实例是可变的,但全部方法都包含足够的同步机制
3.有条件的线程安全,这个类的某些方法必须被顺序调用
4.线程兼容的,在每一个方法调用的外围使用外部同步
5.线程对立的,这个类不能安全地被多个线程并发使用,这样的类或者方法在java中很是少

53. 避免使用线程组
线程组容许你把Thread的基本功能直接应用到一组线程上
线程组基本上已通过时了,不建议使用

第十章 序列化
序列化是将一个对象编码成一个字节流,反序列化则是将字节流转变为对象
对象被序列化以后能够从一个正在运行的虚拟机传递到另外一个虚拟机上,或者存储到磁盘中

54. 谨慎地实现Serializable
应该设计一个高质量的序列化形式,否则后期改变这个类会很麻烦
默认的序列化会致使类中私有的和包级私有的实例域都变成导出API的一部分
一个类实现了Serializable后,随着新版本的发行,相关的测试负担增长了
实现Serializable是一个很严重的承诺(除非该类一段时间后会被抛弃)

55. 考虑使用自定义的序列化形式
理想的序列化形式应该只包含该对象所表示的逻辑数据。
若是没有认真考虑默认序列化形式是否合适,则不要接受这种形式
即便确认了默认序列化形式合适,也须要一个readObject方法以保证约束关系和安全性
无论选用了哪一种序列化形式,都要为每一个可序列化的类声明一个显式的序列版本UID
一个对象的物理表示与他的逻辑数据内容有实质差异时,默认的序列化有4个缺点:
1. 使这个类的导出API永远束缚在该类的内部表示上
2. 消耗过多的空间,物理表示和逻辑数据都要存储起来
3. 消耗过多的时间,序列化逻辑须要昂贵的图遍历
4. 有可能引发栈的溢出,默认的序列化过程会对对象图进行一次递归遍历

56. 保护性地编写readObject方法
readObject方法至关于一个构造函数。所以也须要检查参数的有效性以及必要时对参数进行保护性拷贝
保护性拷贝应该是在有效性检查以前进行的

57. 必要时提供一个readResolve方法 若是一个类定了readResolve方法,在反序列化的时候,新建立的对象会调用这个方法,而这个新建立的对象再也不有用 readResolve方法不只对singleton对象是必要的,对于其余的实例受控的类也是必须的 readResolve方法能够替代保护性的readObject方法

相关文章
相关标签/搜索