java设计模式之单例模式你真的会了吗?(饿汉式篇)

java设计模式之单例模式你真的会了吗?(饿汉式篇)

一、什么是单例模式?

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

二、单例模式之饿汉式有什么特点以及优缺点?

  • 构造方法私有化
  • 类初始化时自行实例化
  • 对外提供统一的静态工厂方法返回实例
  • 优点:实现简单,不存在多线程问题。
  • 缺点:class类在被加载的时候创建Singleton实例,如果对象创建后一直没有使用,则会浪费很大的内存空间,此方法不适合创建大对象。

三、我们常见的饿汉式单例的写法

public class HungrySingleton implements Serializable{
    private static final long serialVersionUID = -1008862048413058509L;

    private HungrySingleton() {}

    //类初始化时自行实例化
    private static final HungrySingleton INSTANCE = new HungrySingleton();

    //对外提供统一的静态工厂方法返回实例
    public static HungrySingleton getInstance() {
        return INSTANCE;
    }
}

四、上面的单例模式是真的就是单例的吗?运行下面代码中的main方法试试?

public class HungrySingleton implements Serializable{
    private static final long serialVersionUID = -1008862048413058509L;

    private HungrySingleton() {}

    //类初始化时自行实例化
    private static final HungrySingleton INSTANCE = new HungrySingleton();

    //对外提供统一的静态工厂方法返回实例
    public static HungrySingleton getInstance() {
        return INSTANCE;
    }
    
    //测试方法
    public static void main(String[] args) {
        //序列化
        ObjectOutputStream oo;
        try {
            oo = new ObjectOutputStream(new FileOutputStream(new File("C:/HungrySingleton.txt")));
            oo.writeObject(HungrySingleton.getInstance());
            oo.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //反序列化
        ObjectInputStream ois;
        HungrySingleton hungrySingleton = null;
        try {
            ois = new ObjectInputStream(new FileInputStream(new File("C:/HungrySingleton.txt")));
            hungrySingleton = (HungrySingleton) ois.readObject();
            ois.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println("是否相同:" + (hungrySingleton == HungrySingleton.getInstance()));
    }
}

结论:通过序列化和反序列化的操作后我们成功的破坏了“单例”

五、为什么饿汉式单例会被序列化和反序列化的操作破坏?

结论:反序列化时如果没有readResolve方法则会返回newInstance的实例,所以导致“单例”被破坏了,如果有readResolve方法则会调用此方法,返回readResolve方法返回的对象

六、所以如何防止饿汉式单例被破坏?需要添加一个readResolve方法并且方法中返回实例即可。

public class HungrySingleton implements Serializable{
    private static final long serialVersionUID = -1008862048413058509L;

    private HungrySingleton() {}

    //类初始化时自行实例化
    private static final HungrySingleton INSTANCE = new HungrySingleton();

    //对外提供统一的静态工厂方法返回实例
    public static HungrySingleton getInstance() {
        return INSTANCE;
    }

    /**
     * 防止采用序列化、反序列化的方式破坏单例
     */
    private Object readResolve() {
        return INSTANCE;
    }

    //测试方法
    public static void main(String[] args) {
        //序列化
        ObjectOutputStream oo;
        try {
            oo = new ObjectOutputStream(new FileOutputStream(new File("C:/HungrySingleton.txt")));
            oo.writeObject(HungrySingleton.getInstance());
            oo.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //反序列化
        ObjectInputStream ois;
        HungrySingleton hungrySingleton = null;
        try {
            ois = new ObjectInputStream(new FileInputStream(new File("C:/HungrySingleton.txt")));
            hungrySingleton = (HungrySingleton) ois.readObject();
            ois.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println("是否相同:" + (hungrySingleton == HungrySingleton.getInstance()));
    }
}

PS:如果你看到了这篇文章,并且觉得对你有帮助,请给个关注和点赞,谢谢!

posted @ 2021-05-18 11:34  野生D程序猿  阅读(145)  评论(0编辑  收藏  举报