单例模式的优点之为什么要使用单例模式
1.由于单例模式只生成一个实例,减小系统性能开销,当一个对象的产生需要多个资源时(读取配置文件、产生其他依赖)那么我们可以在应用启动时直接创建该对象实例,然后永久驻留内存。
2.单例模式可以在系统设置全局访问点,优化共享资源访问。例如可以设计一个单例类,负责所有数据表的映射处理。
五种单例模式的实现方式
饿汉式单例模式(线程安全,调用效率高,不能延时加载)
package singal
;
public class Hungry {
private Byte aByte1
[]=new Byte[1024];
private Byte aByte2
[]=new Byte[1024];
private Byte aByte3
[]=new Byte[1024];
private Byte aByte4
[]=new Byte[1024];
private Hungry(){ }
private final static Hungry HUNGRY
=new Hungry();
public static Hungry
getInstance(){
return HUNGRY
;
}
}
懒汉式单例模式(调用效率不高、可以延时加载)
package singal
;
public class LazySingal {
private LazySingal(){
}
private static LazySingal LAZY_SINGAL
;
public static synchronized LazySingal
getInstance(){
if (LAZY_SINGAL
==null
){
LAZY_SINGAL
=new LazySingal();
}
return LAZY_SINGAL
;
}
}
DCL懒汉式单例模式(volatile)(懒汉式以及双重检测锁式)
package singal
;
import javax
.sound
.midi
.Soundbank
;
import java
.lang
.reflect
.Constructor
;
import java
.lang
.reflect
.Field
;
public class LazyMan {
private static Boolean XUAN
=false;
private LazyMan(){
synchronized (LazyMan
.class){
if (XUAN
==false){
XUAN
=true;
}else throw new RuntimeException("不要试图通过反射来破坏单例!");
}
}
private volatile static LazyMan lazyMan
;
public static LazyMan
getInstance(){
if (lazyMan
==null
){
synchronized (LazyMan
.class){
if (lazyMan
==null
){
lazyMan
= new LazyMan();
}
}
}
return lazyMan
;
}
public static void main(String
[] args
) throws Exception
{
Field xuan
= LazyMan
.class.getDeclaredField("XUAN");
xuan
.setAccessible(true);
Constructor
<LazyMan> declaredConstructor
= LazyMan
.class.getDeclaredConstructor(null
);
declaredConstructor
.setAccessible(true);
LazyMan newInstance
= declaredConstructor
.newInstance();
xuan
.set(newInstance
,false);
LazyMan newInstance1
= declaredConstructor
.newInstance();
System
.out
.println(newInstance1
);
System
.out
.println(newInstance
);
System
.out
.println(newInstance1
==newInstance
);
}
}
singal.LazyMan@4e50df2e
<<-----------上面的输出结果 明显是单例被反射破坏了 结果产生了两个对象
singal.LazyMan@1d81eb93
false
静态内部类(线程安全、调用效率高、可以延时加载)
package singal
;
public class Holder {
private Holder(){
}
public static Holder
getInstance(){
return InnerClass
.HOLDER
;
}
public static class InnerClass{
private static final Holder HOLDER
=new Holder();
}
}
以上都是可以通过反射直接破坏单例模式的
枚举(反射和反序列化不能破坏的单例)(调用效率高,不能延时加载)
@CallerSensitive
@ForceInline
public T
newInstance(Object
... initargs
)
throws InstantiationException
, IllegalAccessException
,
IllegalArgumentException
, InvocationTargetException
{
Class
<?> caller
= override
? null
: Reflection
.getCallerClass();
return newInstanceWithCaller(initargs
, !override
, caller
);
}
T
newInstanceWithCaller(Object
[] args
, boolean checkAccess
, Class
<?> caller
)
throws InstantiationException
, IllegalAccessException
,
InvocationTargetException
{
if (checkAccess
)
checkAccess(caller
, clazz
, clazz
, 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(args
);
return inst
;
}
package singal
;
import java
.lang
.reflect
.Constructor
;
public enum EnumSingle
{
INSTANCE
;
public EnumSingle
getInstance(){
return INSTANCE
;
}
}
class Test{
public static void main(String
[] args
) throws Exception
{
EnumSingle single1
= EnumSingle
.INSTANCE
;
Constructor
<EnumSingle> declaredConstructor
= EnumSingle
.class.getDeclaredConstructor(String
.class,int.class);
declaredConstructor
.setAccessible(true);
EnumSingle single2
= declaredConstructor
.newInstance();
System
.out
.println(single1
);
System
.out
.println(single2
);
}
}
Exception
in thread
"main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller
(Constructor.java:493
)
at java.base/java.lang.reflect.Constructor.newInstance
(Constructor.java:481
)
at singal.Test.main
(EnumSingle.java:28
) <<<------ 控制台输出结果
单例模式的应用场景
Windows的任务管理器就是典型的单例模式(不论你打开多少次任务管理器 只能打开一个)应用程序的日志应用,因为要实现实时的动态更新,一般只能有一个实例去操作。项目中读取配置文件的类,一般只有一个对象。(Properties加载DataSource)数据库连接池也是采用单例模式spring的IOC容器中的每一个bean都是单例的这样能方便容器管理SpringMVC的控制器也是单例的
如何选用合适的单例模式?
单例对象占用资源少,不需要延迟加载 此时枚举好用于饿汉式单例对象占用资源多,需要延时加载 静态内部类好用于懒汉式