创建型模式

在创建对象的同时,隐藏创建逻辑,不使用new直接实例化对象,程序判断需要创建哪些对象时更加灵活。

1 单例模式

一个单例类在任何情况下只存在一个实例,构造方法私有,由自己创建一个静态变量存储实例,对外提供一个静态共有方法获取实例。

  • 只有一个实例,避免了开销
  • 没有抽象层,难以拓展,与单一职责原则冲突。

1.1 常见写法

1.1.1 饿汉式,线程安全

类一加载就创建对象,比较常用,但是容易产生垃圾对象。

  • 线程安全,不加锁,执行效率高
  • 缺点:不是懒加载,浪费内存空间
public class Singleton{
  private Singleton(){}
  private final static Singleton instance = new Singleton();
  public static Singleton getInstance(){
    return instance;
  }
}
  • 使用反射破坏单例
public class Main{
  public static void main(String[] args) throws Exception{
    Constructor<Singleton> declaredConstructor = Singleton.class.getDeclaredConstructor(null);
    declaredConstructor.setAccessible(true);
    Singleton singleton = declaredConstructor.newInstance();
  }
}

1.1.2 懒汉式,线程不安全

public class Singleton{
  private Singleton(){}
  private static Singleton instance;
  public static Singleton getInstance(){
    if ( instance == null ) {
      instance = new Singleton();
    }
    return instance;
  }
}
  • 多线程破坏单例
public class Main(){
  public static void main(String[] args){
    for(int i=0;i<3;i++){
      new Thread(() -> {
        System.out.println("multi thread created singleton:" + Singleton.getInstance());
      }).start()
    }
  }
}

1.1.3 懒汉式,线程安全

加锁

public class Singleton{
  private Singleton(){}
  public static Singleton instance;
  public synchronized static Singleton getInstance(){
    if( instance == null ){
      instance = new Singleton();
    }
    return instance;
  }
}

1.1.4 双重锁校验

public class Singleton{
  private Single(){}
  private volatile static Singleton instance;
  public static Singleton getInstance(){
    if( instance == null ){
      synchronized (Singleton.class){
        if( instance == null ){
          instance = new Singleton();
        }
      }
    }
    return instance;
  }
}

为什么要用volatile关键字?

new关键字不是原子操作:(1)在堆内存开辟内存空间、(2)调用构造方法初始化对象,(3)引用变量指向堆内存空间。

编译器对代码优化后可能会进行指令重排,导致执行顺序为(1)、(3)、(2),当执行完(3)时,其他线程发现instance不等于null,返回没有被初始化的实例。

1.1.5 静态内部类

懒加载,线程安全,效率高,实现简单

public class Singleton{
  private Singleton(){}
  public static Singleton getInstance(){
    return InnerClass.INSTANCE;
  }
  private static class InnerClass{
    private final static Singleton INSTANCE = new Singleton();
  }
}

类的加载时机–主动加载:

  • 遇到new、getstatic、putstatic、invokestatic时
  • 使用java.lang.reflect对类进行反射时
  • 初始化类发现其父类还没初始化时
  • 虚拟器启动时,用户指定要执行的主类
  • 使用JDK1.7的动态语言支持时,如果。。。

当gitInstance被调用时,InnerClass才在Singleton的运行时常量池里,吧符号引用换位直接引用,这时INSTANCE也才被真正的创建。JVM会保证一个类的初始化函数是同步的。

1.1.6 枚举单例

public enum Singleton{
  INSTANCE;
  public void doSomething(String str){
    System.out.println(str);
  }
}
  • 简单、高效、线程安全、可以避免通过反射破坏枚举单例

2 简单工厂模式

image.png

工厂根据Client传入参数不同,返回不同抽象对象实例

  • 适用于创建对象较少的情况。
  • 如果增加新的产品,需要修改工厂逻辑,违背开闭原则

实例

Spring中的BeanFactory使用简单工厂模式,根据传入一个唯一标识获得Bean对象。

3 工厂模式

image.png

与简单工厂模式相比,定义了抽象工厂,将生产任务交给不同的派生类工厂,派生类实现生产接口,从而就不需要通过指定类型来创建特点对象了。

  • 针对一类产品,当有新的类别时,需要修改抽象工厂及派生类逻辑

4 抽象工厂模式

image.png

抽象工厂模式通过在抽象工厂内增加创建产品的接口,并在具体的派生类中实现新加产品的创建(前提是子工厂支持生成该商品,否则这个接口可以什么都不做)。

5 建造者模式

6 原型模式