1.闲话少说,直接上代码。
import java.io.Serializable;
//饿汉式public class Singleton01 implements Serializable{ //1.私有的属性 private static Singleton01 instance=new Singleton01(); //2.私有的构造器 private Singleton01(){} //3.共有的get()方法 public static Singleton01 getInstance(){//这里不需要加同步块synchronized return instance; }}为什么不需要加同步块?这里插播一条短消息
1. 饿汉式是天然的线程安全的,因此不需要加线程锁。饿汉式之所以是天然的线程安全,是因为:
一个字节码文件被加载到内存会经历以下过程:
1、链接(1、验证2、准备3、解析)
2、初始化
3、使用
4、卸载
初始化是执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译器自动收集类中的所有类变量的赋 值动作和静态语句块(static)中的语句合并产生的。在这个过程中instance已经被实例化了。并且虚拟机JVM会保证一个类 的<clinit>()方法在多线程的环境中被正确加锁和同步。因此他是天然线程安全的。
2.下面代码分析如何使用反射破解懒汉式:
public class Singletons_Test_02 {
public static void main(String[] args) throws Exception {Class<Singleton01> clazz = (Class<Singleton01>) Class.forName("com.shc.singleton.Singleton01");
Constructor<Singleton01> c = clazz.getDeclaredConstructor(null);//获得无参构造器 c.setAccessible(true);//跳过权限检查,访问私有的构造器 Singleton01 s3 = c.newInstance(); Singleton01 s4 = c.newInstance(); System.out.println(s3); System.out.println(s4);}
}
此时输出s3与s4的哈希值是相等的。代表不是同一个对象,饿汉式单例通过反射破解完成。
3.下面分析饿汉式如何避免反射漏洞?
public class Singleton1 {
//1.私有的属性 private static Singleton1 instance=new Singleton1(); //2.私有的构造器 private Singleton1(){ if (null != instance) { throw new RuntimeException(); } } //3.共有的get()方法 public static Singleton1 getInstance(){//天然的线程安全,不需要加同步块,因此调用效率高 return instance; }}在私有的构造器中加一个判断,判断要创建的对象是否存在,如果不存在再创建,已存在就抛出运行时异常。
4.下面利用序列化与反序列化漏洞破解饿汉式单例
public class Singletons_Test_03 {
public static void main(String[] args) throws Exception {Singleton01 s1 = Singleton01.getInstance();
Singleton01 s2 = Singleton01.getInstance(); System.out.println(s1); System.out.println(s2); //序列化 FileOutputStream fos = new FileOutputStream("../a.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(s1); oos.close(); fos.close(); //反序列化 ObjectInputStream ois =new ObjectInputStream(new FileInputStream("../a.txt")); Singleton01 s3 = (Singleton01) ois.readObject(); System.out.println(s3);}
}
5.破解饿汉式反序列化漏洞
public class Singleton11 implements Serializable{
//1.私有的属性 private static Singleton11 instance=new Singleton11(); //2.私有的构造器 private Singleton11(){} //3.共有的get()方法 public static Singleton11 getInstance(){ return instance; } //反序列化时(加这个方法可以防止反序列化漏洞) private Object readResolve() throws ObjectStreamException{ return instance; }}以上demo较为简单,不做深入研究,设计模式体验重在应用。