编程知识 cdmana.com

《Effective Java》笔记

2. 创建和销毁对象

1. 静态工厂方法替代构造器

优点:

  1. 名称清晰
  2. 每次调用不必new对象
  3. 可以返回原返回类型任意子类型对象
  4. 返回的对象可以随着调用而发生改变
  5. 返回的对象所属的类,在编写该静态工厂方法的类时可以不存在
    缺点:
  6. private 构造器导致,就不能有子类,子类构造器会默认访问父类构造器

2. 多个构造器参数时可以使用构建器(建造者模式 Builder)

印象比较深刻的是:之前写安卓用到了OkHttp,使用的OkHttp是用Kotlin写的,其中实例化对象用的就是这个建造者模式,当时以为是Kotlin链式调用的某种语法特性,后来才知道是设计模式

主要用于多参数时,避免重叠构造器和避免无参构造器创建对象依次set参数过程中JavaBean可能处于的不一致状态

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

Singleton常见实现方法:

  1. final修饰的公有静态成员
  2. 静态工厂
  3. 单元素Enum

通过放射调用私有构造器,可以修改构造器,创建第二个实例时抛出异常
序列化时除了实现 Serializable接口,还需要提供readResolve,防止反序列化创建新的实例

4. 私有构造器强化不可实例化

5. 优先考虑依赖注入来引用资源

SOLID 原则中的 D 依赖反转原则 (Dependency Inversion Principle),依赖注入是该原则的一种实现方式
创建一个新的实例时,就将该资源传到构造器中

6. 避免创建不必要的对象

  1. 不使用 new String() 方式创建String实例,使用 String s ="hello"; 方式,同一台虚拟机的代码,字符串字面常量相同,该对象就会被重用
  2. 复用创建成本较高的实例:正则Pattern实例
  3. 优先使用基本类型,自动装箱一定程度降低性能
    注意!在提倡保护性拷贝时,重用对象付出代价远大于创建重复对象

7. 清除过期的对象引用

  1. 栈pop时应将pop的对象设置为null
  2. 避免缓存内存泄漏的一种方式:WeakHashMap,除了WeakHashMap的键之外,如果没有存在对某个键的引用,会被自动删除
  3. 监听器和其他回调造成的内存泄漏:只保留他们的弱引用,例如保存成WeakHashMap的键

8.避免使用终结方法和清除方法

终结方法 (finalizer) 和 清除方法(cleaner JDK9) 都不可预测且会造成性能损失
注重时间的任务不应该使用这两种方法来完成
不应该依赖这两种方法来更新重要的持久状态(比如:释放共享资源,可能还没开始释放资源,系统就垮掉了)
TODO p25 终结方法攻击(finalizer attack)
TODO 合理用途:

  1. 安全网,忘记close
  2. 回收对象的本地对等体(native peer)

9. try-with-resources 优先于try-finalyy

实现了AutoCloseable 接口

  1. 优雅
  2. 避免底层物理设备异常导致第一个异常被第二个异常抹除,增加排错成本

3. 对于所有对象都通用的方法

10. 覆盖Equals时请注意遵守通用约定

不用覆盖的情况(满足其一即可)

  1. 类的每个实例本质都唯一
  2. 类无需提供逻辑相等功能
  3. 父类的equals方法足够满足使用
  4. 类是私有或者默认权限或确定不会调用到equals

覆盖equals的通用规范

  1. 自反性 非null值,x.equals(x) 为true 子类和父类不同的equals方法,相互equals会违反自反性
  2. 对称性 非null值,x.equals(y) 等于 y.equals(x)
  3. 传递性 非null值,x.equals(y) 为ture 且 y.equals(z) 为true -> x.equals(z)为true 子类相较于父类增加了一些用于equals的属性,但是子类还使用父类equals,违反传递性
  4. 一致性 非null值,值未修改,多次调用equals 结果应一致
  5. x非null值,x.equals(null) 返回 false

子类与父类 自反性和传递性的对立:无法再拓张可实例化的类的同时,既增加新的值组件,同时又保留equals约定

IDEA 默认子类equals写法就是:使用getClass() 比较对象,然后调用父类equals最后对比子类拓展的属性

Stream 初始化Set:

 private static final Set<Point> unitCircle = Stream.of(   new Point(1, 0),   new Point(0, 1),   new Point(-1, 0),   new Point(0, -1) ).collect(Collectors.toCollection(HashSet::new));

辨析:instanceof getClass()==

  • instanceof 这个对象是否为这个类或其子类的实例
  • getClass()== 运行时期对象的类

使用复合优于继承:提供私有Point域以及共有视图(view)方法

JDK反例:public class Timestamp extends java.util.Date,在同一个集合中使用或者其他方式混合使用,可能有不正确的行为

instanceof 第一个操作符为null 那么返回的一定为false,使用instanceof可以省略null判断

一致性,不要使equals方法依赖于不可靠的资源,JDK反例:URL equals

高质量equals诀窍

  1. == 检查是否为这个对象的引用
  2. instanceof 检查是否为正确类型(同时也可以排除掉null)
  3. 转换为正确的类型
  4. 检查每个关键域
    • 基本类型: ==
    • 浮点数:Float.compare(float,float) Double.compare(double,double) 使用Float.equals或Double.equals 自动装箱减低性能
    • 数组域:Arrays.equals
    • 合法null:Objects.equals(Object,Object) 避免抛出空指针异常
    • 顺序上按照:最有可能不一致或开销最小的域

注意点:

  1. 覆盖equals时总要覆盖hashCode
  2. 不要过度寻找等价关系,比如考虑别名形式
  3. 不要把equals参数定义为非Object 这样是重载而非重写

11. 覆盖equals时总要覆盖hashCode

  1. 同个对象多次调用hashCode返回同一个值
  2. equals(Object) 相等 hashCode返回整数也相等
  3. equals(Object) 不相等 hashCode 有可能相等

Object的hashCode方法为native方法:public native int hashCode();
hashCode注释提到:hashCode返回的是由对象存储地址转化得到的值

 As much as is reasonably practical, the hashCode method defined by class {@code Object} does return distinct integers for distinct objects. (This is typically imple.........

版权声明
本文为[程序猿欧文]所创,转载请带上原文链接,感谢
https://my.oschina.net/mikeowen/blog/4939345

Scroll to Top