EffectJava第三版
 
编码最基本原则
  1. 清晰性和简洁性最为重要 :组件的用 户永远也不应该被其行为所迷惑。 
  2. 组件要尽可能小,但又不能太小 [ 本书中使用的术语“纽 件”( Component),是指任何可重用的软件元素,从单个方法,到包含多个包的复杂框架, 都可以是一个组件 ]。
  3. 代码应该被重用,而不是被拷贝 。
  4. 组件之间的依赖性应该尽可能地降 到最小。
  5. 错误应该尽早被检测出来,最好是在编译时就发现并解决。
 
清晰、正确、可用、健壮、灵活和可维护的程序 => 性能表现优异
 
Java 语言支持四种类型:接口 (包括注释)、 类(包括 enum)、数组和基本类型 。
  1. 接口 (包括注释)、 类(包括 enum)、数组被称为引用类型( reference type),类 实例和数组是对象( object),而基本类型的值则不是对象。
  2. 类的成员( member)由它的域 ( field)、方法( method)、 成员类 ( member class)和成员接口( member interface)组成。
  3. 方法的签名(signature) 由它的名称和所有参数类型组成; 签名不包括方法的返回类型。
 
创建和销毁对象
  1. 静态工厂方法替换构造器  public static Boolean success(){ return Boolean.TRUE;}
        优点:
    1. 有名称能更确切地描述被返回的对象
    2. 不必每次调用重新创建可以利用缓存
    3. 可以返回原类型的任意子类型(更灵活)
    4. 返回的对象可随着参数变更每次调用而变化
    5. 方法返回的对象所属的类,可以在编写时不存在  服务提供者框架( Service Provider Framework)例如JDBC Spi DI(依赖注入)
         缺点:
    1. 类不含有公有或受保护的构造器,就不能子类化
    2. 难以找到(解决方案, 类和接口的注释中关注,遵循命名规范 from/of/create/newInstance/valueOf/getType)
  1. 多个构造参数,考虑构造器
    1. 重叠构造器模式可行(多个参数不同的构造器),编写困难,阅读困难
    2. JavaBeans 模式(先构造对象,再设置对象值),构造过程中对象状态不一致, 对象无法成为不可变(存在set方法)
    3. Builder模式  build的设值方法返回 builder 本身,以便把调用链接起来,得到一个流式的 API。
  2. 私有构造器/枚举类型强化Singleton属性
    1. 公有静态实例 私有构造器被调用一次 (简单清晰)
    2. 私有静态实例 公有静态工厂方法 (灵活可修改, 泛型工厂 作为Supplier<XXXInstance>)
      1. Singleton类序列化(transient且需要提供readResolve保证单例)
    3. 申明包含单元素的枚举类型 (通常是最方便的, 即利用enum实现了序列化 又保证了单例, 但只能用于扩展Enum不能extend其他类)
  3. 通过私有构造器强化不可实例化的能力
                工具类 包含一个私有构造 器,它就不能被实例化, 同样无法被继承子类化
  1. 优先考虑依赖注人来引用资源(灵活性、 可重用性和可测试性)
    1. 静态工具类和 Singleton 类不适合于需要引用底层资源的类 。
    2. 当创建一个 新的实例时 , 就将该资源传到构造器中,就是DI
    3. 依赖注入的对象资源具有不可变性
    4. 依赖注入适用于构造器/静态工厂/构建器
    5. 把实例工厂传入构造器,也是一种依赖注入
    6. 将这些资源 或者工厂传给构造器(或者静态工厂,或者构建器),通过它们来创建类
  2. 避免创建不必要的对象
    1. String = new String;; String = s 重用机制
    2. 优先使用静态工厂方法创建对象 而不是构造器
    3. String.matches正则匹配时, 每次创建pattern实例的成本,(1 使用静态对象预编译pattern 2 延迟加载 使用时初始化)
    4. 对象不变时 要安全重用(map.keyset多次调用返回相同实例,不必创建多个实例)
    5. 自动拆装箱, 优先使用基本类型, 小对象的拆装箱应该以提升程序的清晰性、简洁性和功能性
    6. 重量级对象才值得使用池化思想,例如数据库连接,否则占用内存 损害性能 代码复杂
    7. 保护性拷贝,重用对象代价过高,可能引发Bug与安全漏洞, 不必要的创建只会影响程序风格与性能
  3. 消除过期的对象引用
    1. Jvm会自动垃圾回收,同样需要考虑内存回收
    2. 内存泄漏会导致磁盘交换( Disk Paging),甚至导 致程序失败(OutOfMemoryError 错误)
    3. 栈的增长与收缩会保留对象的过期引用,正确做法 引用过期,清空设为null, 错误的引用也能抛出NPE
    4. 只要类是自己管理内存,程序员就应该警惕内存泄漏问
    5. 缓存常常发生过期引用的内存泄漏
    6. 监听器与其他回调只有注册没有取消,保存成 WeakHashMap中的键会变成弱引用,下个GC周期回收
  4. 避免使用终结方法和清除方法
    1. 终结方法( finalizer)通常是不可预测的
    2. Java9中清除方法(cleaner)认识不可预测的
    3. 永远不应该依赖终结方法或者清除方法来更新重要的 持久状态
    4. 使用终结方法和清除方法有一个非常严重的性能损失
    5. 安全问题  finalizer attack,对象异常应该不再继续,实现AutoCloseable
    6. 正确用途 
      1. 安全网,无法正常结束操作是,最终释放
      2. 本地对象的引用与释放 native peer object 本地对象的本地方法对象
      3. 除非是作为安全网,或者是为了终止非关键的本地资源,否则请不要使用 清除方法
  5. try-with-resources优先于try-finally
    1. try-finally过多嵌套 导致异常堆栈无法完全显示
    2. java7的try-with-resources时代码简洁易懂,容易诊断
 
    对象通用方法
 
  1. 覆盖equals的通用约定
           期望结果
    1. 类的每个实例都唯一
    2. 类本身无需提供逻辑相等
    3. 超累覆盖equals,超类的行为对于这个类也适用
    4. 私有类/包级私有类可以确保equals方法永远不会调用
          通用规范 实现逻辑相等时,覆盖equals方法
    1. 自反性  x . equals(x )必须返回 true
    2. 对称性  x . equals(y )则 y. equals(x )
    3. 传递性  x . equals(y )y . equals(z )则 x . equals(z )
    4. 一致性 多次调用 结果一致
    5. 非空性 所有对象都不等于null
          诀窍
    1. 使用==检查是否为当前对象引用
    2. 使用instanceof检查参数类型正确性
    3. 将参数转换为正确的类型
    4. 对于该类中的每个关键域匹配检查significant
    5. 覆盖 equals 时总要覆盖 hashCode
    6. 不要企图让 equals 方法过于智能
    7. 不要将 equals 声明 中的 Object 对象替换为其他的类型
  1. 覆盖 equals 时总要覆盖 hashCode
    1. 相等的对象必须具有相等的散列码( hash code)  31 * i = = ( i < < 5 ) – i
    2. 不要试图从散列码计算中排除掉一个对象的关键域来提高性能
    3. 不要对 hashCode 方法的返回值做出具体的规定,提供修改灵活性
  2. 始终要覆盖 toString
    1. 提供好的 toString 实现可以便类用起来更加舒适,使用了这个类的系统 也更易于调试。
    2. toString 方法应该返回对象中包含的所有值得关注的信息
    3. 无论是否决定指定格式,都应该在文档中明确地表明你的意图
    4. 为 toString 返回值中包含的所有信息提供一种可以通过编程 访问的途径(如json,具体格式)
  3. 谨慎地覆盖 clone
    1. 实现 Cloneable 接口的类是为了提供一个功能适 当的公有的 cl。ne 方法
    2. x.clone() != x
    3. x.clone().getζlass() == x.getClass()
    4. x.clone() .equals(x)
    5. 不可变的类永远都不应该提供 clone 方法
    6. clone 方法 就是另一个构造器; 必须确保它不会伤害到原始的对象, 并确保正确地创建被克隆对象中的 约束条件
    7. Cloneable 架构与 引用可变对象的 final 域的正常用法是不相兼容的
    8. 公有的 clone 方法应该省略 throws 声明
    9. 对象拷贝的更好的办法是提供一个拷贝构造器( copy constructor)或拷贝工厂( copy factory )。
  4. 考虑实现 Comparable 接口 :自反性、 对称性和传递性 与 弱等同性测试
    1. 确保所有的 x 和 y 都满足 sgn(x.compareTo(y) ) == – sgn (y.compareTo (x )) 
    2. 比较关系是可传递的: (x . compareTo ( y) > 0 & & y. compareTo (z) > 0 )暗示着 x . compareTo (z) > 0
    3. 确保 x. compareTo ( y) == 0 暗示着所有的 z 都满足 sgn(x .compareTo ( z) ) == sgn ( y . compare To ( z) )
    4. 建议(x . compare To (y) == 0) == (x . equals (y))
    5. 在装箱基本类型 的类中使用静态的 compare 方法,或者在 Comparator 接口中使用比较器构造方法(基础类型int hashcode可能溢出)
 
    类和接口
 
  1. 使类和成员的可访问性最小化
    1. 满足封装的基本原则,有效解耦
    2. 尽可能地使每个类或者成员不被外界访问
    3. 公有类的实例域决不能是公有的,包含公有可变域的类通常并不是线程安全的
    4. 让类具有公有的静态 final 数组域,或者返回 这种域的访问方法,这是错误的
  2. 要在公有类而非公有域中使用访问方法
    1. 如果类可以在 它所在的包之外进行访问,就提供访问方法
    2. 如果类是包级私有的,或者是私有的嵌套类, 直接暴露它的数据域并没有本质的错误
    3. 让公有类暴露 不可变的域,其危害相对来说比较小
  3. 使可变性最小化,不可变类
    1. 不要提供任何会修改对象状态的方法
    2. 保证类不会被扩展
    3. 声明所有的域都是 final 的
    4. 声明所有的域都为私有的
    5. 确保对于任何可变组件的互斥访问
    6. 操作返回新的不可变对象
    7. 使类成为 final的 / ,让类的所有构造器都变成私有的或者包级私有的,并添加公有的静态工厂( static factory)来代替公有的构造器
  4. 复合优先于继承
    1. 继承打破了封装性
    2. 只有当子类和超类之间确实存在子类型关系时,使用继承才是恰当的
    3. 如果子 类和超类处在不同的包中,并且超类并不是为了继承而设计的,那么继承将会导致脆弱性
    4. 可以用复合和转发机制来代替继承,尤其是当存在适当 的接口可以实现包装类的时候。 包装类不仅比子类更加健壮,而且功能也更加强大
    5. 例如 MyHashSet extends HashSet 的继承 与  ForwardingSet implements Set{ set s}, MyHashSet extends ForwardingSet