《Effective Java》阅读笔记-第二章

发布时间 2023-11-28 16:32:01作者: LuckyNeko

Effective Java 阅读笔记

第二章 创建和销毁对象

第 1 条:用静态工厂方法代替构造器

静态工厂方法优势:

  • 静态工厂方法有名称

静态工厂方法可以详细的指定名称,而使用构造器时如果没有文档会难以区分不同构造器之间的区别。

  • 不必每次调用的时候创建一个新对象

静态工厂方法可以缓存预先构建好的实例,或者直接返回之前的实例(如果需要的话)。

  • 可以返回任何类型的任何子类型对象

基于接口时,这会非常有用。既可以隐藏具体实现,又能返回需要的类型。

  • 返回对象可以随着每次调用发生变化

EnumSet类没有构造方法,只有静态方法,调用返回的时候会根据不同的枚举大小返回不同的内部类,而且以后也可以很好的新增或删除内部实现,这对调用者是不需要关心的。

  • 返回对象可以暂时不存在

这个在服务提供框架(Service Provider Framework)中很有用,返回对象由服务提供者提供,框架不需要考虑是什么类型。

JDK在1.6的时候也提供了ServiceLoader类,这是一个通用的服务提供处理。

静态工厂方法的缺点:

  • 如果需要的类没有 public 构造器或者 protected 构造器,就不能子类话该类。
  • 工厂类很难被发现

如果没有在API中明确注明,那么想实例化一个提供了工厂方法的类是比较难的。(深有同感)

静态工厂方法管用名称:

  • from:类型转换方法,接受单个参数并返回对应类型。
  • of:聚合方法,接受多个参数,并合并成对应类型。
  • valueOf:比fromof更繁琐的替代方法。
  • instancegetInstance:获取实例;
  • createnewInstance:获取实例,并且保证每次都是一个新实例。
  • get{Type}:获取实例,主要用于工厂类和实例类型不一样时使用,比如Files.getFileStore(path)
  • new{Type}:和get{Type}类似,用于返回新实例,例如:Files.newBufferedReader(path)
  • {type}get{Type}new{Type}的简化版,例如:Collections.list(...)

第 2 条 多个构造参数时有限考虑 Builder 建造器

工厂模式和构造器不能很好的扩展大量可选参数。

第 3 条 私有化构造器或者使用枚举类强化 Singleton 属性

单例可以使用饿汉、懒汉、双重检查锁、枚举来实现。

第 4 条 私有化构造器使类不可实例化

特指不要使用抽象类来禁止实例化。

第 5 条 优先考虑依赖注入来引入资源

能 DI 尽量 DI(依赖注入:Dependency Injection)。

第 6 条 避免创建不必要的对象

反例:String s = new String("blabla")

注意进行正则校验时,优先考虑创建公共的Pattern对象,避免使用静态方法Pattern.matches(),该静态方法每次都会创建一个Pattern,并且Pattern的创建是有一定负担的。

循环时优先使用基本类型,避免不必要的装箱。

第 7 条 消除过期的对象引用

给对象引用复制为 null 就可以消除引用,但是这里的消除引用并不是说局部变量用完之后赋值 null:

public void someMethod() {
    List<String> strList = new ArrayList<>();
    useStrMethod(strList);
    // 并不是指这种
    strList = null;
}

局部变量在方法结束时会自动解除引用,这里的过期引用特指缓存,否则很容易引发内存泄露。
比如在实现一个List时,内部使用数组缓存了对象,那个在调用removeLast方法时,不能仅仅把最大下标移动一下,而是要数组中引用的对象赋为null,这样删除的对象才能被GC。

第 8 条 避免使用终结方法(finalize)和清除方法(cleaner)

Object 上的 finalize 方法已经被标记为 removal,原本是被回收时会出发的方法,但是并不够保险,该方法现在已经被标记过时,不要使用。

cleaner 是 Java 9 加入的方法,加入的目的是代替finalize方法,这是一个使用例子:

Cleaner cleaner = Cleaner.create();
Cleaner.Cleanable cleanable = cleaner.register(anyObj, () -> {
    System.out.println("do clean");
});
cleanable.clean();

不同的垃圾回收算法会在不同时间进行垃圾回收,因此程序不应该依赖这种方法,并且终结方法有严重性能损失。
正常应该使用 AutoCloseable 接口,然后使用 try-with-resource 的方式进行释放。

第 9 条 try-with-resource 优先于 try-finally

当有多个 io 流是,try-finally 的关闭会非常繁琐,并且可读性不好。如果一个类需要被关闭,那么就可以实现 AutoCloseable 接口,然后使用try-with-resource。