Java 单例模式

1.饿汉式

//饿汉式,可能会浪费内存空间
class Hungry {
    private final static Hungry INSTANCE = new Hungry();

    public final static Hungry getInstance() {
        return INSTANCE;
    }

    private Hungry() {

    }
}

2.懒汉式

1.双重检测锁(DCL)

//懒汉式,DCL双重检测锁。在反射下不安全
class Lazy{
    private static Lazy INSTANCE;
    public static Lazy getInstance(){
        if (INSTANCE == null){//如果不为null,就直接返回INSTANCE,不用在进行加锁操作
            synchronized (Lazy.class){
                if (INSTANCE == null){
                    INSTANCE = new Lazy();
                }
            }
        }
        return INSTANCE;
    }
    private Lazy(){}
}

2.在反射下DCL不安全。反射可以直接获取到类的私有构造函数,从而创建对象。

public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new Thread(()->{
                try {
                    Constructor<Lazy> constructor = Lazy.class.getDeclaredConstructor(null);
                    constructor.setAccessible(true);
                    Lazy lazy = constructor.newInstance(null);
                    System.out.println(lazy);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
执行结果:
com.shbj.thread.signal.Lazy@90472a2
com.shbj.thread.signal.Lazy@4388eabf
com.shbj.thread.signal.Lazy@1e057600
com.shbj.thread.signal.Lazy@626213bf
com.shbj.thread.signal.Lazy@616831d4

3.三重检测锁,防止多次创建对象。反射下也不安全,只能防止先调用了getInstance()方法后,使用反射创建对象。

//懒汉式,三重重检测锁。在反射下也不安全
class Lazy2 {
    private static Lazy2 INSTANCE;

    public static Lazy2 getInstance() {
        if (INSTANCE == null) {//如果不为null,就直接返回INSTANCE,不用在进行加锁操作
            synchronized (Lazy2.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Lazy2();
                }
            }
        }
        return INSTANCE;
    }

    private Lazy2() {
        if (INSTANCE != null) {
            throw new RuntimeException("禁止使用反射,破坏单例。");
        }
    }
}
执行结果:
com.shbj.thread.signal.Lazy2@1b6d3586
java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at com.shbj.thread.signal.Demo1.lambda$main$0(Demo1.java:15)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.RuntimeException: 禁止使用反射,破坏单例。
	at com.shbj.thread.signal.Lazy2.<init>(Demo1.java:94)
	... 6 more

4.采用红绿灯,防止多次创建对象。

//懒汉式。在反射下也不安全
class Lazy2 {
    private static Lazy2 INSTANCE;

    public static Lazy2 getInstance() {
        if (INSTANCE == null) {//如果不为null,就直接返回INSTANCE,不用在进行加锁操作
            synchronized (Lazy2.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Lazy2();
                }
            }
        }
        return INSTANCE;
    }

    private Lazy2() {
        if (INSTANCE == null) {
            synchronized (Lazy2.class) {
                if (INSTANCE == null) {
                    INSTANCE = this;
                } else {
                    throw new RuntimeException("禁止使用反射,破坏单例。");
                }
            }
        } else {
            throw new RuntimeException("禁止使用反射,破坏单例。");
        }
    }
}

//如果知道标记名,也可通过反射修改标记,使得可以创建多个对象。
public static void main(String[] args) {
        Lazy2 lazy2 = Lazy2.getInstance();
        System.out.println(lazy2);
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    Constructor<Lazy2> constructor = Lazy2.class.getDeclaredConstructor(null);
                    constructor.setAccessible(true);
                    Field instance = Lazy2.class.getDeclaredField("INSTANCE");
                    instance.setAccessible(true);
                    instance.set(null, null);
                    Lazy2 lazy = constructor.newInstance(null);
                    System.out.println(lazy);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
执行结果:
com.shbj.thread.signal.Lazy2@1b6d3586
com.shbj.thread.signal.Lazy2@4d4f0cbe
com.shbj.thread.signal.Lazy2@1e057600
com.shbj.thread.signal.Lazy2@4533731f
com.shbj.thread.signal.Lazy2@616831d4
java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at com.shbj.thread.signal.Demo1.lambda$main$0(Demo1.java:20)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.RuntimeException: 禁止使用反射,破坏单例。
	at com.shbj.thread.signal.Lazy2.<init>(Demo1.java:109)
	... 6 more

Process finished with exit code 0

3.静态内部类模式

由于内部类只有在被引用的时候才会加载进内存,所以只有在调用了getInstance()方法时,Inner才会被加载,INSTANCE才会被初始化,这样在内部类被加载前就不会浪费内存空间。反射下也不安全。

缺点:不能在创建实例时传入参数,没有懒汉式灵活。

class Outer {
    private Outer() {
    }

    private static class Inner {
        private static final Outer INSTANCE = new Outer();
    }

    public static final Outer getInstance() {
        return Inner.INSTANCE;
    }
}

4.枚举

可以防止反射创建对象。

enum Signal{
    INSTANCE;
    public static Signal getInstance(){
        return INSTANCE;
    }
}

由于反射通过newInstance()创建对象时,会判断要创建的对象类型是否为枚举类型,是枚举就会抛出异常。

if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值