• 单例模式


    个人博客

    http://www.milovetingting.cn

    单例模式

    模式介绍

    整个系统只需要拥有一个全局对象,这样有利于我们协调系统整体的行为。

    定义

    确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

    实现单例模式主要有如下几个关键点:

    • 构造函数不对外开放,一般为private

    • 通过一个静态方法或者枚举返回单例类对象

    • 确保单例类的对象有且只有一个,尤其是在多线程环境下

    • 确保单例类对象在反序列化时不会重新构建对象

    单例模式的写法

    • 饿汉式

    在声明静态对象时已经初始化。

    public class Singleton {
    
    	private static final Singleton mInstance = new Singleton();
    
    	private Singleton() {
    
    	}
    
    	public static Singleton getInstance() {
    		return mInstance;
    	}
    
    }
    
    • 懒汉式

    懒汉模式是声明一个静态对象,并且在用户第一次调用getInstance时进行初始化。优点是:单例只在使用时才会被实例化。缺点是:第一次加载需要及时进行实例化,反应稍慢,每次调用时都进行同步,造成不必要的同步开销。这种模式不建议使用。

    public class Singleton {
    
    	private static volatile Singleton mInstance;
    
    	private Singleton() {
    
    	}
    
    	public static synchronized Singleton getInstance() {
    		if (mInstance == null) {
    			mInstance = new Singleton();
    		}
    		return mInstance;
    	}
    
    }
    
    • DCL(Double Check Lock)实现单例

    DCL模式实现单例的优点是既能够在需要时才初始化单例,又能够保证线程安全,且单例对象初始化后调用getInstance不进行同步锁。

    public class Singleton {
    
    	private static volatile Singleton mInstance;
    
    	private Singleton() {
    
    	}
    
    	public static Singleton getInstance() {
    		if (mInstance == null) {
    			synchronized (Singleton.class) {
    				if (mInstance == null) {
    					mInstance = new Singleton();
    				}
    			}
    		}
    		return mInstance;
    	}
    
    }
    
    • 静态内部类单例模式

    当第一次加载Singleton类时并不会初始化sInstance,只有在第一次调用Sington的getInstance方法时才会导致sInstance被初始化。这是推荐使用的单例模式实现方式。

    public class Singleton {
    
    	private Singleton() {
    
    	}
    
    	public static Singleton getInstance() {
    		return SingletonHolder.sInstance;
    	}
    
    	private static class SingletonHolder {
    		private static final Singleton sInstance = new Singleton();
    	}
    
    }
    
    • 枚举单例

    在上述的几种单例模式实现中,在反序列化的情况下,它们就会出现重新创建对象。

    序列化可以将一个单例的实例对象写到磁盘,然后再读回来,从而有效地获得一个实例。即使构造函数是私有的,反序列化时依然可以通过特殊的途径去创建一个新的实例。反序列化操作提供了一个特别的钩子函数,类中具有一个私有的readResolve()函数,这个函数可以让开发人员控制对象的反序列化。如果要杜绝对象在反序列化时重新生成对象,必须加入readResolve函数。而枚举则不存在这个问题。

    private Object readResolve() throws ObjectStreamException {
    		return mInstance;
    	}
    

    对于序列化,有两点需要注意:

    1. 可序列化类中的字段类型不是Java的内置类型,那么该字段也需要实现Serializable接口

    2. 如果调整了可序列化类的内部结构,如新增,去除某个字段,但没有修改serialVersionUID,那么会引发java.io.InvalidClassException异常或者导致某个属性为0或者null。此时的最好方案是直接将serialVersionUID设置为0L,这样即使修改类的内部结构,反序列化也不会报错,只是新修改的字段会为0或者null。

    写法简单是枚举单例的最大优点。枚举在Java中与普通的类是一样的,不仅能够有字段,还能够有自己的方法。最重要的是默认枚举实例的创建是线程安全的,并且在任何情况下它都是一个单例。

    public enum Singleton {
    
    	INSTANCE;
    	
    	public void doSomething()
    	{
    		
    	}
    
    }
    
    • 使用容器实现单例模式
    public class SingletonManager<T> {
    
    	private static Map<String, Object> objMap = new HashMap<>();
    
    	private SingletonManager() {
    
    	}
    
    	public static void registerService(String key, Object instance) {
    		if (!objMap.containsKey(key)) {
    			objMap.put(key, instance);
    		}
    	}
    
    	public static Object getService(String key) {
    		return objMap.get(key);
    	}
    
    }
    

    在程序的初始阶段,将多种单例类型注入到一个统一的管理类中,在使用时根据key获取对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏具体实现,降低耦合度。

  • 相关阅读:
    api接口统一管理
    axios封装
    事件监听和事件模型
    W3C标准
    Redis安装(PHPredis服务+windows的redis环境)
    Redis介绍
    jQuery ajax方法小结
    博客园鼠标特效
    PHP---截取七牛地址中的文件名
    jQuery---显示和隐藏
  • 原文地址:https://www.cnblogs.com/milovetingting/p/12177217.html
Copyright © 2020-2023  润新知