《Effective Java》学习笔记

​ 仅记录自己学习过,且有一定自我理解和思考的

考虑使用静态工厂方法替代构造方法

优点

  • 有名字

  • 不需要每次调用时创建对象,例:Boolean.valueOf(?)

  • 可以返回其返回类型的任何子类型的对象,例:Collections.unmodifiableCollection(?)

  • 返回对象的类,可以根据输入参数的不同而不同,例:EnumSet.noneOf(?)

  • 编写包含该方法的类时,返回的对象的类不需要存在,例:ServiceLoader.load(?)

缺点

  • 没有public或者protected构造方法的类不能被子类化

  • API文档中很难找到

当构造方法参数过多时使用builder模式

优点

  • 解决参数过多问题
  • 可以对参数间依赖关系进行校验
  • 去掉创建对象时的中间状态
  • 可以不暴露类中的set方法,使其变成不可变对象

缺点

  • 创建对象前必须先创建它的builder
  • 代码比较复杂,4个以上参数才适用

实例

简单情况下可以直接使用@Builder

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
@Getter
public final class Student {

private final String id;
private final String name;

private Student(StudentBuilder studentBuilder){
this.id = studentBuilder.id;
this.name = studentBuilder.name;
}
public static Student.StudentBuilder builder(){
return new Student.StudentBuilder();
}
@Setter
@Accessors(fluent = true,chain = true)
protected static class StudentBuilder{

private String id;
private String name;

public StudentBuilder() {
}

public Student build(){
if(!StringUtils.hasText(name)){
throw new IllegalArgumentException("name not support empty");
}
return new Student(this);
}
}
}

//使用
Student student = Student.builder()
.id("001")
.name("张三")
.build();

实现 Singleton 属性

私有构造方法

饿汉式(类加载时创建)

1
2
3
4
5
6
7
8
9
10
11
12
public class LogGenerator {

private static final LogGenerator LOG_GENERATOR = new LogGenerator();

private LogGenerator(){
}

public static LogGenerator getInstance(){
return LOG_GENERATOR;
}

}

类加载的时候创建,如果类的初始化时间较长,可以将其初始化过程暴露在程序启动时。

懒汉式(第一次使用时创建)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class LogGenerator {

private volatile static LogGenerator LOG_GENERATOR;

private LogGenerator(){
}

public static LogGenerator getInstance(){
if(LOG_GENERATOR == null){
synchronized (LogGenerator.class){
if(LOG_GENERATOR == null){
return new LogGenerator();
}
}
}
return LOG_GENERATOR;
}

}

null判断后加锁再判断:只判断一次,线程并发会出现问题

volatile:防止指令重排序,初始化对象分为三步:1.分配内存 2.调用构造函数初始化对象 3.将初始化对象的地址放在分配内存里。其中第3步可能被重排序在第2步前,此时分配内存已不为null,但是还未初始化对象,使用对象会出现问题。

静态内部类

1
2
3
4
5
6
7
8
9
10
11
12
public class LogGenerator {
private LogGenerator(){
}

public static LogGenerator getInstance(){
return LogGeneratorHolder.LOG_GENERATOR;
}

private static class LogGeneratorHolder{
private static LogGenerator LOG_GENERATOR = new LogGenerator();
}
}

由JVM保证线程安全,同时实现了懒加载

枚类

天然线程安全