01-创建和销毁对象(1-9)

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

工厂方法优势:

  • 有名称,更易读
  • 可选,不必每次创建新对象
  • 可以返回子类型
  • 创建参数化类型实例时,更简洁
  • 可以返回非公有类

工厂方法缺点:

  • 类如果不含有他的公有或者受保护的构造器,就不能被子类化。
  • 与其他静态方法没区别,不易识别

常用的静态工厂名称:valueOf,of,getInstance,newInstance,getType,newType.

2.遇到多个构造参数时要考虑用构建器 Builder 模式

可选参数多的时候,通常使用 重叠构造器 模式,然而当参数过多时,会很难编写,较难阅读,容易致错。

第二种办法是 JavaBean 模式,调用无参构造器创建对象,通过 setter 设置参数。因为构造过程分散,JavaBean 可能不一致,也阻止了将类做成不可变的可能。

应该使用 Builder 模式,不直接生成对象,而是先得到 builder 对象,通过 builder 的类似 seter 方法设置参数,最后调用无参的 build 方法生成不可变对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
public static class Builder {
//对象的必选参数
private final int servingSize;
private final int servings;
//对象的可选参数的缺省值初始化
private int calories = 0;
private int fat = 0;
//只用少数的必选参数作为构造器的函数参数
public Builder(int servingSize,int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val) {
calories = val;
return this;
}
public Builder fat(int val) {
fat = val;
return this;
}
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
}
}
//使用方式
public static void main(String[] args) {
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(100).build();
System.out.println(cocaCola);
}

Builder 模式比重叠构造器更冗长,只在参数很多的时候才使用,比如四个或者更多。

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

Java 1.5 之前,实现单例是通过“构造器私有化,静态公有 final 实例对象”,或直接获取,或通过方法获取。

Java 1.5 之后,实现单例的最佳方法,是编写“单元素的枚举”类型:

1
2
3
4
public enum Elvis {
INSTANCE;
public void whateverMethod() { ... }
}

无偿提供序列化,防止多次实例化。

4.通过私有构造器强化不可实例化的能力

一些工具类不希望被实例化,将构造函数私有,避免外部调用,并在构造函数中抛异常,避免内部调用。

5.用依赖注入代替硬编码

行为参数化是依赖注入的有用变体,将Lexicon dictionary作为参数,相对硬编码更加灵活。

对于行为参数化的类,可以通过策略模式和lambda表达式灵活实现

1
2
3
4
5
6
7
8
9
10
11
// Dependency injection provides flexibility and testability
public class SpellChecker {
private final Lexicon dictionary;

public SpellChecker(Lexicon dictionary) {
this.dictionary = Objects.requireNonNull(dictionary);
}

public boolean isValid(String word) { ... }
public List<String> suggestions(String typo) { ... }
}

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

  • 最好能重用对象而不是在每次需要的时候就创建一个相同功能的新对象。

如果对象是不可变的,它就始终可以被重用。

  • 优先使用基本类型而不是装箱基本类型,当心无意识的自动装箱。

  • 避免轻量级对象池

使用 Byte.valueOf 来创建包装类型,因为 -128~127 的数会缓存起来,要从缓冲池中取,Short、Integer、Long 也是这样。

7.消除过期的对象引用

不需要对所有对象显式置空,以下情形考虑资源手工处理:

  • 类是自己管理内存,如例子中的 Stack 类
  • 使用对象缓存机制时,需要考虑被从缓存中换出的对象,或是长期不会被访问到的对象
  • 事件监听器和相关回调,使用弱引用

8.避免使用终结方法

finalizer 不保证会被及时的执行,甚至不一定执行。

除非是作为安全网,或者为终止非关键的本地资源,否则请不要使用终结方法。

9.用 try-with-resources 代替 try-finally

实现 AutoCloseable 接口的类应使用 try-with-resources,因其可同时处理多个资源,不用嵌套


01-创建和销毁对象(1-9)
https://janycode.github.io/2016/04/28/02_编程语言/01_Java/04_EffectiveJava/01-创建和销毁对象(1-9)/
作者
Jerry(姜源)
发布于
2016年4月28日
许可协议