单例模式的目的是创建一个对象,但是反射的方式,或者使用反序列的方式,就会对这种目的造成威胁,那么我们先来看看如何使用反射,如何使用反序列化,创建构造函数私有化的对象,以及我们如何防止反序列化创建对象。
1.补充:如何选用单例模式
(1)占用资源少,不需要延迟加载的,一般使用的是枚举和饿汉式,但是枚举比饿汉式安全。
(2)占用资源大,需要延迟加载,一般使用静态内部类和懒汉式,静态内部类好于懒汉式,因为他更加的懒汉式、线程安全、调用的效率高。
2.使用反射破解的时候,一般是不包括枚举的,所以他的安全性是恒爱的。
3.方式一:反射破解
package kw.test.sjms; import java.io.Serializable; /* * 懒汉式防止破解,原始单例程序 */ public class FDemoLH implements Serializable{ private static FDemoLH instance ; private FDemoLH() {} public synchronized static FDemoLH getinstance(){ if(instance == null) { instance = new FDemoLH(); } return instance; } }
破解程序
package kw.test.sjms; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Constructor; public class FClient { public static void main(String[] args) throws Exception { /* * 反射的方法调用私有的构造函数。 */ FDemoLH fDemoLH1 = FDemoLH.getinstance(); FDemoLH fDemoLH2 = FDemoLH.getinstance(); System.out.println(fDemoLH1 == fDemoLH2); String className = "kw.test.sjms.FClient"; Class<FDemoLH> clazz = (Class<FDemoLH>)Class.forName("kw.test.sjms.FDemoLH"); Constructor<FDemoLH> c= clazz.getDeclaredConstructor(null); c.setAccessible(true); //跳过检查 FDemoLH fd1 = c.newInstance(); FDemoLH fd2 = c.newInstance(); System.out.println(fd1); System.out.println(fd2); } }
方式二:反序列化破解
package kw.test.sjms; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Constructor; public class FClient { public static void main(String[] args) throws Exception { /* * 序列化 */ FDemoLH fDemoLH1 = FDemoLH.getinstance(); System.out.println(fDemoLH1); FileOutputStream fileOutputStream = new FileOutputStream("D:/a.txt"); ObjectOutputStream oos = new ObjectOutputStream(fileOutputStream); oos.writeObject(fDemoLH1); oos.close(); fileOutputStream.close(); /* * 反序列化 */ ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:/a.txt")); FDemoLH fDemoLH = (FDemoLH)ois.readObject(); System.out.println(fDemoLH); } }
其他的单例方式类似
4.防止破解的方式
package kw.test.sjms; import java.io.Serializable; /* * 懒汉式防止破解 */ public class FDemoLH implements Serializable{ private static FDemoLH instance ; private FDemoLH() {} public synchronized static FDemoLH getinstance(){ if(instance == null) { instance = new FDemoLH(); } return instance; } /* * 此方法是私有的,我尝试过公有,好像是不起作用的 * */ private Object readResolve() { return instance; } }
5.下来测试效率,模拟多线程,打印出每种方式占用的时间。或者使用CountDownLatch类,他是一个多线程的辅助类,观察其他线程有没有执行完。它的内部有一个计算器,当一个线程执行完了就会将计数器减一。Wait方法等待其他线程执行完。
最终的结果是饿汉式最快、枚举、静态内部类、双重检测式、懒汉式依次变慢。