设计模式——单例模式

单例设计模式

  1. 饿汉式(高效,无懒加载,线程安全)
    类一加载就创建对象

    由于类加载器是绝对线程安全的,所以不会出现多个实例的问题
    
  2. 懒汉式(低效,懒加载,同步锁)
    需要对象的时候再实例化,存在线程安全问题,需要同步锁

  3. 双重检查锁式(高效,懒加载,不可靠)
    由于jvm内存模型问题,会导致执行顺序颠倒的可能,不可靠

  4. 静态内部类式(高效,懒加载,推荐)
    类(包括内部类)不会在外部类初始化时初始化,而是需要的时候初始化,保证懒加载
    private static final保证了利用类加载器初始化实例,绝对线程安全
    内部类静态原因:非静态内部类,不能拥有静态属性(容易导致内部类不受外部类限制,直接使用类名得到实例)

  5. 枚举式(高效,无懒加载,绝对安全,推荐)
    由jvm底层保证其绝对安全,不被反射和反序列化破解
    枚举类天生单例

防止被反射和反序列化破解:

防止被反射

1
2
3
4
5
6
private Demo1(){
// 进一步不允许构造器被使用,防止反射
if (demo1 != null){
throw new RuntimeException();
}
}

防止反序列化获取多个对象的漏洞

1
2
3
private Object readResolve() throws ObjectStreamException {
return demo1;
}

下面开始举例:

饿汉式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Demo1 {

// 构造器私有化
private Demo1(){
// 进一步不允许构造器被使用,防止反射
if (demo1 != null){
throw new RuntimeException();
}
}

// 直接创建对象
private static Demo1 demo1 = new Demo1();

public static Demo1 getInstance(){
return demo1;
}

// 防止反序列化获取多个对象的漏洞
private Object readResolve() throws ObjectStreamException {
return demo1;
}
}

懒汉式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Demo2 {

// 私有构造器
private Demo2(){
// 防止反射破解,进一步阻止对象被创建
if (demo2 != null){
throw new RuntimeException();
}
}

// 懒加载的实例
private static Demo2 demo2 = null;

// 获得实例
public static Demo2 getInstance(){
if (demo2 == null){
demo2 = new Demo2();
}
return demo2;
}

// 防止反序列化获取多个对象的漏洞
private Object readResolve() throws ObjectStreamException {
return demo2;
}
}

双重检查锁

因为不推荐使用,暂不实现

静态内部类式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Demo4 {

private static class Demo4Inner{
private static final Demo4 demo4 = new Demo4();
}

public static Demo4 getInstance(){
return Demo4Inner.demo4;
}

private Demo4(){
// 防止反射破解
if (Demo4Inner.demo4 != null){
throw new RuntimeException();
}
}

// 防止反序列化获取多个对象的漏洞
private Object readResolve() throws ObjectStreamException {
return Demo4Inner.demo4;
}
}

枚举式

1
2
3
4
5
6
7
8
public enum  Demo5 {

INSTANCE;

public void method() {
// do something.
}
}

使用反射破解单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class TestReflect {

public static void main(String[] args) {

// Class<Demo1> clazz = Demo1.class;

// 破解Demo1
/*try {
Constructor<Demo1> constructor = clazz.getDeclaredConstructor(null);
constructor.setAccessible(true);
Demo1 instance1 = constructor.newInstance(null);
Demo1 instance2 = constructor.newInstance(null);
System.out.println(instance1);
System.out.println(instance2);
} catch (Exception e) {
e.printStackTrace();
}*/


// 破解Demo4
Class<Demo4> clazz = Demo4.class;
try {
Constructor<Demo4> constructor = clazz.getDeclaredConstructor(null);
constructor.setAccessible(true);
Demo4 instance1 = constructor.newInstance(null);
Demo4 instance2 = constructor.newInstance(null);
System.out.println(instance1);
System.out.println(instance2);
} catch (Exception e) {
e.printStackTrace();
}
}
}