单例模式
强烈推荐的方式:使用枚举:
一个错误的单例模式
/* * 一个有问题的单例模式 * */ public class Single1 { private Single1(){} private Single1 s = null; public Single1 getSingle1(){ //这样子写有问题 if (s == null){ //如果两个程序同时走到这里,就会返回两个实例 return new Single1(); }else{ return s; } } }
几个普通状态线程安全的单例模式,但是可以被反射破解,也可以被序列化和反序列化破解
/* * 一个饿汉,安全的单例模式,推荐 * */ public class Single2 implements Serializable { private static Single2 s = new Single2(); private Single2(){} public static Single2 getSingle2(){ return s; } }
package com.zhuopeng.desigh_model.single_model; import java.io.Serializable; /* * 一个懒汉,双重检查模式,推荐 * 这个例子,对第一个错误的事例进行了改进。如果不使用双重检查模式,每次调用getInstance()都直接进入同步代码块的话,性能不好 * 注意: * instance 使用了volatile修饰 * 原因:代码的重排序,在进行 new Single3()的 时候,需要执行以下的三个步骤 * memory=allocate(); //1:分配内存空间 * ctorInstance(); //2:初始化对象 * singleton=memory; //3:设置singleton指向刚排序的内存空间 * 如果 2 和 3进行 了重排序, 1-3-2的顺序,如果线程A执行了3,这个时候线程B判断if(instance == null)结果为False,直接返回instance, * 就会返回一个没后初始化的instance * */ public class Single3 implements Serializable { private Single3() { } private static volatile Single3 instance; public static Single3 getInstance() { if (instance == null) { synchronized (Single3.class) { if (instance == null) { instance = new Single3(); } } } return instance; } }
package com.zhuopeng.desigh_model.single_model; import java.io.Serializable; /* * 懒汉模式,在调用getInstance() 的时候才初始化一个实例,推荐 * 外部的静态变量,静态代码块在加载类的时候就运行了 * 但是内部的静态类,在外部调用的时候才开始执行静态变量,静态代码块 * */ public class Single4 implements Serializable { private Single4(){} public static Single4 getInstance(){ return Inner.s; } private static class Inner{ private static final Single4 s = new Single4(); } }
升级过后的单例模式,序列化反序列化之后也只有一个实例,也不可以通过反射破解
package com.zhuopeng.desigh_model.single_model; import java.io.Serializable; public class Single6 implements Serializable { //设置一个静态的flag,当类加载的时候这个flag就被创建了 private static boolean flag = false; private static Single6 single6 = new Single6(); private Single6(){ //当上面那个静态变量new的时候flag就会改变,以后通过反射再次创建的时候,就不能创建了 if (!flag){ flag = true; }else{ throw new RuntimeException("单例被破坏"); } } public static Single6 getInstance(){ return single6; } //用于序列化与反序列化的时候,可能会创建出不同的单例 private Object readResolve(){ return single6; } }
一个强烈推荐的:使用枚举的单例模式:
package com.zhuopeng.desigh_model.single_model; import java.io.Serializable; public enum Single5 implements Serializable { INSTANCE; int id; String name; public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } public int getId() { return id; } public String getName() { return name; } }
测试:
package com.zhuopeng.desigh_model.single_model; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; /* * 除了枚举构造的单例模式,其他方法构造的单例模式都可以通过反射来进行破解 * * */ public class Test1 { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //破解使用静态变量构造的单例模式 Constructor cos = Single2.class.getDeclaredConstructor(); //取消访问检查功能 cos.setAccessible(true); Single2 s1 = (Single2) cos.newInstance(); Single2 s2 = (Single2) cos.newInstance(); //生成了四个实例 System.out.println(s1); System.out.println(s1.getSingle2()); System.out.println(s2); System.out.println(s2.getSingle2()); //下面是升级版本的,防止破坏的单例模式 Constructor constructor = Single6.class.getDeclaredConstructor(); constructor.setAccessible(true); //抛出异常,因为Single6的内部静态类已经new出一个实例了 Single6 s6 = (Single6)constructor.newInstance(); System.out.println("s6"+ s6); } }
package com.zhuopeng.desigh_model.single_model; import java.io.*; /* * 部分单例模式在序列化和反序列化后会产生多个实例,除非单例类中重写了readResolve() 方法 * */ public class Test2 { public static void main(String[] args) throws IOException, ClassNotFoundException { Single6 single6 = Single6.getInstance(); FileOutputStream fileOutputStream = new FileOutputStream("o"); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(single6); objectOutputStream.close(); fileOutputStream.close(); FileInputStream fileInputStream = new FileInputStream("o"); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); Single6 s1 = (Single6)objectInputStream.readObject(); objectInputStream.close(); fileInputStream.close(); //结果显示是一个实例,因为Single6中重写了 readResolve()方法,如果没有重写的话,就是两个不同的实例了 System.out.println(single6); System.out.println(s1); } }