Java笔试题:单例设计模式详解,饿汉式懒汉式区别,为什么推荐饿汉式而不是懒汉式?

首先说一下单例设计模式的意义:是要让main方法中只能得到该类的唯一一个对象。

饿汉式

1. 首先我们想怎样才不能让main()方法随便去创建对象呢? ————> 把构造方法私有化。
public class Singleton {
    private Singleton() { 
    }
}

2. 私有化构造方法了以后,在主方法中就不能new对象了,那主方法又怎样去创建对象呢? ————>因为构造方法在自己的类中没有限制,所以可以在自己的类中把对象创建出来。

public class Singleton {
    private Singleton() {
    }
    Singleton sing = new Singleton();
}

3. 怎样把创建的对象sing应用到main()方法中呢?————> 用static修饰创建对象的代码,把sing对象提升到类层级,这样就可以在main()方法中以 " 类名 . " 的方式调用了。

public class Singleton {
    private Singleton() {
    }
    static Singleton sing = new Singleton();
}

(此处我原本以为static修饰了对象的创建,那么主方法不就可以无休止的创建对象了吗? 其实不是,其实static修饰的是sing这个创建出来的对象,而不是这个创建对象的过程;而且构造方法已经私有化,主方法是不可能new对象的。)

4. 但是为了防止唯一的对象被主方法赋值为null;Singleton.sing = null; 把这个唯一的对象毁掉,就需要在创建对象的时候再加一个关键字————> private修饰。

public class Singleton {
   private Singleton() {
   }
   private static Singleton sing = new Singleton();
}

5. 再次私有化了对象之后,只能用公开的get方法来返回sing,同理,因为方法需要对象调用,但是main()不能new对象,所以————> get方法也得变成类层级,这样get方法也不需要对象来调用了,直接以 " 类名 . "的方式调用。

public class Singleton {
    private Singleton() {
    }
    private static Singleton sing = new Singleton();
    public static Singleton getInstance() {
        return sing;
    }
}

( 饿汉式所谓的‘饿’,其实就是写完构造方法后,啥也不管!我必须先new对象。因为不new你所谓的return的对象也不存在是叭…)

饿汉式流程总结 :

  • 私有化构造方法,private修饰。
  • 声明本类型的引用指向本类型的对象,并使用private static 修饰。
  • 提供公有的get方法,负责将对象返回出去,public static 修饰。

----------------------------------------------------------------------------------------------------

懒汉式

因为懒汉式和饿汉式代码近乎相同这里就先直接上代码啦…

懒汉式代码如下(这段代码相对而言不是全部的懒汉式代码,改进代码下面有讲述):

public class Singleton_lan {
    //私有化构造方法
    private Singleton_lan () {

    }
    //声明本类类型的引用指向本类类型的对象,并用private static修饰
    private static Singleton_lan sl =null;
    //创建共有的get方法,负责将上述对象返回出去,用public static修饰
    public static Singleton_lan getInstance () {
        if (null == sl) {
            sl = new Singleton_lan();
        }
        return sl;
    }
}
*懒汉式代码相对于饿汉式代码的本质区别就在于,它是在主方法调用get方法的时候才会去new这个唯一的对象,这也突出了一个‘懒’字。

----------------------------------------------------------------------------------------------------

看完了两种不同的单例设计模式设计思想,我们来比较一下为什么大家都主推饿汉式而不是懒汉式呢?

因为懒汉式存在多线程的问题

看着代码,当两个线程同时调用getInstance()方法时,第一个线程 null = sl 成立,当进入new对象的时候是需要一点点时间的,这一点点时间就会让另一个线程也通过null = sl,这样每个线程都new了一个对象,因此违背了单例设计模式的原则! 这就是推荐用饿汉式而不是懒汉式的主要原因。

----------------------------------------------------------------------------------------------------

懒汉式的补救办法 (上)

想必大家可能会想到哈,没有错。就是用synchronized这个对象锁把这段代码锁起来!(解释一下synchronized的作用,当有synchronized修饰方法时,只允许有一个线程进去,等到这个线程出来,另个线程才能进入。线程安全类与线程不安全类的区分就在于这个类中有没有充分运用synchronized修饰各个方法)
直接扔代码!!
public class Singleton_lan {
    //私有化构造方法
    private Singleton_lan () {

    }
    //声明本类类型的引用指向本类类型的对象,并用private static修饰
    private static Singleton_lan sl =null;
    //创建共有的get方法,负责将上述对象返回出去,用public static修饰
    public static synchronized Singleton_lan getSingleton () {
        if (null == sl) {
            sl = new Singleton_lan();
        }
        return sl;
    }
}

现在当两个线程同时调用这个方法的时候,由于synchronized对象锁锁住了代码,只能放一个线程进去,等到线程结束,对象已经new完,这时另一个线程调用方法 null = sl 已经不成立。

延申一波 :当synchronized修饰静态方法时,也可以这样做:

public class Singleton_lan {
    //私有化构造方法
    private Singleton_lan () {

    }
    //声明本类类型的引用指向本类类型的对象,并用private static修饰
    private static Singleton_lan sl =null;
    //创建共有的get方法,负责将上述对象返回出去,用public static修饰
    public static /*synchronized*/ Singleton_lan getSingleton () {
        synchronized (Singleton_lan.class) {    //同步代码块儿
            if (null == sl) {
                sl = new Singleton_lan();
            }
            return sl;
        }
    }
}

这里运用到了反射机制,一个类的运行时类信息也就是class类只有一个。

懒汉式的补救办法 (下)

其实上面代码的完全可以解决多线程带来的问题,但是有必要每次调用方法获取这个唯一的对象前还得!!加锁,然后去判断null = sl 嘛? 其实只需要第一次的时候加锁 判断,以后就不需要了, 以后就直接return出去了鸭。

扔代码
public class Singleton_lan {
    //私有化构造方法
    private Singleton_lan() {

    }

    //声明本类类型的引用指向本类类型的对象,并用private static修饰
    private static Singleton_lan sl = null;

    //创建共有的get方法,负责将上述对象返回出去,用public static修饰
    public static /*synchronized*/ Singleton_lan getSingleton() {
        /*synchronized (Singleton_lan.class) {    //同步代码块儿
            if (null == sl) {
                sl = new Singleton_lan();
            }
            return sl;
        }*/
        if (null == sl) { //重点在这!!这里又做了一重判断
            synchronized (Singleton_lan.class) {    //同步代码块儿
                if (null == sl) {
                    sl = new Singleton_lan();
                }
            }
        }
        return sl;//不要忘记return永远是在最外层抛这个唯一的对象的
    }
}

大功告成 ,呼~

额…瑕疵可能会很多,但是真的有用心写 ,希望能帮到你哦~! 也希望大佬帮忙指正。 鞠躬

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值