听说微信搜索《Java鱼仔》会变更强哦!
本文收录于github和gitee ,里面有我完整的Java系列文章,学习或面试都可以看看哦
(一)什么是单例模式
在程序中,每new() 一个对象,就会有一个对象实例生成。有时候在程序中,需要有一个在完整运行状态下只需要生成一个的实例,我们把这种实例称为单例。 抽象到设计模式中,这种只生成一个实例的模式就是单例模式(Singleton)。
(二)单例模式的实现
单例模式的实现有很多种方式,归根到底是保证new Class()的操作只做一次,在大多数的实现上,会将需要实现单例的类的构造方法改为private修饰,使得无法通过new命令创建对象实例。
(三)单例模式的代码实现
代码模式有很多种实现方式,主要有饿汉式、懒汉式以及有点特殊的单例注册表(Spring的实现方式)。下面会针对上面的这三种方式各自给出一种实现方式。
3.1 饿汉式
饿汉式简单来说就是在项目启动时就将对象实例创建出来,可以用来做需要执行一次的操作:
publicclassHungrySingleton { publicstaticfinalHungrySingletonINSTANCE; static { INSTANCE=newHungrySingleton(); } privateHungrySingleton(){ } }
调用时只需要直接调用INSTANCE变量就行:
HungrySingletoninstance=HungrySingleton.INSTANCE;
静态代码块饿汉式适合从外部文件中获取数据时使用,我在项目下新建一个info.properties,里面包含一条内容info=hello。饿汉式单例代码如下:
publicclassHungrySingleton2 { publicstaticfinalHungrySingleton2INSTANCE; privateStringinfo; static { Propertiesproperties=newProperties(); try { properties.load(HungrySingleton2.class.getClassLoader().getResourceAsStream("info.properties")); } catch (IOExceptione) { e.printStackTrace(); } INSTANCE=newHungrySingleton2(properties.getProperty("info")); } privateHungrySingleton2(Stringinfo){ this.info=info; } publicStringgetInfo() { returninfo; } publicvoidsetInfo(Stringinfo) { this.info=info; } publicStringtoString() { return"Singleton3{"+"info='"+info+'\''+'}'; } publicstaticvoidmain(String[] args) { HungrySingleton2instance=HungrySingleton2.INSTANCE; System.out.println(instance.getInfo()); } }
3.2 懒汉式
懒汉式是指当第一次调用时才会生成这个实例,后面再调用就只会使用之前生成的实例。懒汉式是实际编写单例模式代码时用的比较多的方案,下面给出一段十分经典的懒汉式单例模式代码案例:
publicclassLazySingleton { privatestaticvolatileLazySingletonInstance; privateLazySingleton(){}; publicstaticLazySingletongetInstance(){ if (Instance==null){ synchronized (LazySingleton.class){ if(Instance==null){ Instance=newLazySingleton(); } } } returnInstance; } }
在面试中,往往这道题能引出volatile和synchorized这两个知识点。
3.3 单例注册表
我看网上很少有人介绍这种单例模式,单例注册表是Spring中Bean单例的核心实现方案。可以通过一个ConcurrentHashMap存储Bean对象,保证Bean名称唯一的情况下也能保证线程安全。下面是单例注册表的简单实现:
publicclassRegSingleton { privatefinalstaticMap<String, Object>singletonObjects=newConcurrentHashMap<>(16); privateRegSingleton(){} publicstaticObjectgetInstance(StringclassName){ if (StringUtils.isEmpty(className)) { returnnull; } if (singletonObjects.get(className) ==null) { try { singletonObjects.put(className, Class.forName(className).newInstance()); } catch (Exceptione) { e.printStackTrace(); } } returnsingletonObjects.get(className); } }
新建一个测试类:
publicclassUser { privateStringid; publicvoidsetId(Stringid) { this.id=id; } publicStringgetId() { returnid; } publicbooleanequals(Objecto) { if (this==o) returntrue; if (!(oinstanceofUser)) returnfalse; Useruser= (User) o; returnObjects.equals(getId(), user.getId()); } publicinthashCode() { returnObjects.hash(getId()); } }
最后测试单例是否生效:
publicclassMain { publicstaticvoidmain(String[] args) { Useruser1= (User) RegSingleton.getInstance("com.javayz.singleton.User"); Useruser2= (User) RegSingleton.getInstance("com.javayz.singleton.User"); System.out.println(user1==user2); } }
(四)单例模式在Spring中的最佳实践
单例模式的最佳实践就是Spring中的Bean了,Spring中的Bean默认都是单例模式,Spring实现单例的方式就采用了单例注册表的方式。
首先在Bean工厂中,如果设置了Spring的Bean模式为单例模式,Spring就会通过getSingleton的方式去获取单例Bean对象。
接着就会进
入到DefaultSingletonBeanRegistry类的getSingleton方法中,忽略掉其他代码,只看单例Bean生成相关代码
publicclassDefaultSingletonBeanRegistryextendsSimpleAliasRegistryimplementsSingletonBeanRegistry { /** Cache of singleton objects: bean name to bean instance. */privatefinalMap<String, Object>singletonObjects=newConcurrentHashMap<>(256); publicObjectgetSingleton(StringbeanName, ObjectFactory<?>singletonFactory) { //一系列处理操作 } finally { if (recordSuppressedExceptions) { this.suppressedExceptions=null; } afterSingletonCreation(beanName); } if (newSingleton) { //如果实例对象不存在,注册到单例注册表中addSingleton(beanName, singletonObject); } } returnsingletonObject; } } }
对应的addSingleton方法就是将对象加入到单例注册表中:
(五)总结
至此,单例模式的内容差不多就结束了,结合源码看设计模式每次都有新收获。我是鱼仔,我们下期再见!