Java设计模式——单例模式

单例模式

一、简介

单例模式

所谓的单例模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类对象只提供一个获取其对象实例的方法(一般都是静态方法)

二、分类

单例模式大致可以分成饿汉式和懒汉式,饿汉和懒汉的区别在于创建对象的时机。
当然,要是按线程安不安全懒汉式还能分成好多种
比如双检锁模式、静态内部类模式、枚举模式等
下面我们会具体说说各种模式

三、饿汉式

饿汉式就是在调用方法获取对象之前就已经创建好了对象,不管你用不用,反正我先创建了,所以叫饿汉式

代码如下:

私有化构造器方式的饿汉式:
私有化构造器,然后直接通过字面量的方式给对象赋值

class Singleton { 
    // 1. 构造器私有化,外部不能 new 
    private Singleton () { 
    } 


    // 2. 本类内部创建对象实例 
    private final static Singleton instance = new Singleton() ; 


    // 3. 对外提供一个公有的静态方法,返回对象实例 
    public static Singleton getInstance () { 
        return instance ; 
    } 
} 

静态代码块方式的饿汉式:
因为静态代码块会随着类的加载而加载,而且只会执行一次,所以也可以用来在调用方法前给对象赋值

class Singleton { 
    // 1. 构造器私有化,外部不能 new 
    private Singleton () { 
    } 


    // 2. 本类内部创建静态变量 
    private static Singleton instance ; 


    // 3. 在静态代码块中创建单例对象 
    static { 
        instance = new Singleton() ; 
    } 


    // 3. 对外提供一个公有的静态方法,返回对象实例 
    public static Singleton getInstance () { 
        return instance ; 
    } 
} 

饿汉式的优缺点
优点:

写法比较简单,在类加载的时候就完成了实例化,避免了线程同步的问题

缺点:

在类加载的时候就完成实例化,并没有实现懒加载(Lazy Loading)的效果。如果从始至终都没有使用过这个对象实例,那么就白白造成了内存的浪费,如果这种类过多甚至会造成溢出

四、懒汉式

饿汉式就是在调用方法获取对象之前不进行实例化,当调用方法获取对象时才会去创建对象,敌不动我不懂所以叫懒汉式

懒汉式分类太多了,慢慢写吧,先看看经典的懒汉式写法

4.1经典懒汉式(线程不安全)

代码如下:

class Singleton { 
    private static Singleton instance ; 

    public Singleton () { 
    } 

    // 提供一个静态的公有方法,当调用该方法时,才去创建 instance 
    public static Singleton getInstance () { 
        if ( instance == null ) { 
            instance = new Singleton() ; 
        } 
        return instance ; 
    } 
} 

然后来说说优缺点吧
经典的懒汉式实现了懒加载,但是只能在单线程的情况下使用
如果在多线程下,一个线程进入了if,还没来得及向下执行,另一个线程也通过了if,这个时候就会产生多个实例的对象,所以在多线程的环境下不可以使用这种方法。

4.2经典懒汉式(线程安全)

代码如下:

class Singleton { 
    private static Singleton instance ; 

    public Singleton () { 
    } 

    // 提供一个静态的公有方法,当调用该方法时,才去创建 instance 
    // 加入 synchronized 同步锁,解决线程安全问题 
    public static synchronized Singleton getInstance () { 
        if ( instance == null ) { 
            instance = new Singleton() ; 
        } 
        return instance ; 
    } 
} 

优缺点:
优点就是解决了4.1的线程不安全问题,
但是这种方式效率太低了,整个同步方法,每个线程在获取对象实例的时候都要进行同步,而其实这个对象只需要进行一次实例化,后面想获得直接返回就好了,根本不需要同步。

4.3懒汉式(双检锁)

代码如下:

public class Singleton { 
    private static volatile Singleton singleton ; 

    public Singleton () { 
    } 


    public static Singleton getInstance () { 

        // 1.先判断是否已经实例化,如果实例化直接返回
        if ( singleton == null ) { 
            // 2.如果没有实例化且有多个线程进去,需要抢锁
            synchronized ( Singleton . class ) { 
                // 再次判断是否被实例化,如果有多个线程从1进来,就可以解决被多次实例化的问题
                if ( singleton == null ) { 
                    singleton = new Singleton() ; 
                } 
            } 
        } 
        return singleton ; 
    } 
} 

双检锁方式的优点:

  • 解决了线程安全的问题
  • 实现了懒加载
  • 效率相比于同步方法的方式要高一点

4.4静态内部类

因为Java内部类的特性,在外部类加载的时候并不会立即加载其内部类,而是当调用其内部类的时候才会发生加载,而静态内部类也只会被实例化一次,所以也可以用来做单例模式。
代码如下:

public class Singleton { 
    private Singleton () { 
    } 

    // 静态内部类,该类中有一个静态属性 Singleton 
    private static class SingletonInstance { 
        private static final Singleton INSTANCE = new Singleton() ; 
    } 

    // 提供一个静态的公有方法,直接返回 SingletonInstance.INSTANCE 
    public static Singleton getInstance () { 
        return SingletonInstance.INSTANCE; 
    } 
} 

静态内部类方式的缺点:

静态内部类的方式有一个致命的缺点,没有办法传参,所以如果不需要传递参数的话,这种方式是比较推荐使用的

4.5枚举

在Java里枚举类实例天生就是线程安全的,而且任何情况下他都是单例的,可以直接作为单例模式来使用。
而且Effective Java中也推荐这种方式,而且枚举类自动支持序列化
代码如下:

public enum Singleton { 
    // 属性 
    INSTANCE ; 

    public void sayHello () { 
        // 行为代码
    } 
} 

关于枚举是否是懒加载的,得到的结论是是懒加载的,但是我并不知道如何去证明。

五、破坏单例模式与预防

破坏单例模式的方式可以分为两种:反射和反序列化

首先讲一讲反射如何破坏单例模式和如何预防

除了枚举单例模式,别的方式我们都可以通过反射获取到单例的类私有的构造器,然后通过私有的构造器创建一个该类的实例出来
预防的话,可以在构造器中判断实例对象是否为null,如果不为null,则直接抛出异常

反序列化破坏单例模式和预防

如果我们的单例类实现了Serializable接口,就可以通过反序列化的方式来破坏单例
解决方案很简单啊,别实现Serializable接口就好了嘛~~
如果你实在头铁,非要去实现Serializable接口,那么你就需要重写Serializable中的反序列化方法readResolve(),在该方法中直接返回单例对象即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值