# Effective Java笔记

## 静态工厂方法代替构造器

* 有名称，接口比构造器更易懂

> BigInteger.probablePrime

* 可以实现单例、对象池、内存重用

> 单例模式

* 可以返回return类型的子类，面向接口编程

  * 可以让返回的类非public

  * 可以通过不同的工厂方法返回不同子类

  > Java Collections Framework
* 利用类型推导，简化模板参数

  ```java
  Map<String, List<String>> m = HashMap.newInstance();
  ```
* 由于返回的子类型是private，就无法直接实例化这些子类型
* 静态工厂方法在Javadoc中没有辨识度
* 常用名称：

  > valueOf, of, getInstance, newInstance, getType, newType

## 构造参数多的时候，使用Builder

* M1: 多参数重载构造函数

  > 代码可读性差
* M2：Java Bean（setter，getter）
  * 简单
  * set过程中对象属性不完整
  * 不能单例化
* M3:Builder

  ```java
  public class Line {
    private final int length;
    private final int id;
    public static class Builder {
        private final int id;
        private final int length = 0;
        public Builder(int id) {
            this.id = id;
        }

        public Builder length(int len) {
            length = len;
            return this;
        }
        public Line build() {
            return new Line(this);
        }
    }
    private Line(Builder builder) {
        id = builder.id;
        length = builder.length;
    }
    public static void main(String[]args) {
        Line line = new Line.Builder(0).length(1).build();
    }
  }
  ```

  * 传递构造参数时像setter一样明确
  * build()调用构造器时，可以执行参数检查进行约束，在build之后才使用返回类，就能保证属性的完整性。
  * 可以向Builder传递模板参数，让它的build()方法返回任意类型
  * Class.newInstance调用无参构造函数，但无参构造函数不存在时，编译不会报错，而Builder的检查则弥补了这一点。

## 私有构造器或枚举类型实现单例

* M1：

  ```java
  public class Elvis {
    public static final Elvis INSTANCE = new Elvis();
    private Elvis() {}
    public void leaveTheBuilding(){}
  }
  ```

> 反射可以调用到私有的构造器

* M2:

  ```java
  public class Elvis {
    private static final Elvis INSTANCE = new Elvis();
    private Elvis() {}
    public static Elvis getInstance() {return INSTANCE;}
    public void leaveTheBuilding() {}
  }
  ```

> 灵活，可以通过修改getInstance()方法，决定是否返回单例对象

* 防止反序列化出错

  ```java
  private Object readResolve() {
    return INSTANCE;
  }
  ```
* 枚举单例（Java 1.5）

  ```java
  public enum Elvis {
    INSTANCE;
    public void leaveTheBuilding() {}
  }
  ```

> 简洁，序列化，防止多实例化，最佳方法

## 私有构造器强化不可实例化

只包含static method和static field的对象不希望被实例化，但实际上它有默认的无参构造器，我们仍然可能实例化它。

因此通过将构造器变为私有防止实例化。

```java
public class UtilityClass {
    private UtilityClass() {
        throw new AssertionError();
    }
}
```

这种方法的一个缺点是，这种工具类无法被子类化。

## 避免创建不必要的对象

* String

  ```java
  String s = new String("test");
  ```

  上面这种写法每次都会重新创建两个String对象

  ```java
  String s = "test";
  ```

  JTS 3.10.5保证了相同内容的字符串重用同一个对象，而且只创建一次。
* 延迟初始化
* 单例
* 对象池
* 多态层连接单例层，不需要为每个多态层创建多个单例层
* static执行开销大的代码块，存储执行结果重用
* 对于一个类中，创建开销大，创建后不修改，但会多次读取的对象，适合
* 但如果static创建的对象很少使用，可以考虑延迟初始化，但延迟初始化实现复杂，也会影响性能
* 有些对象初始化后可能改变，但改变后其功能是不变的，应当保持它为一个确定的引用，然后改变引用所指对象的内容，如Map中的keySet
* 优先使用基本类型（int）而非装箱基本类型(Integer)，性能优化
* 重对象才有必要尽可能避免创建，小对象可以由JVM很容易地构造和销毁，比自己维护对象池要好得多

## 引用泄露

java中没有内存泄露，只有引用泄露。比如一个Stack的实现：

```java
public class Stack {
    private Object[] elements;
    private int size = 0;
    // ...
    public Object pop() {
        return elements[--size];
    }

}
```

这里的pop操作只改变了size，而没有将elements\[size-1]的引用移除，Stack对象一直持有element的引用，应该改为：

```java
public Object pop() {
    Object result = elements[--size];
    elemets[size] = null;
    return result;
}
```

通过置null去掉stack中elements数组对element对象的引用

> 在这个例子中，由于Stack是自己在管理内存，存储池包含了对象引用单元（即elements数组）

需要警惕引用泄露的情形：

* 类中有对象引用单元
* 缓存
* 监听器与回调：bind而没有unbind，好的做法是只保存回调的弱引用。

## Avoid finalize

* Note
  * finalize不保证执行，尽量不要用
  * System.gc和System.runFinalization只是增加finalize执行的机会
  * finalize有严重的性能损失
  * 通过try - catch - finally来显式释放资源
* 合理用法
  * 作为显式释放资源的backup，或者check
  * 回收native peer
* 父类finalize
  * 显式调用super.finalize()
  * 内部类强制子类执行

    \`\`\`java

    public class Foo {

    private final Object finalizeGuardian = new Object() {

    ```
    @Override protected void finalize() throws Throwable {
        Foo.finalize();
    }
    ```

    };

    }


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://blog.cweihang.io/java/l1-new-delete.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
