创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建于使用分离”。
这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。
创建型模式分为:
1. 单例模式
2. 工厂方法模式
3. 抽象工程模式
4. 原型模式
5. 建造者模式
一、单例模式的结构单例模式(Singleton Pattern)是Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
二、单例模式的实现单例模式的主要有以下角色:
1. 单例类。只能创建一个实例的类
2. 访问类。使用单例类
单例设计模式主要分为两类:
1. 饿汉式:类加载就会导致该单实例对象被创建
2. 懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建
1. 饿汉式-方式1(静态成员变量方式)说明:
该方式在成员位置声明 Singleton 类型的静态变量,并创建Singleton 类的对象 instance。instance 对象是随着类的加载而创建的。如果该对象足够大的话,而一直没有使用就会造成内存的浪费。
2. 饿汉式-方式2(静态代码块方式)说明:
该方式在成员位置声 Singleton 类型的静态变量,而对象的创建是在静态代码块中,也是对着类的加载而创建。所以和饿汉式的方式1基本一样,当然该方式也存在内存浪费问题。
3. 懒汉式-方式1(线程不安全)说明:
从上面的代码我们可以看出该方式在成员位置声明 Singleton 类型的静态变量,并没有进行对象的赋值操作。但调用 getInstance() 方法获取 Singleton 类的对象的时候才创建 Singleton 类的对象,这样就实现了懒加载的效果。但是,多线程环境会出现线程安全问题。
4. 懒汉式-方式2(线程安全)说明:
该方式也实现了懒加载效果,同时又解决了线程安全问题。但是在 getInstance() 方法上添加了 synchronized 关键字,导致该方法的执行效果特别低。从上面代码我们可以看出,其实就是在初始化 instance 的时候才出现线程安全问题,一旦初始化完成就不存在了。
5. 懒汉式-方式3(双重检查锁方式)再来讨论一下懒汉模式中加锁的问题,对于 getInstance() 方法来说,绝大部分的操作都是读操作,读操作是线程安全的,所以我们没必要让每个线程必须持有锁才能调用该方法,我们需要调整加锁的时机。
说明:
双重检测锁模式是一种非常好的单例实现模式,解决了单例、性能、线程安全问题。
在多线程的情况下,可能会出现空指针问题,出现问题的原因是JVM在实例化对象的时候会进行优化和指令重排序操作。
只需要使用 volatile 关键字,可以保证可见性和有序性。
6. 懒汉式-方式4(静态内部类方式)静态内部类单例模式中实例由内部类创建,由于 JVM 在加载外部类的过程中,是不会加载静态内部类的,只有在内部类的属性/方法被调用时才会被加载,并初始化其静态属性。静态属性由于被 static 修饰,保证只被实例化一次,并且严格保证实例化顺序。
说明:
静态内部类单例模式是一种优秀的单例模式,是开源项目中比较常用的一种单例模式。在没有加任何锁的情况下,保证了多线程下的安全,并且没有任何性能影响和空间的浪费。
7. 枚举方式三、单例存在的问题 1.问题演示枚举方式实例单例模式是极力推荐的单例实现模式,因为枚举类型是线程安全的,并且只会装载一次。而且枚举类型是所有单例实现中唯一一种不会被破坏的单例实现模式。
破坏单例模式:
使上面定义的单例类(Singleton)可以创建多个对象,枚举方式除外。有两种方式,分别是序列化和反射。
序列化反序列化
2. 问题的解决反射
序列化、反序列化破坏单例模式的解决办法
在 Singleton 类中添加 readResolve() 方法,在反序列化时被反射调用,如果定义了这个方法,就返回这个方法的值,如果没有定义,则返回新 new 出来的对象。
源码解析
四、JDK 源码解析-Runtime类 1. 单例模式反射方式破解单例的解决方法
说明:
当通过反射方式调用构造方法进行创建时,直接抛异常。不运行此中操作。
2. 使用Runtime 类中的方法从源代码中可以看出 Runtime 类使用的是饿汉式(静态属性)方式来实现单例模式的。



