设计模式之单例模式Single
饿汉式单例
package com.zhang.single;
// 饿汉式单例
public class Hungry {
private Hungry() {
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance() {
return HUNGRY;
}
}
懒汉式单例
-
单线程下的懒汉式单例
package com.zhang.single; public class LazyMan { private LazyMan() { } public static LazyMan lazyMan; public static LazyMan getInstance() { if (lazyMan == null) { lazyMan = new LazyMan(); } return lazyMan; } } -
多线程下的懒汉式单例偶尔会失败
package com.zhang.single; // 普通懒汉式单例 public class LazyMan { private LazyMan() { System.out.println(Thread.currentThread().getName() + "ok"); } private static LazyMan lazyMan; public static LazyMan getInstance() { if (lazyMan == null) { lazyMan = new LazyMan(); } return lazyMan; } // 多线程并发测试 public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(()->{ LazyMan.getInstance(); }).start(); } } } -
双重检测锁模式的懒汉式单例 DCL懒汉式
在极端模式下,lazy = new Lazy(); 不是一个原子性操作
1、分配内存空间
2、执行构造方法,初始化对象
3、把这个对象指向这个空间
此操作可能会导致指令重排package com.zhang.single; // 双重检测锁模式的懒汉式单例 DCL懒汉式 public class LazyMan { private LazyMan() { System.out.println(Thread.currentThread().getName() + "ok"); } private volatile static LazyMan lazyMan; public static LazyMan getInstance() { if (lazyMan == null) { synchronized (LazyMan.class) { if (lazyMan == null) { lazyMan = new LazyMan(); } } } return lazyMan; } }
静态内部类单例
package com.zhang.single;
// 静态内部类
public class Holder {
private Holder() {
}
public static Holder getInstance() {
return InnerClass.HOLDER;
}
public static class InnerClass {
private static final Holder HOLDER = new Holder();
}
}
上述这几种单例模式都是不安全的,可以通过反射破坏
下面通过构造器获取一个对象,和通过反射获取一个对象,明显两者不是同一个,单例模式破坏
package com.zhang.single;
import java.lang.reflect.Constructor;
// 通过反射破坏双重检测锁懒汉式单例
public class Lazy {
private Lazy() {
System.out.println(Thread.currentThread().getName() + "启动");
}
private volatile static Lazy lazy;
public static Lazy getInstance() {
if (lazy == null) {
synchronized (Lazy.class) {
if (lazy == null) {
lazy = new Lazy();
}
}
}
return lazy;
}
public static void main(String[] args) throws Exception {
// 正常获取一个对象
Lazy instance = Lazy.getInstance();
// 通过反射获取构造器
Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
// 暴力破解
declaredConstructor.setAccessible(true);
// 通过反射获取的构造器new一个实例对象
Lazy lazy = declaredConstructor.newInstance();
// 测试两个是否相同
System.out.println("instance == lazy:" + (instance == lazy));
}
}
解决办法:在构造器中加入一个判断,并抛出异常。(三重检测)
private Lazy() {
if (lazy != null) {
throw new RuntimeException("不要试图用反射破坏异常");
}
}
但是,在上述单例模式中,两个对象都是通过反射获得,仍然会破坏单例模式
public static void main(String[] args) throws Exception {
// 通过反射获取构造器
Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
// 暴力破解
declaredConstructor.setAccessible(true);
// 通过反射获取的构造器new两个实例对象
Lazy lazy1 = declaredConstructor.newInstance();
Lazy lazy2 = declaredConstructor.newInstance();
// 测试两个是否相同
System.out.println("instance == lazy:" + (lazy1 == lazy2));
}
解决办法:在类中加入一个静态变量标志位,设置为false,以此来判断。
private static boolean zhang = false;
private Lazy() {
if (zhang == false) {
zhang = true;
} else {
throw new RuntimeException("不要试图用反射破坏异常");
}
}
但是,道高一尺魔高一丈,这些方法依旧不是安全的。如:通过将标志位私有权限破坏,仍然可以破坏单例模式。
枚举自带单例模式
通过此方法得到的结果为true
package com.zhang.single;
// enum枚举本身也是一个class类
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance() {
return INSTANCE;
}
}
class Test {
public static void main(String[] args) {
EnumSingle instance1 = EnumSingle.INSTANCE;
EnumSingle instance2 = EnumSingle.INSTANCE;
System.out.println("(instance1 与 instance2) = " + (instance1 == instance2));
}
}
通过反射尝试破坏该枚举类
package com.zhang.single;
import java.lang.reflect.Constructor;
// enum枚举本身也是一个class类
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance() {
return INSTANCE;
}
}
class Test {
public static void main(String[] args) throws Exception {
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
System.out.println("(instance1 与 instance2) = " + (instance1 == instance2));
}
}
运行结果如下,告诉我们没有这个空参构造方法
Exception in thread "main" java.lang.NoSuchMethodException: com.zhang.single.EnumSingle.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.getDeclaredConstructor(Class.java:2178)
at com.zhang.single.Test.main(EnumSingle.java:17)
只能通过jad.exe反编译工具,将该类文件反编译成为一个java文件
发现该java文件中有一个有参构造器
private EnumSingle(String s, int i) {
super(s, i);
}
根据上述代码,继续尝试破坏
class Test {
public static void main(String[] args) throws Exception {
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class, int.class);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
System.out.println("(instance1 与 instance2) = " + (instance1 == instance2));
}
}
运行结果:
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
at com.zhang.single.Test.main(EnumSingle.java:19)
结论:枚举单例确实不能被破坏

浙公网安备 33010602011771号