Java中的单例模式实例分析
本篇内容介绍了“Java中的单例模式实例分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
1、定义2、适用场景单例模式(Singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。
隐藏其所有的构造方法。
属于创建型模式。
3、常见写法确保任何情况下都绝对只有一个实例。
第一种:饿汉式单例:在单例类首次加载时就创建实例
/***@Package:com.hzg.study.design.pattern.singleton.hungry*@Description:饿汉式单例*@Author:HuangZhiGao*@CreateDate:2022-02-1816:15*/publicclassHungrySingleton{privatestaticfinalHungrySingletonINSTANCE=newHungrySingleton();/***私有化构造器*/privateHungrySingleton(){}/***全局访问点*/publicstaticHungrySingletongetInstance(){returnINSTANCE;}}
饿汉式单例静态代码块写法:
/***@Package:com.hzg.study.design.pattern.singleton.hungry*@Description:饿汉式单例(静态代码块初始化)*@Author:HuangZhiGao*@CreateDate:2022-02-1816:15*/publicclassHungryStaticSingleton{privatestaticfinalHungryStaticSingletonINSTANCE;/***静态代码块*/static{INSTANCE=newHungryStaticSingleton();}/***私有化构造器*/privateHungryStaticSingleton(){}/***全局访问点*/publicstaticHungryStaticSingletongetInstance(){returnINSTANCE;}}
第二种:懒汉式单例:被外部类调用时才创建实例
/***@Package:com.hzg.study.design.pattern.singleton.lazy*@Description:懒汉式单例*@Author:HuangZhiGao*@CreateDate:2022-02-1816:24*/publicclassLazySingleton{privatestaticLazySingletonINSTANCE=null;/***私有化构造器*/privateLazySingleton(){}/***全局访问点*/publicstaticLazySingletongetInstance(){if(INSTANCE==null){INSTANCE=newLazySingleton();}returnINSTANCE;}}
懒汉式单例静态匿名内部类写法(性能最优):
/***@Package:com.hzg.study.design.pattern.singleton.lazy*@Description:懒汉式单例(匿名静态内部类)(性能最优)*@Author:HuangZhiGao*@CreateDate:2022-02-1818:00*/publicclassLazyInnerClazzSingletonimplementsSerializable{/***私有化构造器*/privateLazyInnerClazzSingleton(){}/***全局访问点*/publicstaticfinalLazyInnerClazzSingletongetInstance(){returnLazyHolder.INSTANCE;}privatestaticclassLazyHolder{privatestaticfinalLazyInnerClazzSingletonINSTANCE=newLazyInnerClazzSingleton();}}
第三种:注册式单例:将每一个实例都缓存到统一的容器中,使用唯一标识获取实例
注册式单例枚举写法:
/***@Package:com.hzg.study.design.pattern.singleton.registry*@Description:注册式单例-枚举单例*@Author:HuangZhiGao*@CreateDate:2022-02-2110:24*/publicenumEnumSingleton{INSTANCE;/***如果需要让其他对象成为单例,只需要将data改为目标类对象即可*<p/>*通过getter和setter操作*/privateObjectdata;publicObjectgetData(){returndata;}publicvoidsetData(Objectdata){this.data=data;}publicstaticEnumSingletongetInstance(){returnINSTANCE;}}
Spring中常见的注册式单例写法:
/***@Package:com.hzg.study.design.pattern.singleton.registry*@Description:Spring中常见的注册式单例写法*@Author:HuangZhiGao*@CreateDate:2022-02-2110:54*/publicclassContainerSingleton{/***springioc*/privatestaticMap<String,Object>container=newConcurrentHashMap<>();privateContainerSingleton(){}publicstaticObjectgetBean(StringclazzName){//加synchronized代码块保证线程安全synchronized(container){if(!container.containsKey(clazzName)){Objectobject=null;try{object=Class.forName(clazzName).newInstance();container.put(clazzName,object);}catch(Exceptione){e.printStackTrace();}returnobject;}returncontainer.get(clazzName);}}}
第四种:ThreadLocal线程单例:保证线程内部的全局唯一,且天生线程安全
/***@Package:com.hzg.study.design.pattern.singleton.threadlocal*@Description:ThreadLocal线程单例(伪安全)*@Description:可以使用ThreadLocal动态切换数据源*@Author:HuangZhiGao*@CreateDate:2022-02-2111:10*/publicclassThreadLocalSingleton{publicstaticfinalThreadLocal<ThreadLocalSingleton>THREAD_LOCAL=newThreadLocal<ThreadLocalSingleton>(){@OverrideprotectedThreadLocalSingletoninitialValue(){returnnewThreadLocalSingleton();}};privateThreadLocalSingleton(){}publicstaticThreadLocalSingletongetInstance(){returnTHREAD_LOCAL.get();}}4、如何防止单例被破坏1.多线程破坏单例以及解决方法
以懒汉式单例LazySingleton为例:
首先写一个线程实现类,如下:
importcom.hzg.study.design.pattern.singleton.lazy.LazySingleton;/***@Package:com.hzg.study.design.pattern.singleton.lazy.test*@Description:*@Author:HuangZhiGao*@CreateDate:2022-02-1816:32*/publicclassExecutorThreadimplementsRunnable{@Overridepublicvoidrun(){LazySingletoninstance=LazySingleton.getInstance();System.out.println(Thread.currentThread().getName()+":"+instance);}}
main方法测试:
publicclassLazySingletonTest{publicstaticvoidmain(String[]args){Threadthread1=newThread(newExecutorThread());thread1.start();Threadthread2=newThread(newExecutorThread());thread2.start();System.out.println("----------------------------------------");}}
测试结果:显然出现了两个不同的实例
解决方法1:加synchronized关键字修饰getInstance方法
publicclassLazySingleton{privatestaticLazySingletonINSTANCE=null;/***私有化构造器*/privateLazySingleton(){}/***全局访问点*<p/>*synchronized关键字修饰方法*/publicstaticsynchronizedLazySingletongetInstance(){if(INSTANCE==null){INSTANCE=newLazySingleton();}returnINSTANCE;}}
解决方法2:双重检查锁DoubleCheck
/***@Package:com.hzg.study.design.pattern.singleton.lazy*@Description:懒汉式单例(双重检查锁)*@Author:HuangZhiGao*@CreateDate:2022-02-1817:08*/publicclassLazyDoubleCheckSingleton{/***volatile关键字修饰,避免指令重排序引发问题*/privatevolatilestaticLazyDoubleCheckSingletonINSTANCE=null;/***私有化构造器*/privateLazyDoubleCheckSingleton(){}/***全局访问点*<p/>*双重检查锁*/publicstaticLazyDoubleCheckSingletongetInstance(){if(INSTANCE==null){synchronized(LazyDoubleCheckSingleton.class){if(INSTANCE==null){INSTANCE=newLazyDoubleCheckSingleton();}}}returnINSTANCE;}}2.反射破坏单例以及解决方法
以懒汉式单例静态匿名内部类写法LazyInnerClazzSingleton为例:
main方法测试:
publicclassLazyInnerClazzSingletonTest{publicstaticvoidmain(String[]args){try{Class<LazyInnerClazzSingleton>aClazz=LazyInnerClazzSingleton.class;Constructor<LazyInnerClazzSingleton>declaredConstructor=aClazz.getDeclaredConstructor(null);declaredConstructor.setAccessible(true);LazyInnerClazzSingletoninstance1=declaredConstructor.newInstance();LazyInnerClazzSingletoninstance2=LazyInnerClazzSingleton.getInstance();System.out.println(instance1);System.out.println(instance2);System.out.println(instance1==instance2);}catch(Exceptione){e.printStackTrace();}}}
测试结果:构建了两个不同的实例
解决方法:在构造器中增加如下if判断
publicclassLazyInnerClazzSingletonimplementsSerializable{/***私有化构造器*/privateLazyInnerClazzSingleton(){if(null!=LazyHolder.INSTANCE){thrownewRuntimeException("不允许构建多个实例");}}/***全局访问点*/publicstaticfinalLazyInnerClazzSingletongetInstance(){returnLazyHolder.INSTANCE;}privatestaticclassLazyHolder{privatestaticfinalLazyInnerClazzSingletonINSTANCE=newLazyInnerClazzSingleton();}}
再次测试:
3.序列化破坏单例以及解决方法以懒汉式单例静态匿名内部类写法LazyInnerClazzSingleton为例:注意必须先实现序列化接口Serializable
main方法测试:
publicstaticvoidmain(String[]args){LazyInnerClazzSingletoninstance1=LazyInnerClazzSingleton.getInstance();LazyInnerClazzSingletoninstance2=null;try(FileOutputStreamfileOutputStream=newFileOutputStream("LazyInnerClazzSingleton.obj");ObjectOutputStreamobjectOutputStream=newObjectOutputStream(fileOutputStream);FileInputStreamfileInputStream=newFileInputStream("LazyInnerClazzSingleton.obj");ObjectInputStreamobjectInputStream=newObjectInputStream(fileInputStream);){//序列化objectOutputStream.writeObject(instance1);objectOutputStream.flush();//反序列化instance2=(LazyInnerClazzSingleton)objectInputStream.readObject();System.out.println(instance1);System.out.println(instance2);System.out.println(instance1==instance2);}catch(Exceptione){e.printStackTrace();}}
测试结果:构建了两个不同的实例
解决方法:新增readResolve方法
publicclassLazyInnerClazzSingletonimplementsSerializable{/***私有化构造器*/privateLazyInnerClazzSingleton(){if(null!=LazyHolder.INSTANCE){thrownewRuntimeException("不允许构建多个实例");}}/***全局访问点*/publicstaticfinalLazyInnerClazzSingletongetInstance(){returnLazyHolder.INSTANCE;}privatestaticclassLazyHolder{privatestaticfinalLazyInnerClazzSingletonINSTANCE=newLazyInnerClazzSingleton();}/***重写readResolve方法,实际还是创建了两次,只不过是覆盖了反序列化出来的对象,之前反序列化出来的对象会被GC回收*发生在JVM层面,相对来说比较安全*/privateObjectreadResolve(){returnLazyHolder.INSTANCE;}}5、优缺点
优点:
在内存中只有一个实例,减少了内存开销。
可以避免对资源的多重占用。
设置全局访问点,严格控制访问。
缺点:
没有接口,扩展困难。
如果要扩展单例对象,只有修改代码,没有其他途径。
不符合开闭原则
“Java中的单例模式实例分析”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。