1、什么是单例模式
保证一个jvm中一个类只有一个实例
2、单例模式的优缺点
优点:单例类只有一个实例;共享资源,全局使用;节省创建时间,提 高性能;
缺点:可能会存在线程不安全的问题
3、单例的七中写法
分别是「饿汉」、「懒汉(非线程安全)」、「懒汉(线程安全)」、「双重校验锁」、「静态内部类」、「枚举」和「容器类管理、静态块初始化
饿汉式
public class SingletonV1 {/*** 饿汉式 优点:先天性线程安全 为什么先天性 当类加载的时候 就被创建该对象 。* 缺点:如果项目使用过多的饿汉式会发生问题,项目在启动的时候会边的非常慢、存放在方法区占用内存比较大* 如果用户不使用该对象的时候,也会被提前创建好,*/private static SingletonV1 singletonV1 = new SingletonV1();//1.单例模式是否可以让程序猿初始化private SingletonV1() {}/*** 返回该对象的实例** @return*/public static SingletonV1 getInstance() {return singletonV1;}}
非线程安全的懒汉式
public class SingletonV2 {private static SingletonV2 singletonV2;//1.单例模式是否可以让程序猿初始化private SingletonV2() {}/*** 线程安全问题 在多线程情况下 可能会被初始化多次** @return*/public static SingletonV2 getSingletonV2() {//当第一次singletonV2 等于null 情况 才会被初始化try {Thread.sleep(3000);} catch (Exception e) {}if (singletonV2 == null) {singletonV2 = new SingletonV2();}return singletonV2;}}
线程安全的懒汉式
public class SingletonV2 {// 懒汉式 当真正需要使用该对象的时候才会被初始化 线程安全问题 但是效率非常低private static SingletonV2 singletonV2;//1.单例模式是否可以让程序猿初始化private SingletonV2() {}/*** 线程安全问题 在多线程情况下 可能会被初始化多次** @return*/public synchronized static SingletonV2 getSingletonV2() {//当第一次singletonV2 等于null 情况 才会被初始化try {Thread.sleep(3000);} catch (Exception e) {}if (singletonV2 == null) {singletonV2 = new SingletonV2();}return singletonV2;}//100*3000s/*** 线程安全问题 当多个线程共享同一个数据 做写的操作的时候可能会出现线程安全问题 读不存在线程安全问题的* 分析下 懒汉时解决线程安全问题为什么效率比较低呢? 确实因为同步 读和写都加上了锁* 因为第一次创建对象的时候才会加锁,之后获取该对象的时候不需要加锁。*/}
双重校验锁
public class SingletonV3 {/*** volatile 防止重排序 java内存模型 增加可见性*/private volatile static SingletonV3 singletonV3;// 双重检验锁 解决懒汉式 读和写都加上锁的问题 缺点 第一次创建对象可能会比较慢// 如何解决 写和读 都不加锁 还能够保证唯一性 线程安全问题private SingletonV3() throws Exception {if (singletonV3 != null) {throw new Exception("对象已经被初始化..");}System.out.println("SingletonV3被初始化...");}// /**// * 读的不加锁的,写的时候才会加锁。。// */public static SingletonV3 getSingletonV3() throws Exception {// 当多个线程 同时在可能new 对象的时候 才会加锁,保证线程问题。if (singletonV3 == null) {try {Thread.sleep(3000);} catch (Exception e) {}synchronized (SingletonV3.class) {if (singletonV3 == null) { // 当前线程已经获取到锁的呢,在判断一下该对象是否已经初始化过,没有初始化过的 创建singletonV3 = new SingletonV3();}}}return singletonV3;}// 双重检验锁目的什么? 解决懒汉式获取对象效率问题。/*** 如果 if singletonV3 == null 比较巧 正好有10个线程进入到呢 25行代码获取锁* 因为synchronized 保证线程问题,只需要有一个线程获取锁,*/}
静态内部类
public class SingletonV5 {private SingletonV5() {System.out.println("构造函数被初始化...");}public static SingletonV5 getInstance() {return SingletonV5Utils.singletonV5;}// 在类里面嵌套的private static class SingletonV5Utils {private static final SingletonV5 singletonV5 = new SingletonV5();}/*** 内部类在调用的时候才会初始化singletonV5* static 静态 保证唯一* @param args*/// 静态内部类特征:继承懒汉式和饿汉式优点、同时解决双重检验锁第一次加载慢的问题 读和写都不需要同步效率非常高...public static void main(String[] args) {System.out.println("项目启动成功...");SingletonV5 instance1 = SingletonV5.getInstance();SingletonV5 instance2 = SingletonV5.getInstance();System.out.println(instance1 == instance2);}}
工厂方法:
//单例类public class Singleton {//不允许通过new产生对象private Singleton() {}public void doSomething() {//业务逻辑}}//负责生成单例的工厂类public class SingletonFactory {//内存中只有一个singleton对象private static Singleton singleton;//静态代码块,只被加载一次static {try {Class c = Class.forName(Singleton.class.getName());//获得无参构造器Constructor constructor = c.getDeclaredConstructor();//设置无参构造器是可访问的constructor.setAccessible(true);//产生一个实例对象singleton = (Singleton)constructor.newInstance();} catch (Exception e) {System.out.println("有异常。。。");}}//返回singleton对象public static Singleton getSingleton() {return singleton;}}//场景类中运行测试public class Client2 {public static void main(String[] args) {Singleton singleton1 = SingletonFactory.getSingleton();Singleton singleton2 = SingletonFactory.getSingleton();System.out.println(singleton1 == singleton2);}}//结果true //在内存中只有一个实例
枚举
public enum EnumSingleton {INSTANCE, MAYIKT;// 枚举能够绝对有效的防止实例化多次,和防止反射和序列化破解public void add() {System.out.println("add方法...");}// 枚举是如何初始化的? 反序列化底层是如何解决防止单例被破解。}public class EnumSingletonTest {public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {EnumSingleton instance1 = EnumSingleton.INSTANCE;EnumSingleton instance2 = EnumSingleton.INSTANCE;System.out.println(instance1 == instance2);instance1.add();// EnumSingleton[] values = instance1.values();// for (int i = 0; i < values.length; i++) {// System.out.println(values[i]);// }Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class, int.class);declaredConstructor.setAccessible(true);EnumSingleton v3 = declaredConstructor.newInstance();System.out.println(v3 == instance1);EnumSingleton.INSTANCE.add();}}
使用容器管理
public class SingletonManager {private static Map<String, Object> objMap = new HashMap<String, Object>();public static void registerService(String key, Object instance) {if (!objMap.containsKey(key)) {objMap.put(key, instance);}}public static Object getService(String key) {return objMap.get(key);}}
应用java反射技术以序列化技术破解单例
如何应用防止反射破解单例
单例破解以防止,但是此预防是在初始化后预防,如果在初始化前,这样的预防是没有意义的
// 1. 使用懒汉式创建对象SingletonV3 instance1 = SingletonV3.getInstance();// 2. 使用Java反射技术初始化对象 执行无参构造函数Constructor<SingletonV3> declaredConstructor = SingletonV3.class.getDeclaredConstructor();declaredConstructor.setAccessible(true);SingletonV3 instance2 = declaredConstructor.newInstance();System.out.println(instance1 == instance2);//预防private SingletonV3() throws Exception {synchronized (SingletonV3.class) {if (singletonV3 != null) {throw new Exception("该对象已经初始化..");}System.out.println("执行SingletonV3无参构造函数...");}}
使用序列化来破解单例
//序列化public class SingletonV6 implements Serializable {public static void main(String[] args) throws IOException, ClassNotFoundException {// java的序列化技术 目的:/*** 对象从内存写入到硬盘中 序列化* 从硬盘中读取到内存 反序列化*/SingletonV6 instance1 = SingletonV6.getInstance();FileOutputStream fos = new FileOutputStream("c:\\Singleton.obj");ObjectOutputStream oos = new ObjectOutputStream(fos);oos.writeObject(instance1);oos.flush();oos.close();// 反序列化FileInputStream fis = new FileInputStream("E:\\code\\Singleton.obj");ObjectInputStream ois = new ObjectInputStream(fis);SingletonV6 instance2 = (SingletonV6) ois.readObject();System.out.println(instance1 == instance2);应用反射也可以,下边可以的枚举就可以验证反射技术如何破解单例的}private static SingletonV6 singletonV6 = new SingletonV6();//1.单例模式是否可以让程序猿初始化private SingletonV6() {}/*** 返回该对象的实例** @return*/public static SingletonV6 getInstance() {return singletonV6;}//返回序列化获取对象 ,保证为单例public Object readResolve() {return singletonV6;}}
反射技术总结
1、什么是反射技术
反射技术就是动态的获取类的信息,比如属性、方法,同时还可以修改属性和执行方法
2、反射技术的应用场景
jdbc加载驱动;
SpringIOC容器;
初始化对象;
提供扩展功能
public class ReflexUtils {public static UserEntity reflexUser() throws IllegalAccessException, InstantiationException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException {Class<?> classInfo = Class.forName("com.mayikt.singleton.v9.UserEntity");// 1.使用java的反射技术初始化对象 默认执行无参构造函数.. 执行无参构造函数..// Constructor<?> declaredConstructor = classInfo.getDeclaredConstructor();Constructor<?> declaredConstructor = classInfo.getDeclaredConstructor(String.class, Integer.class);declaredConstructor.setAccessible(true);UserEntity userEntity = (UserEntity) declaredConstructor.newInstance();return userEntity;}public static UserEntity reflexUser(String userName, Integer age) throws IllegalAccessException, InstantiationException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException {Class<?> classInfo = Class.forName("com.mayikt.singleton.v9.UserEntity");// 1.使用java的反射技术初始化对象 默认执行无参构造函数.. 执行无参构造函数..// Constructor<?> declaredConstructor = classInfo.getDeclaredConstructor();Constructor<?> declaredConstructor = classInfo.getDeclaredConstructor(String.class, Integer.class);declaredConstructor.setAccessible(true);UserEntity userEntity = (UserEntity) declaredConstructor.newInstance(userName, age);return userEntity;}public static void main(String[] args) throws IllegalAccessException, ClassNotFoundException, InstantiationException, NoSuchMethodException, InvocationTargetException {// UserEntity userEntity = ReflexUtils.reflexUser("zhangsan", 278);// System.out.println(userEntity.getAge() + "," + userEntity.getUserName());Class<?> classInfo = Class.forName("com.mayikt.singleton.v9.UserEntity");//java的反射技术可以给对象设置 执行方法Field[] fields = classInfo.getFields();}}
枚举为何可以防止反射和序列化破解(这里我们只讲解防止反射破解)
public enum EnumSingleton {INSTANCE, MAYIKT;// 枚举能够绝对有效的防止实例化多次,和防止反射和序列化破解public void add() {System.out.println("add方法...");}// 枚举是如何初始化的? 反序列化底层是如何解决防止单例被破解。EnumSingleton() {}}public class EnumSingletonTest {public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {// EnumSingleton instance1 = EnumSingleton.INSTANCE;// EnumSingleton instance2 = EnumSingleton.INSTANCE;// System.out.println(instance1 == instance2);// // 单例七种的,为什么枚举是最好的呢?防止java的反射和序列化破解// Class<EnumSingleton> enumSingletonClass = EnumSingleton.class;// // 没有无参构造函数...// Constructor<EnumSingleton> declaredConstructor = enumSingletonClass.getDeclaredConstructor();// EnumSingleton enumSingleton = declaredConstructor.newInstance();// enumSingleton.add();// 枚举底层是如何实现的// EnumSingleton instance = EnumSingleton.INSTANCE;// EnumSingleton.INSTANCE.add();// // 存放枚举的对象..// EnumSingleton[] values = EnumSingleton.values();// 1.使用java的反射技术执行 枚举的有参构造函数..Class<EnumSingleton> enumSingletonClass = EnumSingleton.class;// 2.查找当前类是否有该构造函数..Constructor<EnumSingleton> declaredConstructor = enumSingletonClass.getDeclaredConstructor(String.class, int.class);declaredConstructor.setAccessible(true);// 3.调用反射方法初始化对象EnumSingleton enumSingleton = declaredConstructor.newInstance("zhangsan", 20);enumSingleton.add();}/*** EnumSingleton.INSTANCE 是一个对象... 类 如何初始化呢? 定义的枚举最终底层肯定是一个类 枚举在底层肯定转换为类** 编译器会实现优化 底层会转换类*/
1、如果使用无参构造反射技术进行破解的时候
2使用java反编译技术,查看枚举类
在该反编译源码中,定义了一个类继承了Enum 该类是中没有无参构造函数,所以反射机制调用无参构造函数是无法初始化的,只有一个有参构造器

如果应用有参构造进行破解单例会发生什么呢
Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class, int.class);declaredConstructor.setAccessible(true);EnumSingleton v3 = declaredConstructor.newInstance();System.out.println(v3 == instance1);
运行报错
为什么报这个错误呢?主要原因是 java的反射初始化对象中,只要对象是是枚举是不会初始化的的。
