Java/Kotlin单例模式的几种写法

2019-11-05  本文已影响0人  超级绿茶

单例模式是软件开发中使用率非常高的一种设计模式。其最大的特点是全局访问和节省内存。如果在项目中有某个类的实例需要被多次使用,但又不需要多次实例化时,且实例需要被全局访问时就可以使用考虑把这个类设计成单例模式,单例模式有如下几个特点:

Java发展至今对于单例模式的编码方式已经形成五种经典的单例模式。

一、饿汉单例:

饿汉单例是将类的实例化工作前置到类的加载时期。这种单例解决了多线程访问时实例唯一的问题,且如同一个饿汉一样急不可待的占用了内存。如果类的初始化工作较耗时的话也可能拖慢整个项目的启动速度。

public class Singleton {
    private Singleton() {}
    // 1)声明并实例一个类型为单例类的私有静态变量
    private static Singleton instance = new Singleton();
    // 2)声明一个静态方法并直接返回单例的静态变量
    public static Singleton getInstance() {return instance;}
}

二、懒汉单例:

这种单例会将类的实例化工作延迟到首次需要使用时完成。但在遇到多线程情况下无法保证实例的唯一性。

public class Singleton {
    private Singleton() {}
    // 1)声明一个类型为单例类的私有静态变量
    private static Singleton instance = null;
    // 2)声明一个静态方法用于获取类的实例,该方法在第一次调用时会实例化对象
    public static Singleton getInstance() {
        if (instance == null) {instance = new Singleton();}
        return instance;
    }
}

三、双重锁懒汉单例:

这种写法是对懒汉式的改进,用于保存证在多线程的情况下对象实例的唯一性。

public class Singleton {
    private Singleton() {}
    // 1)声明一个类型为单例类的私有静态变量
    // 关键字volatile保证了线程得到的变量值为最新的
    private volatile static Singleton instance = null;
    // 2)声明一个静态方法用于获取类的实例
    public static Singleton getInstance() {
        if (instance == null) {
            // 3)锁定代码块
            synchronized (Singleton.class) {
                // 在获取锁之后再次判断实例是否为空,防止在竞争情况下对象被多次实例化
                if (instance == null) {instance = new Singleton();}
            }
        }
        return instance;
    }
}

四、静态内部类模式:

是将自己的实例保存为静态内部类的一个属性,而这个实例也必须声明成静态。静态内部类有一个特性;不会随着外部类的加载而加载的特性,只会在需要调用其属于或方法时才会被加载。因此可以利用这一特点在静态内部类中保存一个静态且最终的外部类实例,然后等到需要这个实例的时候再通过外部的静态方法来获取,第一次获取时会实例化外部类对象,而JVM虚拟机在遇到多线程实例化同一个对象时只能允许一个线程完成实例操作,其它线程会处于等待状态,因此这种虚拟机的特性也保障了实例的唯一性。

public class Singleton{
    private Singleton() {}
    // 1)定义静态内部类,并将外部类的实例声明静态属性
    private static class Inner{
        private static final Singleton instance = new Singleton();
    }
    // 2)声明一个静态方法用于获取类的实例
    public static Singleton getInstance() {
        // 第一次运行到这里时静态内部类才会加载,并实例化其属性
        // 当多线竞争时只会有一个线程运行内部类的构造器,其它线程处于等待状态
        return Inner.instance;
    }
}

五、杖举类单例:

这种模式也是充分利用Java特性的一种写法。是目前为止最优美最简练的写法,除了不能延时加载其它都实现了,因此强烈推荐这种写法。

enum SingletonEnum{
    INSTANCE;
    public void methods(){ // 方法的具体实现...}
}

public class SingletonTest {
    public static void main(String[] args){
        SingletonEnum.INSTANCE.methods(); // 就这么调用
    }
}

说完了Java的五种经典模式后再来说说Kotlin的单例模式。Kotlin的单例模式基本是离开不object和companion object两个关键字,需要注意的是在companion object内部是不能定义名为getInstance的方法,会报Platform declaration clash: The following declarations have the same JVM signature的错误。

Kotlin的饿汉单例:

相比Java的饿汉模式Kotlin只需要一句就解决了。

object Singleton // 就这么简单

object在这里主要的作用是定义一个静态类,在静态类内声明的属性和方法都是静态的,因此也不需要专门声明一个静态属性用于保存对象的实例。

Kotlin的懒汉单例

这种模式主要是通过伴生类来实现的。

// 声明类的同时就可以声明私有的构造器
class Singleton private constructor() {
    // 伴生类会延迟到需要调用类中方法时候加载   
    companion object {
        // 保存外部类的实例并重写属于的getter方法    
        private var instance: Singleton? = null
            get() {
                if (field == null) { field = Singleton() }
                return field
            }
        // 通过伴生类返回外部类的实例
        fun getInst(): Singleton {  return instance!! }
    }
}

Kotlin的静态内部类单例

class Singleton private constructor() {
    companion object {
        private var instance: Singleton? = null
            get() {
                if (field == null) { field = Singleton () }
                return field
            }
        @Synchronized
        fun getInst(): Singleton { return instance!! }
    }
}

点击链接加入群聊【口袋里的安卓】:https://jq.qq.com/?_wv=1027&k=5z4fzdT

上一篇下一篇

猜你喜欢

热点阅读