Java Effective 读书笔记

  最近闲着花费了接近两个多月的时间,把《java effective》看完了,收益颇多,《Java effective》这本书确实值得java程序员细读,可以尝试思考一下如何编写更好的代码、java语言如何进行设计和java规范潜在意义。这些都是值得思考和探究的。

创建和销毁对象:

1.静态工厂方法:考虑用静态工厂方法代替构造器
   优点:
    1.可以自由命名;
    2.不必在每次调用它们的时候都创建一个新对象;
    3.可以返回原返回类型的任何子类的对象;
    4.在创建参数化实例的时候,可以简化代码
   缺点:
    1.类如果不含有公有或受保护的构造器,就不能被子类化;
    2.它们与其他的静态方法实际上没有任何区别

2.构建器:如果类的构造器或者静态工厂中遇到多个参数时要考虑用构建器。在类的内部定义内部类作为参数载体,通过build构建。

3.用私有构造器或者枚举类型强化Singleton属性。

4.通过私有构造器强化不可实例化的能力,特别是帮助类,工具类等添加私有构造方法。

5.避免创建不必要的对象。对象的重用和基本类型的装箱拆箱。

6.消除过期的对象引用。例如:关闭数据流,断开数据库连接,置空大对象。

7.避免使用终结方法finalizer()。对于所有对象都通用的方法。

8.覆盖equals时请遵守通用约定。equals约定:自反性,对称性,传递性,一致性。

9.覆盖equals时总要覆盖hasCode。

10.始终要覆盖toString。

11.谨慎地覆盖clone。

12.考虑实现Comparable接口。

类和接口

13.使类和成员的可访问性最小化。访问权限:private,default,protected,public。

14.在公有类中使用访问方法而非公有域。公有类中字段设为私有,通过方法访问。

15.使可变性最小化:不要提供任何修改对象状态的方法;保证类不会被扩展;使所有的域都是final的;
使所有的域都成为私有的;确保对于任何可变组件的互斥访问。

16.复合优于继承。为继承而设计的必须提供文档说明,否则就禁止继承。
禁止继承方法有:加final修饰;构造器私有化,对外提供静态工厂。

17.接口优于抽象。接口只用于定义类型,方便作为参数传递。

18.类层次优于标签类。抽取公共部分,去除类型条件,采用子类实现不同策略。

19.优先考虑静态成员类,对外提供公有的字段或属性。
公有静态成员类:作用规则跟其他的静态成员相似,常做为外部类的公有辅助类。
私有静态成员类:依赖外部类实例。当不需要访问外部类时,在外部类内作为组件存在。
匿名类:在使用时同时被声明和实例化,只有在非静态环境中才有外部类实例。常用作:动态创建函数对象;创建过程对象;用在静态工厂方法的内部。
局部类:作用规则跟其他的局部变量相似,只有在非静态环境中才有外部类实例。

泛型

20.请不要在新代码中使用原生态类型。如果使用原生态类型,就失掉了泛型在安全性和表述性方面的所有优势。泛型有子类化的规则。无限制通配符类型为 Set<?>,代表任何元素类型,除了null之外。

21.列表优先于数组。数组是协变且可以具体化的。泛型是不可变且可以被擦除的。因此泛型只在编译时强化类型信息,并在运行时丢弃类型信息。擦除就是使泛型可以与没有使用泛型的代码随意进行互用。

22.优先考虑泛型,优先考虑泛型方法。
数组与泛型数组:第一种创建一个Object的数组,将它转换成泛型数组;第二种创建一个Object的数组存储数据,在使用时强制转换成泛型类型;第三种通过Array.newInstance(Class<?> componentType, int length);创建泛型类型数组。

23.利用有限制通配符来提升API的灵活性。PECS:producer-extends,consumer-super。

24.优先考虑类型安全的异构容器。用Class对象作为类型令牌。Class.cast();检验参数是否为Class对象表示的类型实例。Class.asSubclass();将调用它的Class 对象转换成其参数表示的类的一个子类,失败抛出ClassCastException。

枚举和注解

25.用enum代替int常量,用实例域代替序数,用EnumSet代替位域,用EnumMap代替序数索引,用接口模拟可伸缩的枚举。
枚举类型要用在集合(Set)中使用EnumSet存储操作。
位域(bit field):用OR位运算将几个常量合并到一个集合中。例如:public static final int BOLD=1<<3;

26.注解优先于命名模式,坚持使用Override注解,用标记接口定义类型。
命名模式(naming pattern):使用名称来表明程序元素需要通过某种工具或者框架进行特殊处理。
标记接口(marker interface):是指没有包含方法声明的接口,而是指明或者标明一个类实现了具有某种属性的接口。
例如:JUnit测试方法以test开头。
命名模式的缺点:1.名称拼写错误;2.无法确保它们只用到相应的程序元素上;3.没有提供将参数值与程序元素关联起来的好方法。

方法

27.检查参数的有效性,必要时进行保护性拷贝,谨慎设计方法签名,慎用重载,慎用可变参数。

28.返回零长度的数组或者集合,而不是null。为所有导出的API元素编写文档注释。

通用程序设计

29.将局部变量的作用域最小化。
局部变量声明:在第一次使用它的地方声明。

30.for-each循环优先于传统的for循环。

31.如果需要精确的答案,避免使用float和double,而是使用BigDecimal。

32.基本类型优先于装箱基本类型。装箱和拆箱基本类型的混合类型计算时,会进行拆箱,当程序进行拆箱时会抛出NullPointerException异常。当程序装箱基本类型时会导致高开销和不必要的对象创建。

33.当心字符串连接的性能。通过接口引用对象。接口优先于反射机制。

34.谨慎地使用本地方法。谨慎地进行优化。遵守普遍接受的命名惯例。

异常

35.只针对异常的情况才使用异常。

36.对可恢复的情况使用受检异常,对编程错误使用运行时异常。

37. 避免不必要地使用受检的异常。

38.优先使用标准的异常。抛出与抽象相对应的异常。每个方法抛出的异常都要有文档。

39.在细节消息中包含能捕获失败的信息。努力使失败保持原子性。不要忽略异常。
失败原子性(failure atomic):失败的方法调用应该使对象保持在被调用之前的状态,具有这种属性的方法被称为具有失败原子性。

并发

40.同步访问共享的可变数据。避免过度同步。executor和task优先于线程。并发工具优先于wait和notify。
并发工具分三类:ExecutorFrameWork、并发集合(ConcurrentCollection)、同步器(Synchronizer)。
应该优先使用并发集合(ConcurrentXXMap,ConcurrentXXList,ConcurrentXXQueue),再使用外部同步集合(Collections.synchronizedMap)或者并发集合(Hashtable)。
在使用wait和notify时,务必确保从while循环内部调用wait。一般情况优先使用notifyAll,而不是使用notify。

41.线程安全性的文档化
线程安全性的级别: 不可变的(immutable),String;无条件的线程安全(unconditionally thread-safe);有条件线程安全(conditionally thread-safe);非线程安全(not thread-safe);线程对立(thread-hostile)。

42.慎用延迟初始化,不要依赖于线程调度器,避免使用线程组(ThreadGroup,存在很多问题)。

序列化
对象序列化:将对象编码成字节流。反序列化:将字节流编码重新构建成对象。

43.谨慎地实现Serializable接口。考虑使用自定义的序列化形式。保护性地编写readObject方法。
对象反序列化是通过readObject生成实例的,而不是通过构造函数。内部类不应该实现Serializable。编写显式序列化readObject方法,必须执行构造器所有有效性检查和保护性拷贝,如果检查失败,则抛出InvalidObjectException异常。无论是直接方式还是间接方式,都不要调用类中任何可被覆盖的方法。

44.对于实例控制,枚举类型优先于readResolve。考虑用序列化代理代替序列化实例。
尽可能地使用枚举类型来实施实例控制。如果非要用,必须提供一个readResolve方法,并确保该类的所有实例域都为基本类型或者是transient的。序列化代理模式的精髓就是反序列化不再是通过不可控的readObject()途径,而是通过正常的构造函数途径。

记于2018年3月20日

备注:
作者:Shengming Zeng
博客:http://www.cnblogs.com/zengming/

本文是原创,欢迎大家转载;但转载时必须注明文章来源,且在文章开头明显处给明链接。
<欢迎有不同想法或见解的同学一起探讨,共同进步>

原文地址:https://www.cnblogs.com/zengming/p/8593797.html