创建者模式之单例模式
单例模式简介
只能通过该类的某一方法来创建其对象,不能通过new来创建其对象;同时还要确保该类的对象从始至终只能创建一个对象。
这个类体需要提供一个访问其唯一对象的方法。
创建单例模式的方式
- 饿汉式单例模式 —— 静态成员变量
// 饿汉式 —— 当类被创建时,对象就已经被创建出来。
// 1. 私有构造方法 防止被new创建对象。
private Singleton(){
}
// 2. 在本类中创建本类对象
private static Singleton instance = new Singleton();
// 3. 提供一个公共的访问方式让外界获取该对象。
public static Singleton getInstance(){
return instance;
}
- 饿汉式单例模式 —— 静态代码块
// 私有构造方法
private Singleton(){
}
// 声明singleton类型的变量
private static Singleton instance; // null
// 在静态代码块中进行赋值
static {
instance = new Singleton();
}
// 对外提供获取该类对象的方法
public static Singleton getInstance(){
return instance;
}
- 懒汉式 —— 线程安全
// 懒汉式 —— 只有当需要该对象时,对象才被创建。
// 私有构造
private Singleton(){
}
// 声明该类对象
private static Singleton instance;
// 对外提供访问方式
public static synchronized Singleton getInstance(){
// 判断instance是否为null,如果为null,说明还没有创建Singleton对象
if (instance==null){
instance = new Singleton();
}
return instance;
}
- 懒汉式 —— 双重检查锁方式
// 私有构造方法
private Singleton(){
}
// 声明该类对象 volatile 保证指令有序性
private static volatile Singleton instance; // volatile 确保指令执行的顺序。
// 对外提供公共的访问方式
public static synchronized Singleton getInstance(){
// 第一次判断,如果instance的值不为null,不需要抢占锁,直接返回对象。
if (instance==null){
synchronized (Singleton.class){
// 第二次判断
if (instance==null){
instance = new Singleton();
}
}
}
return instance;
}
- 静态内部类方式
// 私有构造
private Singleton(){
}
// 定义一个静态内部类
private static class SingletonHolder{
// 在内部类中声明并初始化外部类的对象
private static final Singleton INSTANCE = new Singleton();
}
// 提供公共的访问方式
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
- 枚举实现单例模式 —— 唯一一种可以抵抗反射和序列化反序列化破坏的单例模式
public enum Singleton {
INSTANCE;
}
破坏单例模式
- 反射破坏单例模式:其根本原理在于反射调用其无参构造方法多次创建对象而破坏单例模式。
// 1. 获取Singleton的字节码对象
Class<Singleton> clazz = Singleton.class;
// 2. 获取无参构造方法对象
Constructor<Singleton> cons = clazz.getDeclaredConstructor();
// 3. 取消访问检查
cons.setAccessible(true);
// 4. 创建Singleton对象
Singleton s1 = (Singleton) cons.newInstance();
Singleton s2 = (Singleton) cons.newInstance();
System.out.println(s1 == s2);
- 反序列化破坏单例模式:其根本在于反序列化时采用的是反射机制来进行对象的创建。
public static void main(String[] args) throws Exception {
// writeObject2File();
// readObjectFromFile();
// readObjectFromFile();
}
// 向文件中读取数据(对象)
public static void readObjectFromFile() throws Exception {
// 1. 创建输入流对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("G:/Documents/Desktop/a.txt"));
// 2. 读取对象
Singleton instance = (Singleton) ois.readObject();
System.out.println(instance);
// 3. 释放资源
ois.close();
}
// 向文件中写入数据(对象)
public static void writeObject2File() throws Exception {
// 1. 获取Singleton对象
Singleton instance = Singleton.getInstance();
// 2. 创建对象输出流对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("G:/Documents/Desktop/a.txt"));
// 3. 写对象
oos.writeObject(instance);
// 4. 释放资源
oos.close();
}
大佬写的对于序列化反序列化破坏单例模式的讲解博文地址:
序列化对单例模式的破坏
解决破坏单例模式
- 解决反射破坏单例模式:在私有构造方法中进行判断。
synchronized (Singleton.class){
// 判断flag的值,是否是true,如果是,说明非第一次访问,直接抛一个异常即可。
if (flag){
throw new RuntimeException("不能创建多个对象");
}
// 将flag的值设置为true
flag = true;
}
- 解决反序列化破坏单例模式:在类中增加readResolve( )方法
// 类中有该方法就会防止单例模式被破坏
// 当进行反序列化时,会自动调用该方法,将该方法的返回值直接返回。
public Object readResolve(){
return instance;
}