Java设计模式 (单例模式)

什么是单例模式

在JVM运行环境中,一个类只存在一个实例对象,并且这个类只提供一个获取到该实例的方法(静态方法)

为什么要使用单例模式

频繁使用的对象实例,且对象本身的属性不改变,每次都 new 一个实例,且执行实例方法结果都一样,这样就会存在多个实例对象,造成系统资源的浪费。

单例模式八种写法

  1. 饿汉式<线程安全> (当确保当前实例一定会使用一次或多次的时候;推荐使用)
  2. 饿汉式<线程安全,静态代码块>(推荐使用
  3. 懒汉式<线程不安全>(不推荐使用
  4. 懒汉式<线程安全;同步方法>(不推荐使用
  5. 懒汉式<线程不安全,同步代码快>(不推荐使用
  6. 懒汉式<线程安全,同步代码快,双重检查>(推荐使用
  7. 静态内部类<线程安全>(推荐使用
  8. 枚举<线程安全>(推荐使用

1:饿汉式<线程安全>

/**
 * 饿汉式  最简单的写法
 */
public class Singleton01 {
    /**
     * 私有化构造函数,防止外部可以 new
     */
    private Singleton01() {}

    /**
     * 声明静态变量,且实例化
     */
    private final static Singleton01 SINGLETON = new Singleton01();

    /**
     * 获取单例对象
     * @return {@link #SINGLETON}
     */
    public static Singleton01 getInstance() {
        return SINGLETON;
    }
}

优缺点说明

优点:写法简单,在类装载的时候,就创建了对象实例,避免线程同步问题;
缺点: 在类装载的时候,就实例化了对象,没有达到懒加载的效果;如果对象实例一直没有被使用,会占据内存资源;


2:饿汉式<线程安全;静态代码快>

/**
 * 饿汉式  静态代码块的写法
 */
public class Singleton02 {
    /**
     * 私有化构造函数,防止外部可以 new
     */
    private Singleton02() {}

    /**
     * 声明静态变量,且实例化
     */
    private static Singleton02 SINGLETON;

    /**
     * 静态代码快,类装载就会执行
     */
    static {
        SINGLETON = new Singleton02();
    }

    /**
     * 获取单例对象
     * @return {@link #SINGLETON}
     */
    public static Singleton02 getInstance() {
        return SINGLETON;
    }
}

优缺点说明

优点:写法简单,在类装载的时候,就会执行静态代码块创建对象实例,避免线程同步问题;
缺点: 在类装载的时候,就实例化了对象,没有达到懒加载的效果;如果对象实例一直没有被使用,会占据内存资源;


3:懒汉式<线程不安全>

/**
 * 懒汉式 线程不安全
 */
public class Singleton03 {
    /**
     * 私有化构造函数,防止外部可以 new
     */
    private Singleton03() {}

    /**
     * 声明静态变量
     */
    private static Singleton03 singleton03;

    /**
     * 获取单例对象
     * @return {@link #singleton03}
     */
    private static Singleton03 getInstance() {
        if (singleton03 == null) {
            /** 
             * 如果 {@link #singleton03} 为 null 的情况,就实例化
             * 但是在多线程的情况下,会同时进入到这里,对{@link #singleton03}进行实例化
             */
            singleton03 = new Singleton03();
        }
        return singleton03;
    }
}

优缺点说明

优点: 起到了懒加载的作用,但只能适用于单线程。
缺点: 在多线程情况下,一个线程进入 if (singleton03 == null)判断语句快,还未来得及实例化对象;另一个线程也进来了,这就会产生多个实例对象


4:懒汉式<线程安全;同步方法>

/**
 * 懒汉式 线程安全 同步方法
 **/
public class Singleton04 {
    /**
     * 私有化构造函数,防止外部可以 new
     */
    private Singleton04() {}

    /**
     * 声明静态变量
     */
    private static Singleton04 singleton04;

    /**
     * 方法加上 synchronized 同步关键字,每次访问都会加锁,保证线程安全
     * 但是如果 {@link #singleton04} 已经实例化了,加锁就会影响性能
     * @return {@link #singleton04}
     */
    public synchronized static Singleton04 getInstance() {
        if (singleton04 == null) {
            singleton04 = new Singleton04();
        }
        return singleton04;
    }
}

优缺点说明

优点: 起到了懒加载的作用,解决了线程安全问题
缺点: 多线程情况下,每个线程要获取对象实例,都需要加锁,而对象实例初始化只需要一次就可以,会导致效率变低


5:懒汉式<线程不安全,同步代码快>

/**
 * 懒汉式 线程不安全,同步代码快
 **/
public class Singleton05 {
    /**
     * 私有化构造函数,防止外部可以 new
     */
    private Singleton05() {}

    /**
     * 声明静态变量
     */
    private static Singleton05 singleton05;

    /**
     * 获取实例对象
     * @return {@link #singleton05}
     */
    public static Singleton05 getInstance() {
        if (singleton05 == null) {
            /**
             * 如果 {@link #singleton05} 为空,就获取锁,执行实例化操作
             * 但可能会有多个线程同时进入到这里,而进行多次实例化,
             */
            synchronized (Singleton05.class) {
                singleton05 = new Singleton05();
            }
        }
        return singleton05;
    }
}

优缺点说明

优点: 毫无优点
缺点: 多线程的情况下,会同时进入到 if (singleton05 == null) 方法快中,虽然有锁的代码快,但是线程进来后同样会实例化对象;


6:懒汉式<线程安全,同步代码快,双重检查>

/**
 * 懒汉式<线程安全,同步代码快,双重检查>
 **/
public class Singleton06 {
    /**
     * 私有化构造函数,防止外部可以 new
     */
    private Singleton06() {}

    /**
     * 声明静态变量
     */
    private static Singleton06 singleton06;

    /**
     * 获取对象实例
     * @return
     */
    public static Singleton06 getSingleton() {
        if (singleton06 == null) {
            synchronized (Singleton06.class) {
                /**
                 * 再加一个判断,防止多线程进入,而造成多次实例化对象
                 */
                if (singleton06 == null) {
                    singleton06 = new Singleton06();
                }
            }
        }
        return singleton06;
    }
}

优缺点说明

优点: 延迟加载,线程安全,效率较高;在实际开发中,推荐使用这种写法
值得一提的是:多重检查在多线程开发中经常使用。


7:静态内部类<线程安全>

/**
 * 静态内部类<线程安全>
 **/
public class Singleton07 {
    /**
     * 私有化构造函数,防止外部可以 new
     */
    private Singleton07() { }

    /**
     * 获取实例
     * @return
     */
    public static Singleton07 getInstance() {
        return Singleton07Instance.SINGLETON07;
    }

    /**
     * 静态内部类,
     * 在外部类{@link Singleton07}装载时,并不会装载该类{@link Singleton07Instance},
     * 而是在需要实例化的时,调用{@link #getInstance()}才会装载{@link Singleton07Instance}类,
     * 从而完成 {@link #SINGLETON07} 的实例化
     */
    private static class Singleton07Instance {
        /* 声明一个静态的实例对象 */
        final static Singleton07 SINGLETON07 = new Singleton07();
    }
}

优缺点说明

优点: 静态内部类方式不会在外部类装载的时候而装载,而是等调用该类的方法时,才会进行装载;JVM只会装载一次类,所以是线程安全的,也实现了懒加载;


8:枚举<线程安全>

/**
 * 枚举单例模式
 **/
public enum Singleton08 {
    /**
     * 只声明一个一个属性
     */
    SINGLETON;

    /**
     * Singleton08.SINGLETON.print();  调用单例方法
     */
    public void print() {
        System.out.println("这是枚举单例");
    }
}

优缺点说明

优点: 避免多线程同步问题,还能防止反序列化创建对象;


值得提一下:反射可以破坏单例唯一实例

单例类
在这里插入图片描述
反射调用单例的构造方法测试,会重新创建一个实例对象;
在这里插入图片描述
避免利用反射,而破坏单例,在构造方法里改造下;在这里插入图片描述
在这里插入图片描述

先用反射创建实例,就不会报错了;
在这里插入图片描述
在实际开发中,不要用反射去创建单例的实例对象,很显然违背了单例模式的唯一实例原则;


觉得对您有帮助,就点个赞呗。😀
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Me_Liu_Q

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值