目录

一、单例模式

二、饿汉模式和懒汉模式

1、饿汉模式,线程安全

2、懒汉模式

懒汉模式1,线程不安全(不常用)

懒汉模式2,线程安全(不常用)

懒汉模式3,线程安全,双重校验(不常用)

懒汉模式4,线程安全,双重校验,volatile可见性,实现较为复杂

懒汉模式5,线程安全,静态内部类

懒汉模式6,线程安全,静态内部类,防止反射

3、readResolve方法

序列化测试

ObjectOutputStream是怎么校验readResolve()的

概括一下ObjectOutputStream().readObject()的整个大致流程

4、枚举方式,线程安全(不常用)

三、项目地址


一、单例模式

单例对象是一种常用的设计模式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。这样的模式有几个好处:

1、某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。

2、省去了new操作符,降低了系统内存的使用频率,减轻GC压力。

3、有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。

单例模式的特点总结就是

  • 1、单例类只能有一个实例。
  • 2、单例类必须自己创建自己的唯一实例。
  • 3、单例类必须给所有其他对象提供这一实例。

二、饿汉模式和懒汉模式

饿汉式和懒汉式的区别,就是懒汉式比较懒,不先加载实例;饿汉式不管用户是否要使用该类的对象,就先创建好了一个实例放在内存中。

1、饿汉模式,线程安全

对象实例创建

package cn.zygxsq.design.module.singletonPattern.hungryMode;/*** 懒汉模式
* 原文:https://blog.csdn.net/qq_27471405/article/details/127167410* Created by yjl on 2022/8/4.*/
public class Singleton {/* 持有私有静态实例,防止被引用*/private static Singleton instance = new Singleton();/* 私有构造方法,防止被实例化 */private Singleton() {}/* 静态工程方法,返回Singleton实例 */public static Singleton getInstance() {return instance;                                                                   // 原文:https://blog.csdn.net/qq_27471405/article/details/127167410}/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */private Object readResolve() {return instance;}/* 要操作的一些方法*/public void dosomething(){System.out.println("单例模式方法调用");}
}

test调用Singleton实例中的方法

package cn.zygxsq.design.module.singletonPattern.hungryMode;/**
* 原文:https://blog.csdn.net/qq_27471405/article/details/127167410* Created by yjl on 2022/8/4.*/
public class SingletonTest {public static void main(String[] args) {//不合法的构造函数//编译时错误:构造函数 Singleton() 是不可见的//Singleton instance = new Singleton();                              // 原文:https://blog.csdn.net/qq_27471405/article/details/127167410//获取唯一可用的对象Singleton instance = Singleton.getInstance();//调用方法instance.dosomething();}
}

执行结果

上述方法就是实现单例模式的其中一种(饿汉模式),这种方式比较常用,但是在类中不管用户是否要使用该类的对象,就先创建好了一个实例放在内存中,这就比较浪费内存。

优点:没有加锁,执行效率会提高。

缺点:类加载时就初始化,浪费内存。

它基于 classloader 机制避免了多线程的同步问题

我们试着不先创建对象的实例,到用的时候才去创建,这就需要用到懒汉模式

2、懒汉模式

懒汉模式1,线程不安全(不常用)

对象实例创建

package cn.zygxsq.design.module.singletonPattern.lazyMode.lazy1;/*** 懒汉模式1,线程不安全
* 原文:https://blog.csdn.net/qq_27471405/article/details/127167410* Created by yjl on 2022/8/4.*/
public class SingletonLazy1 {/* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */private static SingletonLazy1 instance = null;/* 私有构造方法,防止被实例化 */private SingletonLazy1() {}/* 静态工程方法,创建实例 */public static SingletonLazy1 getInstance() {if (instance == null) {instance = new SingletonLazy1();}return instance;}/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */private Object readResolve() {return instance;}/* 要操作的一些方法*/public void dosomething(){System.out.println("单例模式方法调用");}
}

test调用对象实例中的方法

package cn.zygxsq.design.module.singletonPattern.lazyMode.lazy1;import cn.zygxsq.design.module.singletonPattern.hungryMode.Singleton;/*** 原文:https://blog.csdn.net/qq_27471405/article/details/127167410* Created by yjl on 2022/8/4.*/
public class SingletonTestLazy1 {public static void main(String[] args) {//不合法的构造函数//编译时错误:构造函数 SingletonLazy1() 是不可见的//SingletonLazy1 instance = new SingletonLazy1();//获取唯一可用的对象SingletonLazy1 instance = SingletonLazy1.getInstance();                                            // 原文:https://blog.csdn.net/qq_27471405/article/details/127167410//调用方法instance.dosomething();}
}

执行结果

上述懒汉模式可以满足基本要求,但是,像这样毫无线程安全保护的类,如果我们把它放入多线程的环境下,肯定就会出现问题了,如何解决?我们首先会想到对getInstance方法加synchronized关键字

懒汉模式2,线程安全(不常用)

package cn.zygxsq.design.module.singletonPattern.lazyMode.lazy2;/*** 懒汉模式1,线程安全
* 原文:https://blog.csdn.net/qq_27471405/article/details/127167410* Created by yjl on 2022/8/4.*/
public class SingletonLazy2 {/* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */private static SingletonLazy2 instance = null;/* 私有构造方法,防止被实例化 */private SingletonLazy2() {}/*  synchronized加锁,保证单例 */public static synchronized SingletonLazy2 getInstance() {if (instance == null) {instance = new SingletonLazy2();                                                      // 原文:https://blog.csdn.net/qq_27471405/article/details/127167410}return instance;}/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */private Object readResolve() {return instance;}/* 要操作的一些方法*/public void dosomething(){System.out.println("单例模式方法调用");}
}

上述实现方式可以保证线程安全了,但是,synchronized作为修饰符在方法上使用,在性能上会有所下降,因为每次调用getInstance(),都要对对象上锁,事实上,只有在第一次创建对象的时候需要加锁,之后就不需要了,所以,需要改进

懒汉模式3,线程安全,双重校验(不常用)

package cn.zygxsq.design.module.singletonPattern.lazyMode.lazy3;/*** 懒汉模式1,线程安全
* 原文:https://blog.csdn.net/qq_27471405/article/details/127167410* Created by yjl on 2022/8/4.*/
public class SingletonLazy3 {/* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */private static SingletonLazy3 instance = null;/* 私有构造方法,防止被实例化 */private SingletonLazy3() {}/*  synchronized加锁,保证单例 */public static SingletonLazy3 getInstance() {if (instance == null) {synchronized (SingletonLazy3.class) {if (instance == null) {instance = new SingletonLazy3();}}}return instance;}/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */private Object readResolve() {return instance;}/* 要操作的一些方法*/public void dosomething(){System.out.println("单例模式方法调用");}
}

上述实现方式似乎解决了之前提到的问题,将synchronized关键字加在了方法内部,也就是说当调用的时候是不需要加锁的,只有在instance为null,并创建对象的时候才需要加锁,性能有一定的提升。但是,这样的情况,还是有可能有问题的。会出现指令重排序的情况

看下面的情况:在Java指令中创建对象和赋值操作是分开进行的,也就是说

instance = new SingletonLazy3(); 这一段代码

语句并非是一个原子操作,在 JVM 中这句代码大概做了下面 3 件事情:

1、给 new的对象 分配内存

2、调用 Singleton 的构造函数来初始化成员变量

3、将引用instance指向分配的内存空间(执行完这步 instance 就为非 null 了)

但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,另外一个线程B抢夺到了CPU的执行权,这时instance已经是非null了(但却没有初始化),所以线程B会直接返回 instance,然后使用,结果就会出现问题了(因为对象还没有初始化)。

所以对于第三种进行优化的方式,就是对instance加一个volatile可见性 ,防止指令重排序

private volatile static SingletonLazy4 instance = null;

懒汉模式4,线程安全,双重校验,volatile可见性,实现较为复杂

package cn.zygxsq.design.module.singletonPattern.lazyMode.lazy4;/*** 懒汉模式4,线程安全,双重校验,volatile可见性,实现较为复杂
* 原文:https://blog.csdn.net/qq_27471405/article/details/127167410* Created by yjl on 2022/8/4.*/
public class SingletonLazy4 {/* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */private volatile static SingletonLazy4 instance = null;/* 私有构造方法,防止被实例化 */private SingletonLazy4() {}/*  synchronized加锁,保证单例 */public static SingletonLazy4 getInstance() {if (instance == null) {synchronized (SingletonLazy4.class) {if (instance == null) {instance = new SingletonLazy4();                                               // 原文:https://blog.csdn.net/qq_27471405/article/details/127167410}}}return instance;}/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */private Object readResolve() {return instance;}/* 要操作的一些方法*/public void dosomething(){System.out.println("单例模式方法调用");}
}

懒汉模式4基本可以用了,但是实现起来比较繁琐,还可以有另外一种简单的方式,使用内部类来实现。(原创文章原文链接)

懒汉模式5,线程安全,静态内部类

使用内部类来维护单例的实现,JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的(就是加载完毕后别的线程才能使用)。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕,这样我们就不用担心上面的问题。同时该方法也只会在第一次调用的时候使用互斥机制,这样就解决了低性能问题。

package cn.zygxsq.design.module.singletonPattern.lazyMode.lazy5;/*** 懒汉模式5,线程安全,静态内部类
* 原文:https://blog.csdn.net/qq_27471405/article/details/127167410* Created by yjl on 2022/8/4.*/
public class SingletonLazy5 {/* 私有构造方法,防止被实例化 */private SingletonLazy5() {}/* 此处使用一个内部类来维护单例 */private static class SingletonFactory {private static SingletonLazy5 instance = new SingletonLazy5();}/* 获取实例 */public static SingletonLazy5 getInstance() {return SingletonFactory.instance;                                             // 原文:https://blog.csdn.net/qq_27471405/article/details/127167410}/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */private Object readResolve() {return getInstance();}/* 要操作的一些方法*/public void dosomething(){System.out.println("单例模式方法调用");}
}

这种懒汉模式静态内部类方式和饿汉模式很像,可以和饿汉模式对比着看一下,饿汉模式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到懒加载效果),而这种懒汉方式5静态内部类方式是 SingletonLazy5 类被装载了,instance 不一定被初始化。因为 SingletonFactory 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonFactory 类,从而实例化 instance。想象一下,如果实例化 instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。这个时候,这种方式相比饿汉模式方式就显得很合理。

懒汉模式5静态内部类方式已经很牛了,但是如果在构造函数中抛出异常,实例将永远得不到创建,也会出错。

还有,懒汉模式5静态内部类已经很牛了,但是如果遇到反射调用,我们可以使用反射去创建这个类的对象,即使它的构造器是私有的,我们也是可以调用到的,那也可以创建多个实例。那么这个时候我们就需要再次修改代码去访问别人反射调用构造器。

所以说,十分完美的东西是没有的,我们只能根据实际情况,选择最适合自己应用场景的实现方法。

反射调用demo

package cn.zygxsq.design.module.singletonPattern.lazyMode.lazy5;import java.lang.reflect.Constructor;
import java.lang.reflect.Method;/*** 反射获取私有构造函数,创建多个实例
* 原文:https://blog.csdn.net/qq_27471405/article/details/127167410* Created by yjl on 2022/8/4.*/
public class SingletonTestLazy5Reflection {public static void main(String[] args) throws Exception{// 1:通过Class的静态方法forName加载Class aClass = Class.forName("cn.zygxsq.design.module.singletonPattern.lazyMode.lazy5.SingletonLazy5");//获取私有构造方法//Constructor con = c.getConstructor(String.class);//NoSuchMethodException没有这个方法异常//原因是一开始我们使用的方法只能获取公共的,下面这种方式就可以Constructor con = aClass.getDeclaredConstructor();//用该私有方法创建对象//IllegalAccessException:非法访问异常//暴力访问con.setAccessible(true);//值为true则指示反射的对象在使用是应该取消Java语言访问检查// 实例化对象的方法Object o1 = con.newInstance();System.out.println(o1);//Method m = o1.getClass().getDeclaredMethod("dosomething", null);//访问方法Method m = aClass.getDeclaredMethod("dosomething", null);//调用方法m.invoke(o1, null);// 第二次创建对象Class aClass2 = Class.forName("cn.zygxsq.design.module.singletonPattern.lazyMode.lazy5.SingletonLazy5");Constructor con2 = aClass2.getDeclaredConstructor();//用该私有方法创建对象//IllegalAccessException:非法访问异常//暴力访问con2.setAccessible(true);//值为true则指示反射的对象在使用是应该取消Java语言访问检查// 实例化对象的方法Object o2 = con2.newInstance();System.out.println(o2);}
}

执行结果

 可以看到多个对象是不同的

懒汉模式6,线程安全,静态内部类,防止反射

我们为了避免别人反射调用,我们修改一下构造器为下面这样的

private SingletonLazy6() {if(!flag){flag = true;}else{throw new RuntimeException("不能多次创建单例对象");}
}

demo

package cn.zygxsq.design.module.singletonPattern.lazyMode.lazy6;/*** 懒汉模式6,线程安全,静态内部类,防止反射多次
* 原文:https://blog.csdn.net/qq_27471405/article/details/127167410* Created by yjl on 2022/8/4.*/
public class SingletonLazy6 {private static boolean flag;/* 私有构造方法,防止被实例化 */private SingletonLazy6() {if(!flag){flag = true;}else{throw new RuntimeException("不能多次创建单例对象");}}/* 此处使用一个内部类来维护单例 */private static class SingletonFactory {private static SingletonLazy6 instance = new SingletonLazy6();}/* 获取实例 */public static SingletonLazy6 getInstance() {return SingletonFactory.instance;                                                                   // 原文:https://blog.csdn.net/qq_27471405/article/details/127167410}/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */private Object readResolve() {return getInstance();}/* 要操作的一些方法*/public void dosomething(){System.out.println("单例模式方法调用");}
}

test反射多次调用测试

package cn.zygxsq.design.module.singletonPattern.lazyMode.lazy6;import java.lang.reflect.Constructor;
import java.lang.reflect.Method;/*** 反射获取私有构造函数,创建多个实例
* 原文:https://blog.csdn.net/qq_27471405/article/details/127167410* Created by yjl on 2022/8/4.*/
public class SingletonTestLazy6Reflection {public static void main(String[] args) throws Exception{// 1:通过Class的静态方法forName加载Class aClass = Class.forName("cn.zygxsq.design.module.singletonPattern.lazyMode.lazy6.SingletonLazy6");//获取私有构造方法//Constructor con = c.getConstructor(String.class);                                                             // 原文:https://blog.csdn.net/qq_27471405/article/details/127167410//NoSuchMethodException没有这个方法异常//原因是一开始我们使用的方法只能获取公共的,下面这种方式就可以Constructor con = aClass.getDeclaredConstructor();//用该私有方法创建对象//IllegalAccessException:非法访问异常//暴力访问con.setAccessible(true);//值为true则指示反射的对象在使用是应该取消Java语言访问检查// 实例化对象的方法Object o1 = con.newInstance();System.out.println(o1);//Method m = o1.getClass().getDeclaredMethod("dosomething", null);//访问方法Method m = aClass.getDeclaredMethod("dosomething", null);//调用方法m.invoke(o1, null);// 第二次创建对象Class aClass2 = Class.forName("cn.zygxsq.design.module.singletonPattern.lazyMode.lazy6.SingletonLazy6");Constructor con2 = aClass2.getDeclaredConstructor();//用该私有方法创建对象//IllegalAccessException:非法访问异常//暴力访问con2.setAccessible(true);//值为true则指示反射的对象在使用是应该取消Java语言访问检查// 实例化对象的方法Object o2 = con2.newInstance();System.out.println(o2);}
}

执行结果

3、readResolve方法

懒汉模式6静态内部类防止反射,反射的问题处理完了之后,这里还有一个问题,就是如果把单例对象进行序列化然后再反序列化,那么内存中就会出现俩个一样的单例对象,只是内存地址不同。这种情况我们可以使用readResolve方法来防止。

private Object readResolve(){.....}

ObjectInputStream 会检查对象的class是否定义了readResolve方法。如果定义了,将由readResolve方法指定返回的对象。返回对象的类型一定要是兼容的,否则会抛出ClassCastException 。

序列化测试

实现一下Serializable

package cn.zygxsq.design.module.singletonPattern.lazyMode.lazy6.testReadResolve;import java.io.Serializable;/*** 懒汉模式6,线程安全,静态内部类,防止反射多次
* 原文:https://blog.csdn.net/qq_27471405/article/details/127167410* Created by yjl on 2022/8/4.*/
public class SingletonLazy6Serializable implements Serializable {private static boolean flag;/* 私有构造方法,防止被实例化 */private SingletonLazy6Serializable() {if(!flag){flag = true;}else{throw new RuntimeException("不能多次创建单例对象");}}/* 此处使用一个内部类来维护单例 */private static class SingletonFactory {private static SingletonLazy6Serializable instance = new SingletonLazy6Serializable();}/* 获取实例 */public static SingletonLazy6Serializable getInstance() {return SingletonFactory.instance;                                                              // 原文:https://blog.csdn.net/qq_27471405/article/details/127167410}/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */private Object readResolve() {return getInstance();}/* 要操作的一些方法*/public void dosomething(){System.out.println("单例模式方法调用");}
}

test调用SingletonLazy6Serializable

package cn.zygxsq.design.module.singletonPattern.lazyMode.lazy6.testReadResolve;import cn.zygxsq.design.module.singletonPattern.lazyMode.lazy6.SingletonLazy6;import java.io.*;/*** 反射获取私有构造函数,创建多个实例
* 原文:https://blog.csdn.net/qq_27471405/article/details/127167410* Created by yjl on 2022/8/4.*/
public class SingletonTestLazy6ReadResolve {public static void main(String[] args) throws Exception{// 将SingletonLazy6Serializable类的readResolve()方法注释执行一下和不注释执行一下// 查看打印结果SingletonTestLazy6ReadResolve singletonTestLazy6ReadResolve = new SingletonTestLazy6ReadResolve();singletonTestLazy6ReadResolve.copy();}//测试方式,把单例对象序列化后再反序列化从而获得一个新的对象 就相当于复制了一个单例对象public SingletonLazy6Serializable copy() throws Exception{System.out.println(SingletonLazy6Serializable.getInstance());ByteArrayOutputStream os = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(os);oos.writeObject(SingletonLazy6Serializable.getInstance());                                                                     // 原文:https://blog.csdn.net/qq_27471405/article/details/127167410InputStream is = new ByteArrayInputStream(os.toByteArray());ObjectInputStream ois = new ObjectInputStream(is);SingletonLazy6Serializable obj = (SingletonLazy6Serializable) ois.readObject();System.out.println(obj);return obj;}
}

将SingletonLazy6Serializable类的readResolve()方法注释执行一下和不注释执行一下,分别查看一下打印结果

注释readResolve()方法执行结果,返回的是内存地址不同

不注释readResolve()方法执行结果,返回的内存地址相同

这就是为什么我将单例模式都要加一个readResolve()方法了,这个你们在其他博客基本上是很难见到的,我这里(小小鱼儿小小林)讲得稍微更详细更清楚一点,其他人估计只能讲到上述的饿汉模式和懒汉模式5静态内部类,基本上已经很全很厉害了,其实懒汉模式5静态内部类基本上也够用了。能解决系统的97%了

我这里额外加了一个懒汉模式6静态内部类防止反射,并且讲述了必须加一个readResolve()方法,不然会有序列化问题。

ObjectOutputStream是怎么校验readResolve()的

可以看一下readObject()方法

再看一下

Object obj = readObject0(false);

看一下readOrdinaryObject()方法,很重要

case TC_OBJECT:
                    return checkResolve(readOrdinaryObject(unshared));

private Object readOrdinaryObject(boolean unshared)throws IOException{if (bin.readByte() != TC_OBJECT) {throw new InternalError();}ObjectStreamClass desc = readClassDesc(false);desc.checkDeserialize();Class<?> cl = desc.forClass();if (cl == String.class || cl == Class.class|| cl == ObjectStreamClass.class) {throw new InvalidClassException("invalid class descriptor");}Object obj;try {obj = desc.isInstantiable() ? desc.newInstance() : null;} catch (Exception ex) {throw (IOException) new InvalidClassException(desc.forClass().getName(),"unable to create instance").initCause(ex);}passHandle = handles.assign(unshared ? unsharedMarker : obj);ClassNotFoundException resolveEx = desc.getResolveException();if (resolveEx != null) {handles.markException(passHandle, resolveEx);}if (desc.isExternalizable()) {readExternalData((Externalizable) obj, desc);} else {readSerialData(obj, desc);}handles.finish(passHandle);if (obj != null &&handles.lookupException(passHandle) == null &&desc.hasReadResolveMethod()){Object rep = desc.invokeReadResolve(obj);if (unshared && rep.getClass().isArray()) {rep = cloneArray(rep);}if (rep != obj) {// Filter the replacement objectif (rep != null) {if (rep.getClass().isArray()) {filterCheck(rep.getClass(), Array.getLength(rep));} else {filterCheck(rep.getClass(), -1);}}handles.setObject(passHandle, obj = rep);}}return obj;}

看一下下面的截图

readOrdinaryObject(boolean unshared)方法中获取单例类的ObjectStreamClass对象desc,判断对象是否能实例化。可以则进行实例化,至此单例类进行了第一次实例化,对象名为obj

第一次实例化完成后,通过反射寻找该单例类中的readResolve()方法,没有则直接返回obj对象。这就是我们对没有readResolve()方法的类进行序列化后生成不同对象的原因。

因为我们有定义readResolve()方法,desc通过invokeReadResolve(Object obj)方法调用readResolve()方法获取单例对象instance,将他赋值给rep

概括一下ObjectOutputStream().readObject()的整个大致流程

1.我们在单例类中定义一个readResolve()方法,用于返回instance对象。
2.反序列化获取单例类对象时调用readObject()方法。
3.readObject()方法中调用readObject0()方法。
4.readObject0()方法中调用readOrdinaryObject(boolean unshared)方法。
5.readOrdinaryObject(boolean unshared)方法中获取单例类的ObjectStreamClass对象desc,判断对象是否能实例化。可以则进行实例化,至此单例类进行了第一次实例化,对象名为obj。
6.第一次实例化完成后,通过反射寻找该单例类中的readResolve()方法,没有则直接返回obj对象。这就是我们对没有readResolve()方法的类进行序列化后生成不同对象的原因。
7.因为我们有定义readResolve()方法,desc通过invokeReadResolve(Object obj)方法调用readResolve()方法获取单例对象instance,将他赋值给rep。
8.rep与obj进行比较,由于obj是反射获取的对象,当然与rep不等,于是将rep的值instance赋值给obj,将obj返回,返回对象instance也就保证了单例。
9.简而言之就是当我们通过反序列化readObject()方法获取对象时会去寻找readResolve()方法,如果该方法不存在则直接返回新对象,如果该方法存在则按该方法的内容返回对象。

懒汉模式6静态内部类防止反射写起来还是有点复杂的,其实还有一种更简单的方式,那就是用枚举的方式

4、枚举方式,线程安全(不常用)

package cn.zygxsq.design.module.singletonPattern.enumMode;/*** 单例模式,枚举方式
* 原文:https://blog.csdn.net/qq_27471405/article/details/127167410* Created by yjl on 2022/8/5.*/
public enum  SingletonEnum {INSTANCE;/* 要操作的一些方法*/public void dosomething(){System.out.println("单例模式方法调用");                                                                        // 原文:https://blog.csdn.net/qq_27471405/article/details/127167410}
}

调用的话就跟枚举方式调用的方式一样

package cn.zygxsq.design.module.singletonPattern.enumMode;/*** Created by yjl on 2022/8/5.*/
public class SingletonEnumTest {public static void main(String[] args) {SingletonEnum instance = SingletonEnum.INSTANCE;instance.dosomething();}
}

这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。

这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。

不能通过 reflection attack 来调用私有构造方法。

三、项目地址

以上代码在下面地址中有全部代码

https://github.com/jalenFish/design-patterns/


参考文章:

单例模式 | 菜鸟教程

单例设计模式之readResolve()方法_♀桂圆的博客-CSDN博客

感谢原作者的分享,让技术人能够更快的解决问题

java设计模式之单例模式|单例模式之饿汉模式、懒汉模式、枚举方式|最详细的6种懒汉模式详解相关推荐

  1. 【设计模式:单例模式】单例模式01:饿汉模式

    单例模式:饿汉模式 正文开始@Assassin 目录: 单例模式:饿汉模式 1. 什么是设计模式? 2. 单例设计模式: 2.1 什么是单例模式? 2.2 单例模式应用实例: 1. 什么是设计模式? ...

  2. Java单例模式--------懒汉式和饿汉式

    Java单例模式--------懒汉式和饿汉式 单件模式用途: 单件模式属于工厂模式的特例,只是它不需要输入参数并且始终返回同一对象的引用. 单件模式能够保证某一类型对象在系统中的唯一性,即某类在系统 ...

  3. Java设计模式中的单例模式

    有时候在实际项目的开发中,我们会碰到这样一种情况,该类只允许存在一个实例化的对象,不允许存在一个以上的实例化对象,我们将这种情况称为Java设计模式中的单例模式.设计单例模式主要采用了Java的pri ...

  4. 单例模式懒汉式和饿汉式区别

    单例模式懒汉式和饿汉式区别 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 这种模式涉及到一个单 ...

  5. Java设计模式学习记录-单例模式

    前言 已经介绍和学习了两个创建型模式了,今天来学习一下另一个非常常见的创建型模式,单例模式. 单例模式也被称为单件模式(或单体模式),主要作用是控制某个类型的实例数量是一个,而且只有一个. 单例模式 ...

  6. (单例设计模式中)懒汉式与饿汉式在多线程中的不同

    /*目的:分析一下单例设计模式中,懒汉式与饿汉式在多线程中的不同!开发时我们一般选择饿汉式,因为它简单明了,多线程中不会出现安全问题!而饿汉式需要我们自己处理程序中存在的安全隐患,但是饿汉式的程序技术 ...

  7. 转:Java 7 种阻塞队列详解

    转自: Java 7 种阻塞队列详解 - 云+社区 - 腾讯云队列(Queue)是一种经常使用的集合.Queue 实际上是实现了一个先进先出(FIFO:First In First Out)的有序表. ...

  8. 《Java 2实用教程》(第5版)(清华大学出版社)作者:张跃平、耿祥义习题答案详解

    <Java 2实用教程>(第5版)(清华大学出版社)作者:张跃平.耿祥义习题答案详解 **此答案与详解是本人做作业时所写部分答案,如有错误之处请指出 ** 习题2 1.问答题 (3) 逻辑 ...

  9. java调用javascript函数_[Java教程]JavaScript函数的4种调用方法详解

    [Java教程]JavaScript函数的4种调用方法详解 0 2016-08-09 00:00:12 在JavaScript中,函数是一等公民,函数在JavaScript中是一个数据类型,而非像C# ...

最新文章

  1. 食堂就餐刷卡系统源码_智慧食堂重新定义你的食堂管理系统
  2. 柯南君:看大数据时代下的IT架构(5)消息队列之RabbitMQ--案例(Work Queues起航)...
  3. 计算机学硕哪些学校好考,什么学校研究生好考,计算机专业研究生哪个学校好考一点...
  4. 脉讯发布“社群搜索工具” 助企业精准洞察消费者需求
  5. redis 备份导出rdb_redis简单安装学习
  6. php 科研,科研之路2
  7. 设置ArcGIS的外观改回到出厂
  8. NTLDR is missing 的解决方法
  9. 涉密专用服务器审计系统,国产专用服务器主机审计
  10. ASP.NE浏览时 无法显示 XML 页
  11. 使用现有在线翻译服务进行代码翻译的体验
  12. 离线语音控制命令识别,全向麦、会议蓝牙音箱方案
  13. linux下磁盘情况查询命令
  14. 灵思科电子科技—室内定位技术有哪些_七大室内定位技术详解
  15. 自己动手「焊」键盘,使用Python编写,一键放连招不在话下!
  16. mysql 自连接与内连接
  17. 相关向量机(RVM)
  18. 打倒虚伪的动物福利者!
  19. eWebeditor集成aspjpeg
  20. UG编程,简单六步让你学会三坐标编程

热门文章

  1. 基于jsp的银行柜员业务绩效考核系统(含论文
  2. php tuxedo 0 4 1,Tuxedo杂记
  3. ARM 协处理器指令
  4. ASUS|华硕天选4 FX507ZV ZU ZC 工厂系统带ASUS Recovery恢复功能
  5. overloaded java_java中重写(Override)和重载(overloaded)的区别
  6. Springboot过滤器禁止ip频繁访问功能实现
  7. 移除safari浏览器点击出现蓝框
  8. Tomcat删掉错误页面的版本信息
  9. 移动端H5调起手机发短信功能
  10. 达梦密码策略参数PWD_POLICY详解