关于java:单例模式详解

45次阅读

共计 2456 个字符,预计需要花费 7 分钟才能阅读完成。

思维导图

概念

保障一个零碎中的一个类,只有一个实例

实现

私有参数

/**
 * 私有参数实现
 */
public class Singleton {public static final Singleton INSTANCE = new Singleton();

    private Singleton() {}

}

长处:简略
毛病:违反了 java 的封装

饿汉式

/**
 * 饿汉式
 */
public class Singleton {private static final Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance(){return INSTANCE;}

}

长处:应用前就创立好实例,多线程平安
毛病:加载 Singleton.class 文件的时候,实例对象就创立了,节约了内存

懒汉式

/**
 * 懒汉式
 */
public class Singleton {

    private static Singleton instance;

    private Singleton() {}

    public static Singleton getInstance(){if(instance == null){instance = new Singleton();
        }
        return instance;
    }

}

毛病:多线程并发下不平安,可能会创立多个实例
长处:只有调用 getInstance()办法才会创立实例

Double CheckLock

/**
 * Double CheckLock
 */
public class Singleton {

    private static Singleton instance;

    private Singleton() {}

    public static Singleton getInstance(){if(instance == null){
            // 为什么要先判断在加锁 因为 instance 只有刚开始的时候为 null,// 前面都不会为 null,防止后续应用中的不必要的同步
            synchronized (Singleton.class){if(instance == null){instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

长处:线程平安,效率高
毛病:实现简单

动态外部类

/**
 * 动态外部类
 */
public class Singleton {private Singleton() { }

    public static Singleton getInstance(){return SingletonHold.instance;}

    public static class SingletonHold{private static Singleton instance = new Singleton();
    }
}

长处:线程平安,只有在调用 getInstance 办法时,才会创立实例

枚举

/**
 * 枚举单例
 */
public enum Singleton {INSTANCE;}

长处:实现超级简略

序列化问题

测试代码

public class Singleton implements Serializable {private Singleton() { }

    public static Singleton getInstance() {return SingletonHold.instance;}

    static class SingletonHold {private static Singleton instance = new Singleton();
    }

    public static void main(String[] args) throws Exception {
        try (FileOutputStream fileOutputStream = new FileOutputStream("d://text.txt");
                FileInputStream fileInputStream = new FileInputStream("d://text.txt");
                ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
                ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)
        ) {Singleton s1 = Singleton.getInstance();
            objectOutputStream.writeObject(s1);
            Singleton s2 = (Singleton) objectInputStream.readObject();
            System.out.println(s1 == s2);
        } catch (Exception e) {System.out.println("序列化异样");
        }
    }
}

测试后果

剖析

序列化后的单例再把他序列化回来,是两个不同的对象

防止序列化问题

 private Object readResolve() throws ObjectStreamException {return SingletonHold.instance;}

在之前的代码中退出这一个办法即可

为什么加了这一段代码就能够呢


343 行:ObjectStreamClass 的 lookup 办法

391 行:lookup 办法中的这一行结构了 entry 对象,进入构造方法

520 行:反射查找要序列化的对象是否有 writeObject 办法
523 行:反射查找要序列化的对象是否有 readObject 办法
526 行:反射查找要序列化的对象是否有 readObjectNoData 办法
531 行:反射查找要序列化的对象是否有 writeReplace 办法
533 行:反射查找要序列化的对象是否有 readResolve 办法

2078 行:反序列化的时候,如果有 readResolve 办法,就调用 readResolve 办法返回序列化对象
所以加了这一段代码之后,方序列化就会返回 SingletonHold.instance,所以是同一个对象

正文完
 0