目录
一 单例模式的应用场景
单例模式(Singleton Pattern)是指一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。
例:公司的CEO
二 饿汉式单例
饿汉式单例模式是在类加载的时候就立即初始化了,并创立对象,不存在线程安全问题。
优点:没有加锁效率高,用户体验比懒汉好。
缺点:类加载的时候就初始化,无论是否使用都占用着空间,浪费内存。
代码如下:
public class HungrySingleton {
private static final HungrySingleton hungrySingleton = new HungrySingleton();
//构造私有化
private HungrySingleton() {}
public static HungrySingleton getInstence() {
return hungrySingleton;
}
}
下面是静态代码块饿汉模式/
public class HungryStaticSingleton {
private static final HungryStaticSingleton hungrySingleton ;
static {
hungrySingleton = new HungryStaticSingleton();
}
//构造私有化
private HungryStaticSingleton() {}
public static HungryStaticSingleton getInstence() {
return hungrySingleton;
}
}
三 懒汉式单例
在外部掉用的时候才会加载
优点:在掉用的时候才会初始化节约内存
缺点:存在线程不安全问题,。
代码如下:
public class LazySingleton {
private static LazySingleton lazySingleton = null;
private LazySingleton() { }
//调用的时候才开始加载
//加入synchronized关键字保证线程安全
public synchronized static LazySingleton getInstence() {
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
//兼顾效率,并且达到线程安全目的双重检查锁
public class LazyDoubleCheckSingleton {
private static LazyDoubleCheckSingleton lazySingleton = null;
private LazyDoubleCheckSingleton() {}
//调用的时候才开始加载
//双重检索锁 -----进行两次判断
public static LazyDoubleCheckSingleton getInstence() {
if (lazySingleton == null) {
synchronized (LazyDoubleCheckSingleton.class) {
if (lazySingleton == null) {
lazySingleton = new LazyDoubleCheckSingleton();
}
}
}
return lazySingleton;
}
}
虽然改良版的要好很多但是有synchronized自然就会影响效率 当然还有一种终极版本静态内部类方式代码如下:
public class LazyInnerClassSingleton {
private LazyInnerClassSingleton() {}
//1.单例空间共享
//2.保证不会被重写/覆盖
public static final LazyInnerClassSingleton getInstance() {
return LazyClass.LAZY;
}
//默认不加载
private static class LazyClass {
private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
}
}
四 注册式单例
注册式单例有两种一为枚举型单例,容器式单例
枚举创建的单例无法被反射破坏,无法被反序列化破坏,并且线程安全。
代码如下:
public enum EnumSingleton {
INSTENCT;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumSingleton getInstenct() {
return INSTENCT;
}
}
容器缓存方式的优点在于创建实例非常多的情况,便于管理,但是这是线程不安全的。
代码如下:
public class ContainerSingletion {
private ContainerSingletion() { }
private static Map<String, Object> ioc = new ConcurrentHashMap<String, Object>();
public static Object getBean(String clssName) {
synchronized (ioc){
if (!ioc.containsKey(clssName)) {
Object obj = null;
try {
obj = Class.forName(clssName).newInstance();
ioc.put(clssName, obj);
} catch (Exception e) {
e.printStackTrace();
}
return obj;
} else {
return ioc.get(clssName);
}
}
}
}
五 破坏单例的方式
破坏单例模式有两种: 反射强行破坏,序列化与反序列化的破坏。
Class<?> clzz = LazyInnerClassSingleton.class;
//拿到LazyInnerClassSingleton的构造函数
Constructor constructor = clzz.getDeclaredConstructor(null);
constructor.setAccessible(true);
//调用两次构造方法
Object o1 = constructor.newInstance();
Object o2 = constructor.newInstance();
System.out.println(o1 == o2);
----------------------------------------------------------------------------------
// 上面则是反射破坏:
// 解决办法:就拿静态内部类举例
//在构造方法中加入判断防止反射强行入侵
private LazyInnerClassSingleton() {
if (LazyClass.LAZY==null){
throw new RuntimeException("还想进来?此路不通!!!");
}
}
序列化与反序列化破坏: 我们将一个单例创建好写入磁盘,下次再使用的时候从磁盘中读取出来,反序列化转成内存对象会重新分配内存,即重新创建对象。
解决办法:在单例类中重写readResolve()方法 返回当前对象。
//重写这个方法防止序列化破坏
private Object readResolve() {
return INSTENCE;
}
单例模式所有代码(含测试):https://github.com/sqwf/patten.git