创建型模式
在创建对象的同时,隐藏创建逻辑,不使用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 简单工厂模式
工厂根据Client传入参数不同,返回不同抽象对象实例
- 适用于创建对象较少的情况。
- 如果增加新的产品,需要修改工厂逻辑,违背开闭原则。
实例
Spring中的BeanFactory使用简单工厂模式,根据传入一个唯一标识获得Bean对象。
3 工厂模式
与简单工厂模式相比,定义了抽象工厂,将生产任务交给不同的派生类工厂,派生类实现生产接口,从而就不需要通过指定类型来创建特点对象了。
- 针对一类产品,当有新的类别时,需要修改抽象工厂及派生类逻辑
4 抽象工厂模式
抽象工厂模式通过在抽象工厂内增加创建产品的接口,并在具体的派生类中实现新加产品的创建(前提是子工厂支持生成该商品,否则这个接口可以什么都不做)。