设计模式之单例设计模式

本文详细介绍了Java中的几种单例设计模式,包括饿汉式、双检锁实现的懒汉式、静态内部类实现的懒汉式以及枚举类实现的单例。每种模式都有其特点和适用场景,如饿汉式用空间换时间,静态内部类实现懒加载,枚举单例防止反射和反序列化破坏。此外,还提到了Spring中通过单例注册表实现单例,克服了单例类不能被继承的缺点。
摘要由CSDN通过智能技术生成

单例设计模式

单例设计模式的种类很多,也都有自己的优缺点和应用场景,这里只记录常用的几种单例设计模式

饿汉式

public class Singleton{
	// 类内部实例化
	private final static Singleton instance = new Singleton();
	// 构造器私有化,防止new对象
	private Singleton(){
	
	}
	// 对外提供公有方法调用
	public static Singleton getInstance(){
		return instance;
	}
	
	// 反序列化保证单例
	pubile Object readResolve(){
		return instance;
	}
}
  • 饿汉式即 类加载的时候实例化
  • 没有使用懒加载,类加载就产生对象,如果始终未使用则造成内存浪费
  • 用空间换时间

在JAVA源码中的应用

  • java.lang.Runtime 类,使用饿汉式 保证单例
public class Runtime {
    private static Runtime currentRuntime = new Runtime();

    /**
     * Returns the runtime object associated with the current Java application.
     * Most of the methods of class <code>Runtime</code> are instance
     * methods and must be invoked with respect to the current runtime object.
     *
     * @return  the <code>Runtime</code> object associated with the current
     *          Java application.
     */
    public static Runtime getRuntime() {
        return currentRuntime;
    }

    /** Don't let anyone else instantiate this class */
    private Runtime() {}
}

双检锁实现懒汉式

public class Singleton{
	// 使用 volatile 防止 指令重排序
    private static volatile Singleton instance;
    //构造器私有化,防止new对象
    private Singleton() {
    
    }
    //只有在第一次使用的时候构造实例对象,使用synchronized代码块和双重判断避免多线程问题,并且提供效率
    public static Singleton getInstance() {
        if (instance == null){
            synchronized (Singleton.class){
                if (instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  • 实现懒加载
  • 只有前面的少数线程可能会获取锁,只要实例被创建,后面的线程一般只需第一个if判断就返回了对象

静态内部类实现懒汉式

public class Singleton{
    private static Singleton instance;
    //构造器私有化,防止new对象
    private Singleton() {
    }
    //静态内部类,在外部类加载的时候不会加载静态内部类
    private static class SingletonInstance{
        private static final Singleton INSTANCE = new Singleton();
    }
    
    public static Singleton getInstance(){
        //只有在使用到静态内部类的时候才会加载,并且通过类加载机制保证在初始化的时候只有一个实例产生
        return SingletonInstance.INSTANCE;
    }
}
  • 相比普通的饿汉式或静态代码块实现的单例,静态内部类在外部类被调用时不会立即被初始化,只有当其中的静态内部参数被调用时,才会被初始化。这样保证懒加载
  • 通过静态内部类的初始化Singleton,jvm保证类的初始化只能有一个线程可以同时初始化同一个类,也就是获取初始化InnerClass静态类的锁只会被一个线程获取到,所以在InnerClass被初始化时,其中的静态资源会优先被初始化
  • 在反序列化中使用readResolve()方法 可以解决反序列化问题

枚举类实现单例

public enum EnumSingleton {
    INSTANCE;
}
/**
	编译后,可以看出,枚举类属于 饿汉式
*/
public final class  EnumSingleton extends Enum< EnumSingleton> {
    public static final  EnumSingleton  ENUMSINGLETON;
    public static  EnumSingleton[] values();
    public static  EnumSingleton valueOf(String s);
    static {};
}
  • 枚举单例不能被反射破坏
  • 反射在通过newInstance创建对象时,会检查该类是否ENUM修饰,如果是则抛出异常,反射失败
/**
	Constructor类的newInstance方法
**/
@CallerSensitive
public T newInstance(Object ... initargs)
    throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
    if (!override) {
        if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller, clazz, null, modifiers);
        }
    }
    // 如果 反射的类 为 枚举类,则在此处抛出异常,反射失败
    if ((clazz.getModifiers() & Modifier.ENUM) != 0)
        throw new IllegalArgumentException("Cannot reflectively create enum objects");
    ConstructorAccessor ca = constructorAccessor;   // read volatile
    if (ca == null) {
        ca = acquireConstructorAccessor();
    }
    @SuppressWarnings("unchecked")
    T inst = (T) ca.newInstance(initargs);
    return inst;
}
  • 枚举单例不会被反序列化破坏
  • Java规范字规定,每个枚举类型及其定义的枚举变量在JVM中都是唯一的
  • 因此在枚举类型的序列化和反序列化上,Java做了特殊的规定
  • 在序列化的时候Java仅仅是将枚举对象的name属性输到结果中
  • 反序列化的时候则是通过java.lang.Enum的valueOf()方法来根据名字查找枚举对象
  • 比如说,序列化的时候只将 INSTANCE 这个名称输出,反序列化的时候再通过这个名称,查找对应的枚举类型
  • 因此反序列化后的实例也会和之前被序列化的对象实例相同

Spring中 实现单例模式

  • 上述几种单例模式,都有其优点和应用场景
  • 但其共同缺点是,不能被子类继承
  • spring采用单例注册表的形式实现单例,使其能为普通的类创建单例,也使创建的单例类能够被继承
/**
	单例表实现单例
**/
public class RegSingleton {
    private static HashMap registry = new HashMap();

    //静态块,在类被加载时自动执行  
    static {
        RegSingleton rs = new RegSingleton();
        registry.put(rs.getClass().getName(), rs);
    }

    //受保护的默认构造函数,如果为继承关系,则可以调用,克服了单例类不能为继承的缺点  
    protected RegSingleton() {
    }

    //静态工厂方法,返回此类的唯一实例  
    public static RegSingleton getInstance(String name) {
        if (name == null) {
            name = "RegSingleton";
        }
        if (registry.get(name) == null) {
            try {
                registry.put(name, Class.forName(name).newInstance());
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return (RegSingleton) registry.get(name);
    }
}   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值