1、什么是单例模式
    保证一个jvm中一个类只有一个实例
    2、单例模式的优缺点
    优点:单例类只有一个实例;共享资源,全局使用;节省创建时间,提 高性能;
    缺点:可能会存在线程不安全的问题
    3、单例的七中写法
    分别是「饿汉」、「懒汉(非线程安全)」、「懒汉(线程安全)」、「双重校验锁」、「静态内部类」、「枚举」和「容器类管理、静态块初始化

    饿汉式

    1. public class SingletonV1 {
    2. /**
    3. * 饿汉式 优点:先天性线程安全 为什么先天性 当类加载的时候 就被创建该对象 。
    4. * 缺点:如果项目使用过多的饿汉式会发生问题,项目在启动的时候会边的非常慢、存放在方法区占用内存比较大
    5. * 如果用户不使用该对象的时候,也会被提前创建好,
    6. */
    7. private static SingletonV1 singletonV1 = new SingletonV1();
    8. //1.单例模式是否可以让程序猿初始化
    9. private SingletonV1() {
    10. }
    11. /**
    12. * 返回该对象的实例
    13. *
    14. * @return
    15. */
    16. public static SingletonV1 getInstance() {
    17. return singletonV1;
    18. }
    19. }

    非线程安全的懒汉式

    1. public class SingletonV2 {
    2. private static SingletonV2 singletonV2;
    3. //1.单例模式是否可以让程序猿初始化
    4. private SingletonV2() {
    5. }
    6. /**
    7. * 线程安全问题 在多线程情况下 可能会被初始化多次
    8. *
    9. * @return
    10. */
    11. public static SingletonV2 getSingletonV2() {
    12. //当第一次singletonV2 等于null 情况 才会被初始化
    13. try {
    14. Thread.sleep(3000);
    15. } catch (Exception e) {
    16. }
    17. if (singletonV2 == null) {
    18. singletonV2 = new SingletonV2();
    19. }
    20. return singletonV2;
    21. }
    22. }

    线程安全的懒汉式

    1. public class SingletonV2 {
    2. // 懒汉式 当真正需要使用该对象的时候才会被初始化 线程安全问题 但是效率非常低
    3. private static SingletonV2 singletonV2;
    4. //1.单例模式是否可以让程序猿初始化
    5. private SingletonV2() {
    6. }
    7. /**
    8. * 线程安全问题 在多线程情况下 可能会被初始化多次
    9. *
    10. * @return
    11. */
    12. public synchronized static SingletonV2 getSingletonV2() {
    13. //当第一次singletonV2 等于null 情况 才会被初始化
    14. try {
    15. Thread.sleep(3000);
    16. } catch (Exception e) {
    17. }
    18. if (singletonV2 == null) {
    19. singletonV2 = new SingletonV2();
    20. }
    21. return singletonV2;
    22. }
    23. //100*3000s
    24. /**
    25. * 线程安全问题 当多个线程共享同一个数据 做写的操作的时候可能会出现线程安全问题 读不存在线程安全问题的
    26. * 分析下 懒汉时解决线程安全问题为什么效率比较低呢? 确实因为同步 读和写都加上了锁
    27. * 因为第一次创建对象的时候才会加锁,之后获取该对象的时候不需要加锁。
    28. */
    29. }

    双重校验锁

    1. public class SingletonV3 {
    2. /**
    3. * volatile 防止重排序 java内存模型 增加可见性
    4. */
    5. private volatile static SingletonV3 singletonV3;
    6. // 双重检验锁 解决懒汉式 读和写都加上锁的问题 缺点 第一次创建对象可能会比较慢
    7. // 如何解决 写和读 都不加锁 还能够保证唯一性 线程安全问题
    8. private SingletonV3() throws Exception {
    9. if (singletonV3 != null) {
    10. throw new Exception("对象已经被初始化..");
    11. }
    12. System.out.println("SingletonV3被初始化...");
    13. }
    14. // /**
    15. // * 读的不加锁的,写的时候才会加锁。。
    16. // */
    17. public static SingletonV3 getSingletonV3() throws Exception {
    18. // 当多个线程 同时在可能new 对象的时候 才会加锁,保证线程问题。
    19. if (singletonV3 == null) {
    20. try {
    21. Thread.sleep(3000);
    22. } catch (Exception e) {
    23. }
    24. synchronized (SingletonV3.class) {
    25. if (singletonV3 == null) { // 当前线程已经获取到锁的呢,在判断一下该对象是否已经初始化过,没有初始化过的 创建
    26. singletonV3 = new SingletonV3();
    27. }
    28. }
    29. }
    30. return singletonV3;
    31. }
    32. // 双重检验锁目的什么? 解决懒汉式获取对象效率问题。
    33. /**
    34. * 如果 if singletonV3 == null 比较巧 正好有10个线程进入到呢 25行代码获取锁
    35. * 因为synchronized 保证线程问题,只需要有一个线程获取锁,
    36. */
    37. }

    静态内部类

    1. public class SingletonV5 {
    2. private SingletonV5() {
    3. System.out.println("构造函数被初始化...");
    4. }
    5. public static SingletonV5 getInstance() {
    6. return SingletonV5Utils.singletonV5;
    7. }
    8. // 在类里面嵌套的
    9. private static class SingletonV5Utils {
    10. private static final SingletonV5 singletonV5 = new SingletonV5();
    11. }
    12. /**
    13. * 内部类在调用的时候才会初始化singletonV5
    14. * static 静态 保证唯一
    15. * @param args
    16. */
    17. // 静态内部类特征:继承懒汉式和饿汉式优点、同时解决双重检验锁第一次加载慢的问题 读和写都不需要同步效率非常高...
    18. public static void main(String[] args) {
    19. System.out.println("项目启动成功...");
    20. SingletonV5 instance1 = SingletonV5.getInstance();
    21. SingletonV5 instance2 = SingletonV5.getInstance();
    22. System.out.println(instance1 == instance2);
    23. }
    24. }

    工厂方法:

    1. //单例类
    2. public class Singleton {
    3. //不允许通过new产生对象
    4. private Singleton() {
    5. }
    6. public void doSomething() {
    7. //业务逻辑
    8. }
    9. }
    10. //负责生成单例的工厂类
    11. public class SingletonFactory {
    12. //内存中只有一个singleton对象
    13. private static Singleton singleton;
    14. //静态代码块,只被加载一次
    15. static {
    16. try {
    17. Class c = Class.forName(Singleton.class.getName());
    18. //获得无参构造器
    19. Constructor constructor = c.getDeclaredConstructor();
    20. //设置无参构造器是可访问的
    21. constructor.setAccessible(true);
    22. //产生一个实例对象
    23. singleton = (Singleton)constructor.newInstance();
    24. } catch (Exception e) {
    25. System.out.println("有异常。。。");
    26. }
    27. }
    28. //返回singleton对象
    29. public static Singleton getSingleton() {
    30. return singleton;
    31. }
    32. }
    33. //场景类中运行测试
    34. public class Client2 {
    35. public static void main(String[] args) {
    36. Singleton singleton1 = SingletonFactory.getSingleton();
    37. Singleton singleton2 = SingletonFactory.getSingleton();
    38. System.out.println(singleton1 == singleton2);
    39. }
    40. }
    41. //结果
    42. true //在内存中只有一个实例

    枚举

    1. public enum EnumSingleton {
    2. INSTANCE, MAYIKT;
    3. // 枚举能够绝对有效的防止实例化多次,和防止反射和序列化破解
    4. public void add() {
    5. System.out.println("add方法...");
    6. }
    7. // 枚举是如何初始化的? 反序列化底层是如何解决防止单例被破解。
    8. }
    9. public class EnumSingletonTest {
    10. public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    11. EnumSingleton instance1 = EnumSingleton.INSTANCE;
    12. EnumSingleton instance2 = EnumSingleton.INSTANCE;
    13. System.out.println(instance1 == instance2);
    14. instance1.add();
    15. // EnumSingleton[] values = instance1.values();
    16. // for (int i = 0; i < values.length; i++) {
    17. // System.out.println(values[i]);
    18. // }
    19. Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class, int.class);
    20. declaredConstructor.setAccessible(true);
    21. EnumSingleton v3 = declaredConstructor.newInstance();
    22. System.out.println(v3 == instance1);
    23. EnumSingleton.INSTANCE.add();
    24. }
    25. }

    使用容器管理

    1. public class SingletonManager {
    2. private static Map<String, Object> objMap = new HashMap<String, Object>();
    3. public static void registerService(String key, Object instance) {
    4. if (!objMap.containsKey(key)) {
    5. objMap.put(key, instance);
    6. }
    7. }
    8. public static Object getService(String key) {
    9. return objMap.get(key);
    10. }
    11. }

    应用java反射技术以序列化技术破解单例
    如何应用防止反射破解单例

    单例破解以防止,但是此预防是在初始化后预防,如果在初始化前,这样的预防是没有意义的

    1. // 1. 使用懒汉式创建对象
    2. SingletonV3 instance1 = SingletonV3.getInstance();
    3. // 2. 使用Java反射技术初始化对象 执行无参构造函数
    4. Constructor<SingletonV3> declaredConstructor = SingletonV3.class.getDeclaredConstructor();
    5. declaredConstructor.setAccessible(true);
    6. SingletonV3 instance2 = declaredConstructor.newInstance();
    7. System.out.println(instance1 == instance2);
    8. //预防
    9. private SingletonV3() throws Exception {
    10. synchronized (SingletonV3.class) {
    11. if (singletonV3 != null) {
    12. throw new Exception("该对象已经初始化..");
    13. }
    14. System.out.println("执行SingletonV3无参构造函数...");
    15. }
    16. }

    使用序列化来破解单例

    1. //序列化
    2. public class SingletonV6 implements Serializable {
    3. public static void main(String[] args) throws IOException, ClassNotFoundException {
    4. // java的序列化技术 目的:
    5. /**
    6. * 对象从内存写入到硬盘中 序列化
    7. * 从硬盘中读取到内存 反序列化
    8. */
    9. SingletonV6 instance1 = SingletonV6.getInstance();
    10. FileOutputStream fos = new FileOutputStream("c:\\Singleton.obj");
    11. ObjectOutputStream oos = new ObjectOutputStream(fos);
    12. oos.writeObject(instance1);
    13. oos.flush();
    14. oos.close();
    15. // 反序列化
    16. FileInputStream fis = new FileInputStream("E:\\code\\Singleton.obj");
    17. ObjectInputStream ois = new ObjectInputStream(fis);
    18. SingletonV6 instance2 = (SingletonV6) ois.readObject();
    19. System.out.println(instance1 == instance2);
    20. 应用反射也可以,下边可以的枚举就可以验证反射技术如何破解单例的
    21. }
    22. private static SingletonV6 singletonV6 = new SingletonV6();
    23. //1.单例模式是否可以让程序猿初始化
    24. private SingletonV6() {
    25. }
    26. /**
    27. * 返回该对象的实例
    28. *
    29. * @return
    30. */
    31. public static SingletonV6 getInstance() {
    32. return singletonV6;
    33. }
    34. //返回序列化获取对象 ,保证为单例
    35. public Object readResolve() {
    36. return singletonV6;
    37. }
    38. }

    反射技术总结
    1、什么是反射技术
    反射技术就是动态的获取类的信息,比如属性、方法,同时还可以修改属性和执行方法
    2、反射技术的应用场景
    jdbc加载驱动;
    SpringIOC容器;
    初始化对象;
    提供扩展功能

    1. public class ReflexUtils {
    2. public static UserEntity reflexUser() throws IllegalAccessException, InstantiationException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException {
    3. Class<?> classInfo = Class.forName("com.mayikt.singleton.v9.UserEntity");
    4. // 1.使用java的反射技术初始化对象 默认执行无参构造函数.. 执行无参构造函数..
    5. // Constructor<?> declaredConstructor = classInfo.getDeclaredConstructor();
    6. Constructor<?> declaredConstructor = classInfo.getDeclaredConstructor(String.class, Integer.class);
    7. declaredConstructor.setAccessible(true);
    8. UserEntity userEntity = (UserEntity) declaredConstructor.newInstance();
    9. return userEntity;
    10. }
    11. public static UserEntity reflexUser(String userName, Integer age) throws IllegalAccessException, InstantiationException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException {
    12. Class<?> classInfo = Class.forName("com.mayikt.singleton.v9.UserEntity");
    13. // 1.使用java的反射技术初始化对象 默认执行无参构造函数.. 执行无参构造函数..
    14. // Constructor<?> declaredConstructor = classInfo.getDeclaredConstructor();
    15. Constructor<?> declaredConstructor = classInfo.getDeclaredConstructor(String.class, Integer.class);
    16. declaredConstructor.setAccessible(true);
    17. UserEntity userEntity = (UserEntity) declaredConstructor.newInstance(userName, age);
    18. return userEntity;
    19. }
    20. public static void main(String[] args) throws IllegalAccessException, ClassNotFoundException, InstantiationException, NoSuchMethodException, InvocationTargetException {
    21. // UserEntity userEntity = ReflexUtils.reflexUser("zhangsan", 278);
    22. // System.out.println(userEntity.getAge() + "," + userEntity.getUserName());
    23. Class<?> classInfo = Class.forName("com.mayikt.singleton.v9.UserEntity");
    24. //java的反射技术可以给对象设置 执行方法
    25. Field[] fields = classInfo.getFields();
    26. }
    27. }

    枚举为何可以防止反射和序列化破解(这里我们只讲解防止反射破解)

    1. public enum EnumSingleton {
    2. INSTANCE, MAYIKT;
    3. // 枚举能够绝对有效的防止实例化多次,和防止反射和序列化破解
    4. public void add() {
    5. System.out.println("add方法...");
    6. }
    7. // 枚举是如何初始化的? 反序列化底层是如何解决防止单例被破解。
    8. EnumSingleton() {
    9. }
    10. }
    11. public class EnumSingletonTest {
    12. public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    13. // EnumSingleton instance1 = EnumSingleton.INSTANCE;
    14. // EnumSingleton instance2 = EnumSingleton.INSTANCE;
    15. // System.out.println(instance1 == instance2);
    16. // // 单例七种的,为什么枚举是最好的呢?防止java的反射和序列化破解
    17. // Class<EnumSingleton> enumSingletonClass = EnumSingleton.class;
    18. // // 没有无参构造函数...
    19. // Constructor<EnumSingleton> declaredConstructor = enumSingletonClass.getDeclaredConstructor();
    20. // EnumSingleton enumSingleton = declaredConstructor.newInstance();
    21. // enumSingleton.add();
    22. // 枚举底层是如何实现的
    23. // EnumSingleton instance = EnumSingleton.INSTANCE;
    24. // EnumSingleton.INSTANCE.add();
    25. // // 存放枚举的对象..
    26. // EnumSingleton[] values = EnumSingleton.values();
    27. // 1.使用java的反射技术执行 枚举的有参构造函数..
    28. Class<EnumSingleton> enumSingletonClass = EnumSingleton.class;
    29. // 2.查找当前类是否有该构造函数..
    30. Constructor<EnumSingleton> declaredConstructor = enumSingletonClass.getDeclaredConstructor(String.class, int.class);
    31. declaredConstructor.setAccessible(true);
    32. // 3.调用反射方法初始化对象
    33. EnumSingleton enumSingleton = declaredConstructor.newInstance("zhangsan", 20);
    34. enumSingleton.add();
    35. }
    36. /**
    37. * EnumSingleton.INSTANCE 是一个对象... 类 如何初始化呢? 定义的枚举最终底层肯定是一个类 枚举在底层肯定转换为类
    38. *
    39. * 编译器会实现优化 底层会转换类
    40. */

    1、如果使用无参构造反射技术进行破解的时候
    图片1.png
    2使用java反编译技术,查看枚举类
    图片2.png
    在该反编译源码中,定义了一个类继承了Enum 该类是中没有无参构造函数,所以反射机制调用无参构造函数是无法初始化的,只有一个有参构造器

    图片3.png
    如果应用有参构造进行破解单例会发生什么呢

    1. Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class, int.class);
    2. declaredConstructor.setAccessible(true);
    3. EnumSingleton v3 = declaredConstructor.newInstance();
    4. System.out.println(v3 == instance1);

    运行报错
    图片4.png

    为什么报这个错误呢?主要原因是 java的反射初始化对象中,只要对象是是枚举是不会初始化的的。
    图片5.png