看的视频:https://www.bilibili.com/video/av43896218/?p=286
实现方式:(构造器私有,提供一个外部可以访问的方法(可以提供实例))
1、饿汉式:线程安全,调用效率高, 不能延时加载
2、懒汉式:线程安全,调用效率不高,可以延时加载(要用的时候才加载 )
3、双重锁检测式:“由于JVM底层内部模型原因,偶尔会出现问题,不建议使用”
4、静态内部类式:线程安全,调用效率高,且可以实现延时加载
(上面的四种方式都可以通过反射和反序列化 破坏 单例模式)
5、枚举:线程安全,调用效率高,不能延时加载,可以天然的防止反射和反序列化
如果单例对象占用资源少,不需要延时加载:
枚举式 优于 饿汉式
如果单例对象占用资源大, 需要延时加载:
静态内部类式 优于 懒汉式
饿汉式:
/* 饿汉式: 最开始就已经把实例给创建好了 */ public class Singleton01 { public static Singleton01 instance = new Singleton01(); //一开始就已经创建好了 private Singleton01(){ } public static Singleton01 getInstance(){ return instance; } }
懒汉式:
/* 懒汉式: 用的时候才会初始化实例 效率不高就是因为有synchronized这个关键字 */ public class Singleton02 { private static Singleton02 instance = null; private Singleton02(){ } public static synchronized Singleton02 getInstance(){ if(instance == null){ instance = new Singleton02(); } return instance; } }
静态内部类:
/* 静态内部类: 只有在主动调用这个类的时候才会加载 外部类加载的时候 静态内部类不会加载 */ public class Singleton03 { static class Inner{ private static Singleton03 instance = new Singleton03(); } private Singleton03(){ } public static Singleton03 getInstance(){ return Inner.instance; } }
枚举:
/* 枚举 天然的可以防止 反射和反序列化 */ public enum Singleton04 { INSTANCE; //就表示一个实例 获取的时候直接 Singleton04.INSTANCE public void method(){ //这里可以写一些方法 } }
通过反射破坏单例模式:
public class Test01 { public static void main(String[] args) throws Exception{ Class<?> clazz = Class.forName("com.test.Singleton01"); //获取构造函数: Constructor c = clazz.getDeclaredConstructor(); c.setAccessible(true); //对于私有属性和方法 设置为允许访问 //通过构造函数 构造实例 Singleton01 singleton001 = (Singleton01)c.newInstance(); Singleton01 singleton002 = (Singleton01)c.newInstance(); System.out.println(singleton001 == singleton002); // false } }
如何破坏反射:
public class Singleton01 { private static boolean flag = false; //标志位 public static Singleton01 instance = new Singleton01(); //一开始就已经创建好了 // 上面的已经调用过一次 这个私有的构造函数了
private Singleton01(){ //反射的时候操作的就是这个私有的构造函数 synchronized (Singleton01.class){ if(flag == false){ flag = true; }else{ //表示第二次用构造函数进行实例化 throw new RuntimeException("单例模式正在被破坏"); } } } public Singleton01 getInstance(){ return instance; } }
在用反射就会报错....
通过反序列化破坏单例模式:
对象序列化:将对象状态转换为字节流的过程,可以将其保存到磁盘文件中或者通过网络发送到任何其他程序中。
反序列化:将字节流转化为对象的过程。
public class Test01 { public static void main(String[] args) throws Exception{
//序列化 singleton001 Singleton01要实现 Serializable Singleton01 singleton001 = Singleton01.getInstance(); Singleton01 singleton002 = Singleton01.getInstance(); FileOutputStream fos = new FileOutputStream("d:/a.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(singleton001); oos.close(); fos.close(); //反序列化 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/a.txt")); Singleton01 singleton003 = (Singleton01)ois.readObject(); System.out.println(singleton001 == singleton002); //true System.out.println(singleton001 == singleton003); //false } }
防止反序列化:
public class Singleton01 implements Serializable { public static Singleton01 instance = new Singleton01(); //一开始就已经创建好了 private Singleton01(){ } public static Singleton01 getInstance(){ return instance; } //在反序列化的时候 直接调用这个方法 返回instance 而不是返回反序列化后的新对象 private Object readResolve() throws ObjectStreamException { return instance; } }