23种设计模式之-单例模式

导读:本篇文章讲解 23种设计模式之-单例模式,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

一、什么是单例模式

 

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

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

注意:

  • 1、单例类只能有一个实例。
  • 2、单例类必须自己创建自己的唯一实例。
  • 3、单例类必须给所有其他对象提供这一实例。

二、单例模式实现的方式 

1.饿汉式

饿汉式顾名思义处于饥饿状态,不管你是不是使用,在开始就先创建好。优点是线程安全,但是如果不用的话就浪费了空间。其实这点浪费几乎可以忽略,jdk源码中多处使用了这种方式。

package com.example.hutooldemo.single;

/**
 * description: EHan 不管是不是使用,先创建好。虽然浪费了空间但是线程安全<br>
 * @date: 2020/10/15 0015 上午 8:29 <br>
 * @author: William <br>
 * version: 1.0 <br>
 */
public class EHan {

    /**
     * 默认创建对象,等待使用
     */
    private static EHan instance = new EHan();

    /**
     * 私有化构造方法
     */
    private EHan (){}

    /**
     * 对外提供获取对象方法
     */
    public static EHan getInstance(){
        return instance;
    }
}

2.懒汉式

懒汉式顾名思义这个方式就是“懒”,不用就不创建,当使用到的时候再创建。好处是节省了空间,但是在多线程状态下是不安全的。因为如果多个线程同一时间使用到这个对象调用对象初始化方法,可能会创建出多个对象。建议不要使用。

package com.example.hutooldemo.single;

/**
 * description: LHan 懒汉模式,什么时候使用什么时候创建,线程不安全,
 * 如果多个线程同时调用getInstance()方法则会创建多个对象<br>
 * @date: 2020/10/15 0015 上午 8:26 <br>
 * @author: William <br>
 * version: 1.0 <br>
 */
public class LHan {

    /**
     * 默认先不创建对象,使用时再创建
     */
    private static LHan instance;

    /**
     * 私有化构造方法
     */
    private LHan(){}

    /**
     * 对外提供获取对象方法
     */
    public static LHan getInstance(){
        if(instance == null){
            instance = new LHan();
        }
        return instance;
    }

}

3.双重校验锁模式

双重校验锁(DCL)模式,这个模式是在懒汉式的基础上增加了一个双重校验来保证线程安全。这种方式即节省了空间,同时保证了线程安全。推荐使用这种方式。

package com.example.hutooldemo.single;

/**
 * description: DoubleCheckLock 双重校验锁(DCL)模式,这个模式是在懒汉式的基础上
 * 增加了一个双重校验,来保证线程安全。这种方式即节省了空间,同时保证了线程安全<br>
 * @date: 2020/10/15 0015 上午 8:56 <br>
 * @author: William <br>
 * version: 1.0 <br>
 */
public class DoubleCheckLock {


    /**
     * 默认先不创建对象,使用时再创建,使用volatile关键字是为了防止指令重排
     */
    private static volatile DoubleCheckLock instance;


    /**
     * 私有化构造方法
     */
    private DoubleCheckLock(){}

    /**
     * 对外提供获取对象方法
     */
    private static DoubleCheckLock getInstance(){
        if(instance == null){
            //添加锁,若果同一时间多个线程同时调用获取对象方法,保证每次只有一个线程进入
            synchronized (DoubleCheckLock.class){
                //进入后再次判断对象是否为空,如果为空则创建,不为空直接跳出。确保只有一个对象被创建
                if(instance == null){
                    instance = new DoubleCheckLock();
                }
            }
        }
        return instance;
    }
}

4.静态内部类

package com.example.hutooldemo.single;

/**
 * description: Singleton 静态内部类<br>
 *
 * @date: 2020/10/15 0015 上午 9:08 <br>
 * @author: William <br>
 * version: 1.0 <br>
 */
public class Singleton {

    /**
     * 默认创建静态内部类,在内部类中初始化对象,对象会在内
     * 部类加载时创建,内部类没有加载的时候则不会创建对象
     */
    private static class SingletonInner{
        private static final Singleton instance = new Singleton();
    }


    /**
     * 私有化构造方法
     */
    private Singleton(){}

    /**
     * 对外提供获取对象方法
     */
    private static final Singleton getInstance(){
        return SingletonInner.instance;
    }
}

通过静态内部类的方式实现单例模式是线程安全的,同时静态内部类不会在Singleton类加载时就加载,而是在调用getInstance()方法时才进行加载,达到了懒加载的效果。

似乎静态内部类看起来已经是最完美的方法了,其实不是,可能还存在反射攻击或者反序列化攻击。且看如下代码:

package com.example.hutooldemo.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * description: SingleTest <br>
 * @date: 2020/10/15 0015 上午 8:26 <br>
 * @author: William <br>
 * version: 1.0 <br>
 */
public class SingleTest {


    public static void main(String[] args) throws Exception {
        Singleton singleton = Singleton.getInstance();
        Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        Singleton newSingleton = constructor.newInstance();
        System.out.println(singleton == newSingleton);
    }

}

输出结果如下

23种设计模式之-单例模式

5.枚举

枚举的方式是比较少见的一种实现方式,但是代码却更简洁清晰。并且还自动支持序列化机制,绝对防止多次实例化。如果想防止别人通过反射攻击或者反序列化攻击实现多次实例化可以使用这种方式。

package com.example.hutooldemo.single;

/**
 * description: EnumSingleton 枚举模式,线程安全,代码简单且能绝对防止多次实例化<br>
 *
 * @date: 2020/10/15 0015 上午 9:20 <br>
 * @author: William <br>
 * version: 1.0 <br>
 */
public enum EnumSingleton {
    INSTANCE;
    public void yourMethod(){
        System.out.println("~枚举方式实现单例~");
    }
}

调用方式:直接使用就好

package com.example.hutooldemo.single;

/**
 * description: SingleTest <br>
 * @date: 2020/10/15 0015 上午 8:26 <br>
 * @author: William <br>
 * version: 1.0 <br>
 */
public class SingleTest {

    public static void main(String[] args) {
        EnumSingleton.INSTANCE.yourMethod();
    }

}

总结:

推荐使用饿汉式,双重检验锁模式,以及枚举方式。因为枚举更加简洁安全,相信枚举方式会越来越多的被使用。

欢迎添加微信一起学习、交流,共同进步

23种设计模式之-单例模式

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/97033.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!