0
点赞
收藏
分享

微信扫一扫

设计模式-创建型模式-单例模式

彭维盛 2022-04-06 阅读 50
单例模式

创建型模式-单例模式

1. 单例模式的定义与特点

单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。

单例模式有 3 个特点:

  1. 单例类只有一个实例对象;
  2. 该单例对象必须由单例类自行创建;
  3. 单例类对外提供一个访问该单例的全局访问点。

2. 单例模式的优点和缺点

单例模式的优点:

  • 单例模式可以保证内存里只有一个实例,减少了内存的开销。
  • 可以避免对资源的多重占用。
  • 单例模式设置全局访问点,可以优化和共享资源的访问。

单例模式的缺点:

  • 单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。
  • 在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。
  • 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。

3. 单例模式的应用场景

  • 需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少 GC。
  • 某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等。
  • 某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用。
  • 各种连接池等。
  • spring默认Bean的模式

4. 单例模式的结构

  • 必须定义一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例

5.单例模式的结构实现

  1. 懒汉式单例(获取实例为空才创建实例)
public class Singleton {
    public static void main(String[] args) {
        LazySingleton instance1 = LazySingleton.getInstance();
        LazySingleton instance2 = LazySingleton.getInstance();
        System.out.println(instance1 == instance2);
    }
}

/**
 1. 懒汉式单例
 */
class LazySingleton {
    /**
     * volatile 保证了不同线程对这个变量进行操作时的可见性
     * 禁止进行指令重排序(代码执行顺序)
     */
    private static volatile LazySingleton lazySingleton = null;

    /**
     * 构造器私有化
     */
    private LazySingleton() {

    }
    
    /**
     * 双重验证 防止多线程下获取为空
     *
     * @return
     */
    public static LazySingleton getInstance() {
        if (lazySingleton == null) {
            synchronized (LazySingleton.class) {
                if (lazySingleton == null) {
                    lazySingleton = new LazySingleton();
                }
            }
        }
        return lazySingleton;
    }
}
  1. 饿汉式单例(实例在类加载时创建)
public class Singleton {
    public static void main(String[] args) {
        HungrySingleton instance1 = HungrySingleton.getInstance();
        HungrySingleton instance2 = HungrySingleton.getInstance();
        System.out.println(instance1 == instance2);
    }
}
/**
 * 饿汉式单例
 * 实例在类加载时创建
 * 保证在调用 getInstance 方法之前单例已经存在了。
 */
class HungrySingleton {

    private HungrySingleton() {

    }

    private static final HungrySingleton hungrySingleton = new HungrySingleton();

    public static HungrySingleton getInstance() {
        return hungrySingleton;
    }
}

6.单例模式的扩展

单例模式可扩展为有限的多例(Multitcm)模式(比如连接池)

/**
 * 有限多例模式(应用场景 连接池)
 */
class Multitcm {

    private static int maxNum = 8;

    private static int currNum = 0;

    private static ArrayList<String> multitcmList = new ArrayList<>(maxNum);

    private static ArrayList<Multitcm> connArrayList = new ArrayList<>(maxNum);

    private Multitcm() {
    }

    private Multitcm(String info) {
        multitcmList.add(info);
    }

    static {
        for (int i = 0; i < maxNum; i++) {
            connArrayList.add(new Multitcm(i + "号连接"));
        }
    }

    public static Multitcm getInstance() {
        Random random = new Random();
        currNum = random.nextInt(maxNum);
        return connArrayList.get(currNum);
    }

    public void connectionInfo() {
        System.out.println(multitcmList.get(currNum));
    }
}

7.破坏单例模式的方式

  1. 克隆-浅克隆(实现Cloneable接口重写Object的clone方法)
public class Singleton {
    public static void main(String[] args) throws CloneNotSupportedException {
        LazySingleton instance = LazySingleton.getInstance();
        LazySingleton clone = (LazySingleton) instance.clone();
        System.out.println(instance == clone);
    }
}

/**
 1. 懒汉式单例
 */
class LazySingleton implements Cloneable {
    /**
     * volatile 保证了不同线程对这个变量进行操作时的可见性
     * 禁止进行指令重排序(代码执行顺序)
     */
    private static volatile LazySingleton lazySingleton = null;

    /**
     * 构造器私有化
     */
    private LazySingleton() {

    }

    /**
     * 双重验证 防止多线程下获取为空
     *
     * @return
     */
    public static LazySingleton getInstance() {
        if (lazySingleton == null) {
            synchronized (LazySingleton.class) {
                if (lazySingleton == null) {
                    lazySingleton = new LazySingleton();
                }
            }
        }
        return lazySingleton;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
  1. 克隆-深克隆(实现Serializable 接口,先序列化在反序列化)
public class Singleton {

    public static void main(String[] args) throws Exception {
        LazySingleton s1=LazySingleton.getInstance();
        //序列化
        ByteArrayOutputStream bos=new ByteArrayOutputStream();
        ObjectOutputStream oos=new ObjectOutputStream(bos);
        oos.writeObject(s1);
        //反序列化
        ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois=new ObjectInputStream(bis);
        LazySingleton s2= (LazySingleton) ois.readObject();

        System.out.println(s1);
        System.out.println(s2);
    }
}

/**
 1. 懒汉式单例
 */
class LazySingleton implements Serializable {

    private static final Long serialVersionUID = 1L;

    /**
     * volatile 保证了不同线程对这个变量进行操作时的可见性
     * 禁止进行指令重排序(代码执行顺序)
     */
    private static volatile LazySingleton lazySingleton = null;

    /**
     * 构造器私有化
     */
    private LazySingleton() {

    }

    /**
     * 双重验证 防止多线程下获取为空
     *
     * @return
     */
    public static LazySingleton getInstance() {
        if (lazySingleton == null) {
            synchronized (LazySingleton.class) {
                if (lazySingleton == null) {
                    lazySingleton = new LazySingleton();
                }
            }
        }
        return lazySingleton;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
  1. 反射
public static void main(String[] args) throws CloneNotSupportedException, Exception {
       LazySingleton instance = LazySingleton.getInstance();
       Class<? extends LazySingleton> aClass = instance.getClass();
       Constructor<? extends LazySingleton> declaredConstructor = aClass.getDeclaredConstructor();
       declaredConstructor.setAccessible(true);
       LazySingleton lazySingleton = declaredConstructor.newInstance();
       System.out.println(instance == lazySingleton);
 }
  1. (反射)第一种解决方法(在构造器判断实例是否为空,不等于null直接抛异常。但是反射可以修改私有变量的值,跳过判断,前提知道参数名称)
   /**
     * 构造器私有化
     */
    private LazySingleton() {
        if(lazySingleton!=null){
            throw new RuntimeException("老子是单例,你别想破坏!");
        }
        System.out.println("private Singleton()");
    }
  1. (反射)第二种解决方法(使用内部类的方式)
class HungrySingleton {

    private HungrySingleton() {
        if(LazyHolder.LAZY != null){
            throw new RuntimeException("不允许创建多个实例");
        }
    }


    public static final HungrySingleton getInstance(){
        //在返回结果以前,一定会先加载内部类
        return LazyHolder.LAZY;
    }

    //默认不加载
    private static class LazyHolder{
        private static final HungrySingleton LAZY = new HungrySingleton();
    }
}

8.总结

设计模式不一定必须遵守设计原则,按需设计。

举报

相关推荐

0 条评论