单例设计模式
单例设计模式的种类很多,也都有自己的优缺点和应用场景,这里只记录常用的几种单例设计模式
饿汉式
public class Singleton {
private final static Singleton instance = new Singleton ( ) ;
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 ( ) ;
public static Runtime getRuntime ( ) {
return currentRuntime;
}
private Runtime ( ) { }
}
双检锁实现懒汉式
public class Singleton {
private static volatile Singleton instance;
private Singleton ( ) {
}
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;
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修饰,如果是则抛出异常,反射失败
@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;
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) ;
}
}