《Effective Java 中文版》- 20240715

1. 用静态方法替代构造器(五个优点、两个缺点)。

相比于构造器,优点如下:

  • 有名称;
  • 不用再每次调用时返回一个新的对象;
  • 可以返回声明类型的子类型的对象;适用于基于接口的框架;
  • 每次调用时,可以基于不同的参数返回不同的对象实例;
  • 所返回对象的类并不一定要存在;这种灵活的静态工厂方法提供了灵活的服务提供者框架;
    相比于构造器,缺点如下:
  • 如果没有公共的或者受保护的构造器,就不能为其生成子类;

子类构造器会自动调用父类构造器,所以当父类没有构造器为其调用时,就没有办法创建子类;

  • JavaDoc生成文档时,对其展示没有构造器明显;
2. 当构造函数的参数比较多时使用生成器。
  • Builder类作为使用类的静态内部类,其字段为外部类可能需要的构造参数;
  • 可以防止复杂对象在构造过程中被破坏;
  • 同一个生成器可以用来生成多个相同的对象(享元);
3. 利用私有构造器或者枚举类型强化Singleton属性;
public class Elvis  {
    public static final Elvis INSTANCE = new Elvis();
    private Elvis() {...}
    // ...
    // 第二种
    private static final Elvis INSTANCE = new Elvis();
    public static Elvis getInstance () { return INSTANCE; }

    // 此处的作用:防止Singleton的对象序列化后,再反序列化会生成新的对象,
    // 破坏了Singleton的设计;
    private Object readResolve() {
        return INSTANCE; // 让GC处理假的Elvis对象;
    }
}
4. 利用私有构造器防止类被实例化;(上面👆的代码有示例)
5. 优先考虑通过依赖注入来链接资源;

对于行为会被底层资源以参数化的方式影响的类而言,静态工具类和Singleton类都不合适;

// 依赖注入:即将依赖资源通过参数的形式传递到类的构造器中
public class SpellChecker {
    private final Lexicon dictionary;
    public SpellChecker(Lexicon dictionary) {
        this.dictionary = Object.requiredNonNull(dictionary); 
    }
}
6. 避免创建不必要的对象;
  • 比如正则处理会生成Pattern对象,对性能要求高的场景,可以预先生成Pattern对象;
  • 自动装箱也会生成可能不必要的对象;所以优先使用基本类型而不是其封装类,并提防无意中的自动装箱;
7. 及时清理掉过期的引用;
8. 承接上一点,避免使用终结(finalizer)方法和清理(cleaner)方法;
  • 两个方法清理不够及时,甚至不保证会运行;
  • 终结过程中发生的异常会被忽略,导致其他对象可能处于损坏状态;
9. 与try-finally相比,首选try-with-resources;
10. 重写equals方法;

需要遵守五个约定:

  • 自反性:x.equals(x);
  • 对称性:x.equals(y) 与 y.equals(x)结果相同;
  • 传递性:x.equals(y) && y.equals(z) => x.equals(z);
  • 一致性: 无论何时调用,x.equals(y)的结果不变;
  • 非空性:x.equlas(null) 返回false;
    编写高质量equals方法的技巧:
  • 使用 ==检查参数是否指向当前对象的引用;
  • 使用 instanceof 运算符检查参数是否具有正确的类型;
  • 将参数转换为正确的类型;
  • 对于类中每个“重要”的字段,检查参数的这些字段是否与当前对象相应字段匹配;
11. 重写equals方法时应始终重写hashcode方法;
  • equals相等,hashcode一定相等;
  • hashcode相等,equals不一定相等;
  • hash算法应尽可能返回分散的散列值,以避免hash冲突,导致检索性能较低;
12. 总是重写toString() 方法;
  • 可以增强debug或者日志中类的可读性;
13. 谨慎重写clone方法;
  • clone的实现可能会是一个浅拷贝;
  • clone充当了构造器的作用,必须确保它不会对原始对象造成伤害,并确保它在克隆体上正确建立不变式;
  • 大多数情况,提供复制构造器会是更好的选择;
public class Yum {
    public Yum(Yum yum) {
    // ...
    }
}

14. 考虑实现Comparable接口;

  • 当类可能需要一些比较操作时可考虑实现;
  • 尽量使用系统提供的基于静态的比较器,或者基于比较器构造方法的比较器;
// 基于静态的比较器
static Comparator<Object> hashCodeOrder = new Comparator<>() {
    public int compare(Object o1, Object o2) {
        return Integer.compare(o1.hashCode(), o2.hashCode());
    }
}
// 基于比较器构造方法
static Comparator<Object> hashCodeOrder = Comparator.compareInt(o -> o.hashCode());
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容