1.简介
单例模式的特点:(1)单例类只能有一个实例
(2)单例类必须自己创建自己的唯一实例
(3)单例类必须给所有其他对象提供这一事例
2.懒汉式
#线程不安全,第一次类加载反应稍慢
public class Singleton{
private static Singleton single= null;
//构造方法私有化
private Singleton(){}
public static Singleton getInstance(){
if(single == null){
single = new Singleton();
}
return single;
}
}
3.饿汉式
#线程安全,调用效率高,但是不能延时加载,但是类一加载就实例化,提前占用了系统资源
public class Singleton{
private static Singleton single= new Singleton();
//构造方法私有化
private Singleton(){}
public static Singleton getInstance(){
return single;
}
}
4.双重锁
public class Singleton{
private volatile static Singleton single = null;
private Singleton(){}
public static Singleton getInstance(){
if(single == null){
synchionized(Singleton.class){
if(single == null){
single = new Singleton();
}
}
}
return single;
}
}
5.静态内部类
#线程安全,推荐使用
public class Singleton{
private static class SingletonClassInstance{
private static final Singleton instance = new Singleton();
}
private Singleton(){}
public static Singleton getInstance(){
return SingletonClassInstance.instance;
}
}
6.枚举类
#线程安全,调用效率高,不能延时加载,可以天然的防止反射和反序列化调用
经验丰富的 Android 开发人员都会尽量避免使用枚举。官方文档有说明:相比于静态常量Enum会花费两倍以上的内存。
public enum Singleton{
//枚举元素本身就是单例
INSTANCE;
//添加自己需要的操作
public void operation(){
}
}
7.利用容器实现单例
#利用了 HashMap 容器 key 不可重复的特性。
- 优点:这种实现方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一接口进行获取操作,降低用户使用成本,也对用户隐藏了具体实现,降低耦合度。
- 缺点:没有私有化构造方法,用户可以 new 出新的实例对象。
import java.util.HashMap;
import java.util.Map;
public class Singleton {
private static Map<String, Object> objMap = new HashMap<String, Object>();
private Singleton() {
}
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);
}
}
8.防止反射破坏单例
public class Singleton {
private static boolean flag = false;
private Singleton(){
synchronized(Singleton.class)
{
if(flag == false)
{
flag = !flag;
}
else
{
throw new RuntimeException("单例模式被侵犯!");
}
}
}
private static class InnerClassSingleton {
private final static Singleton sSingleton = new Singleton();
}
public static Singleton getInstance() {
return InnerClassSingleton.sSingleton;
}
}
9.防止序列化和反序列化破坏单例
#通过序列化可以讲一个对象实例写入到磁盘中,通过反序列化再读取回来的时候,即便构造方法是私有的,也依然可以通过特殊的途径,创建出一个新的实例,相当于调用了该类的构造函数。要避免这个问题,我们需要在代码中加入如下方法,让其在反序列化过程中执行 readResolve 方法时返回 sSingleton 对象。
private Object readResolve() throws ObjectStreamException {
return sSingleton;
}