目录
一.实现单例模式
单例模式(Singleton): 确保一个类只有一个实例,并提供一个全局访问点.可以保证对象的唯一性,并方便对象的管理和控制.
实现思路:
- 如果要实现一个单例,首先要把类的构造函数私有化,否则就可以随时通过构造函数创建对象
- 构造函数私有化之后,需要提供一个方法,可以初始化对象,并且保证只能初始化一个对象,还需要考虑线程安全问题.
- 实现方法主要有5种:懒汉、饿汉、静态内部类、双重校验锁、枚举.
1.懒汉
懒汉:就是需要对象的时候才去创建,可以避免提前创建浪费资源
//单例模式--懒汉 public class Demo { private static Demo demo; private Demo(){} public static synchronized Demo getDemo(){ if (demo == null) { demo = new Demo(); } return demo; } }
2.饿汉
饿汉:就是在类刚一开始初始化的时候就把对象创建处理
//单例模式--饿汉 public class Demo1 implements Serializable { private static Demo1 demo1 = new Demo1(); private Demo1(){} public static Demo1 getDemo1(){ return demo1; } }
3.静态内部类
//单例模式--静态内部类 public class Demo2 { private static class Demo2Holder{ public static final Demo2 DEMO_2= new Demo2(); } private Demo2(){} public static final Demo2 getDemo2(){ return Demo2Holder.DEMO_2; } }
4.双重校验锁
//单例模式--双重校验锁 public class Demo4 { private volatile static Demo4 demo4; private Demo4(){} public static Demo4 getDemo4(){ if (demo4==null) { synchronized (Demo4.class){ if (demo4 == null) { demo4 = new Demo4(); } } } return demo4; } }
5.枚举
//单例模式--枚举 public enum Demo3 { DEMO_3; public void method(){} }
二.破坏单例模式
单例模式主要是通过把一个类的构造方法私有化,来避免重复创建多个对象的。那么,想要破坏单例,只要想办法能够执行到这个私有的构造方法就行了。—般来说做法有使用反射及使用反序列化都可以破坏单例。
1.反射破坏单例模式
//通过反射破坏单例模式 public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { Demo demo = Demo.getDemo(); //通过反射获取构造函数 Constructor<Demo> declaredConstructor = Demo.class.getDeclaredConstructor(); //将构造函数设为可访问类型 declaredConstructor.setAccessible(true); //调用构造函数创建对象 Demo demo1 = declaredConstructor.newInstance(); //比较对象 System.out.println(demo == demo1);// 输出为flase }
2.反序列化破坏单例模式
序列化底层会通过反射调用无参构造方法新建对象
//通过反序列化破坏单例模式 public static void main(String[] args)throws Exception{ //写入对象 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("duixiang.txt")); oos.writeObject(Demo1.getDemo1()); //读取对象 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("duixiang.txt")); Demo1 demo1 = (Demo1) ois.readObject(); System.out.println(demo1 == Demo1.getDemo1());//输出false oos.close(); ois.close(); }
三.避免破坏单例模式
1.避免反射破坏单例模式
反射是调用默认的无参构造函数创建出来的,只需要我们改造下构造函数,使其在反射调用的时候识别出来对象是不是被创建过就行了:
//单例模式--懒汉 public class Demo { private static Demo demo; private Demo(){ if (demo != null) { throw new RuntimeException("对象已经创建过了"); } } public static synchronized Demo getDemo(){ if (demo == null) { demo = new Demo(); } return demo; } }
2.避免反序列化破坏单例模式
只需要在类中定义readResolve就可以避免单例模式被破坏
//单例模式--饿汉 public class Demo1 implements Serializable { private static Demo1 demo1 = new Demo1(); private Demo1(){} public static Demo1 getDemo1(){ return demo1; } private Object readResolve(){ return demo1; } //通过反序列化破坏单例模式 public static void main(String[] args)throws Exception{ //写入对象 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("duixiang.txt")); oos.writeObject(Demo1.getDemo1()); //读取对象 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("duixiang.txt")); Demo1 demo1 = (Demo1) ois.readObject(); System.out.println(demo1 == Demo1.getDemo1());//输出为true oos.close(); ois.close(); } }