创建者模式-单例模式

suaxi
2022-08-27 / 0 评论 / 46 阅读 / 正在检测是否收录...

特点:将对象的创建与使用分离,可以降低系统的耦合度,使用者不需要关注对象的创建细节

分为:单例模式、工厂模式、抽象工厂模式、原型模式、建造者模式

1. 单例模式

单例模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建,这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象

单例模式的实现:

  • 饿汉式:类一加载就创建该实例对象
  • 懒汉式:需要使用时才创建该实例对象

(1)饿汉式(静态变量方式)

该方式在成员位置声明 Singleton 类型的静态变量,并创建 Singleton 类的对象 instanceinstance 对象随着类的加载被创建,如果该类足够大且一直没有被使用的情况下会造成内存的浪费
Singleton

public class Singleton {

    //1.私有构造方法
    private Singleton() {
    }

    //2.在本类中创建本类对象
    private static Singleton instance = new Singleton();

    //3.提供一个公共的访问方式,让外界获取该对象
    public static Singleton getInstance() {
        return instance;
    }

}

Client

public class Client {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();

        //true
        System.out.println(instance == instance1);
    }
}



(2)饿汉式(静态代码块方式)

该方式在成员位置声明 Singleton 类型的静态变量,而对象的创建是在静态代码块中,也是随着类的加载而被创建,与静态变量方式类似
Singleton

public class Singleton {

    private Singleton() {

    }

    private static Singleton instance;

    static {
        instance = new Singleton();
    }

    public static Singleton getInstance() {
        return instance;
    }
}

Client

public class Client {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();

        //true
        System.out.println(instance == instance1);
    }
}



(3)懒汉式(线程不安全)

当调用 getInstance() 方法获取 Singleton 类的对象的时候才创建 Singleton 类的对象,这样就实现了懒加载,但在多线程操作时存在线程不安全的问题
Singleton

public class Singleton {

    private Singleton() {

    }

    private static Singleton instance;

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

Client

public class Client {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();

        //多线程操作时,存在线程不安全的问题
        System.out.println(instance == instance1);
    }
}



(4)懒汉式(双重检测锁)

使用 volatile 关键字保证可见性和防止指令重排(JVM在实例化对象时会进行优化和指令重排序操作,可能产生空指针)
Singleton

public class Singleton {

    private Singleton() {

    }

    //volatile保证可见性和防止指令重排
    private static volatile Singleton instance;

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

Client

public class Client {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();

        System.out.println(instance == instance1);
    }
}



(5)懒汉式(静态内部类)

实例由内部类创建,由于JVM在加载外部类的过程中不会加载静态内部类,只有内部类的属性/方法被调用时才会被加载,并初始化其静态属性,其中静态属性由于被 static 修饰,保证只被实例化一次,并且严格保证实例化顺序

说明:第一次加载 Singleton 类时不会去初始化 INSTANCE,只有第一次调用 getInstance() 时,虚拟机加载 SingletonHolder 并初始化 INSTANCE ,这样既保证了现成安全,也保证了 Singleton 的唯一性,在没有加任何锁的情况下保证了线程的安全且不造成性能和内存的浪费(双重检测锁方式 和 静态内部类方式可以任选其一)
Single

public class Singleton {

    private Singleton() {

    }

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

Client

public class Client {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();

        System.out.println(instance == instance1);
    }
}



(6)枚举方式

枚举是线程安全的,且只会被加载一次,注意枚举方式属于饿汉式
Singleton

public enum Singleton {
    INSTANCE;
}

Client

public class Client {
    public static void main(String[] args) {
        Singleton instance = Singleton.INSTANCE;
        Singleton instance1 = Singleton.INSTANCE;

        //true
        System.out.println(instance == instance1);
    }
}



(7)存在的问题

破坏单例模式的方式:

  • 序列化和反序列化
  • 反射

解决方法:

  • 序列化和反序列化 - 在单例类中添加 readResolve() 方法,在反序列化时,如果类中定义了这个方法,就返回这个方法的值,反之则创建新的对象
  • 反射 - 在单例类的构造方法中通过标志位来判断是否是第一次访问,且在该构造方法中需要加 synchronized 锁,锁该类 synchronized(Singleton.class)
0

评论 (0)

取消