1. 首页
  2. 编程面试题
  3. Java
  4. 设计模式

单例模式设计模式--饿汉模式



饿汉模式

package instance;

public class SingleTon {
    // 饿汉模式,构造方法必须私有,所有的单例构造方法必须私有,才能保证构造方法只有本类能够使用
    private SingleTon(){
        System.out.println("构造方法执行了,创建单例对象");
    }
    // 私有构造内部可以调用,创建实例
    private static final SingleTon instance = new SingleTon();

    // 获取实例方法
    public static SingleTon getInstance() {
        return instance;
    }
}

测试类

package instance;

public class TestSingleTon {
    public static void main(String[] args) {

        System.out.println(SingleTon.getInstance());
        System.out.println(SingleTon.getInstance());
    }
}

输出结果:

类加载的时候初始化一次,初始化的时候通过new SingleTon()就生成了单例对象,多次创建单例地址一样,说明验证成功,多次创建单例对象并不是每次调用每次生成,而是直接用初始化好的单例对象

构造方法执行了,创建单例对象
instance.SingleTon@67424e82
instance.SingleTon@67424e82

格外注意:单例是可以被破坏的,可以通过反射、反序列化破坏,下面演示代码

通过反射破坏单例

public class TestSingleTon {
    public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {

        System.out.println(SingleTon.getInstance());
        System.out.println(SingleTon.getInstance());

        fanshe(SingleTon.class);
    }

// 通过反射破坏单例模式
    public static void fanshe(Class<?> clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Constructor<?> constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        System.out.println("反射创建单例:" + constructor.newInstance());
    }
}

输出结果可以看到,通过反射拿到构造方法创建出来的单例对象地址已经发生了变化,单例已经被破坏了

构造方法执行了,创建单例对象
instance.SingleTon@67424e82
instance.SingleTon@67424e82
构造方法执行了,创建单例对象
反射创建单例:instance.SingleTon@42110406

那么如何避免单例模式被破坏?

只需要在构造方法加一个非null判断抛出异常即可

private SingleTon(){
        if (instance!=null){
            throw new RuntimeException("单例对象不能重复创建");
        }
        System.out.println("构造方法执行了,创建单例对象");
    }

重新运行代码,打印堆栈信息如下,通过反射创建单例的时候,就抛出了异常

构造方法执行了,创建单例对象
instance.SingleTon@67424e82
instance.SingleTon@67424e82
Exception in thread "main" 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 instance.TestSingleTon.fanshe(TestSingleTon.java:18)
    at instance.TestSingleTon.main(TestSingleTon.java:12)
Caused by: java.lang.RuntimeException: 单例对象不能重复创建
    at instance.SingleTon.<init>(SingleTon.java:7)
    ... 6 more

通过反序列化破坏单例

// 首先让类实现以下序列化接口
public class SingleTon implements Serializable{}

测试类:

// 调用反序列化方法
fanxuliehua(SingleTon.getInstance());
// 定义反序列化方法
    private static void fanxuliehua(Object instance) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(instance);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
        System.out.println("反序列化创建对象:"+ois.readObject());
    }

// 对象地址不一样,说明单例被破坏

构造方法执行了,创建单例对象
instance.SingleTon@67424e82
instance.SingleTon@67424e82
反序列化创建对象:instance.SingleTon@ea30797

反序列化破坏单例解决办法,类中添加如下方法,

 public Object readResolve(){
        return instance;
    }

输出结果:

构造方法执行了,创建单例对象
instance.SingleTon@67424e82
instance.SingleTon@67424e82
反序列化创建对象:instance.SingleTon@67424e82

通过unsafe破坏单例

// unsafe破坏单例,unsafe为jdk里面的类
    private static void unsafe(Class<?> clazz) throws InstantiationException {
        Object o = UnsafeUtils.getUnsafe().allocateInstance(clazz);
        System.out.println("unsafe创建实例:"+o);
    }
构造方法执行了,创建单例对象
instance.SingleTon@67424e82
instance.SingleTon@67424e82
unsafe创建实例:instance.SingleTon@5387f9e0

unsafe破坏单例暂无解决方法

精品推荐


GPT-4 Plus账号大大大降价了!
免费AI写作工具!

发布者:admin,如若转载,请注明出处:https://ai1024.vip/42878.html

QR code
//