Toc
  1. 设计模式的思想
  2. 单例模式介绍
  3. Java类加载顺序
  4. 饿汉式单例模式
  5. 懒汉式单例模式
Toc
0 results found
Espada
千千万万设计模式之单例模式

这下好了,对象也要共享不让你new了。

本系列已经开源至GitHub,repository地址

最初只是为了做个人笔记,参考了前人的笔记和博客,在这里我用更接潮流、更接地气的例子来帮助加深理解记忆。

由于本人技术水平也有限,着重点在于思想的理解,若出现任何错误、不恰当内容,欢迎各位前来issues指正。

感谢任何分享、开源学习教程的前辈,正是有你们这一群乐于奉献的人才让整个生态变得生机勃勃、让这个行业日新月异。

设计模式的思想

新开个系列,把前人写的设计模式好好地学习钻研下,这里做点笔记。这里参考的博文地址

什么是设计模式?放在两年前的我,不但不了解它,也不会去重视它。我只在乎能够猥琐实现,程序运行不报错。但是在版本快速迭代、需求明天就改、框架稳定升级的现在,自己也写了不少代码,积累了一些经验和知识,在快速成长的过程中,愈发觉得优秀的开发工程师就是会比平庸的开发工程师在设计、建模的过程中去花更多时间去思考、去推理。其实我觉得我也算是考虑问题比较全面、比较细致的人了(大雾)。

这里又可以引申出面向对象和面向过程,优秀的开发工程师可以把面向过程的程序写得非常内聚、可扩展性好、具备一定的复用性;而平庸的程序员用面向对象的语言一样也能把程序写得松散随意、毫无抽象与建模、模块耦合严重、维护性差。而设计模式也是考究程序员对业务的建模能力,以及对架构的宏观掌握能力,面向对象来说,以对象模型为核心,丰富模型的内涵,扩展模型的外延,通过模型的行为组合去共同解决某一力问题,抽象的能力必不可少。

啥是面向对象?总结就是四大特性:封装、继承、多态、抽象。这里不细讲了,留到之后在总结一篇post吧。

单例模式介绍

我们先不谈什么是单例模式,我想其实很多人其实最关心的是什么时候需要用到单例模式?使用单例模式之后有什么提升与益处?

首先我们知道单例单例,可以简单理解为单个实例,而实例化是指在面向对象的编程中,把用类创建对象的过程称为实例化。是将一个抽象的概念类,具体到该类实物的过程。

那么什么情况下会优先、或者强制使用单例模式创建实例呢:

  • 数据库连接池,注意: 这里的单例指的是数据库连接池对象,而不是单个连接对象,这点一定要分清。
  • Spring中的Bean,获取实例的时候都是默认单例模式,所以多线程是要注意(面试题警告⚠)。
  • API接口中的tokenid的获取,比如百度AI文字识别的accessToken中的获取,一般该token有一定的有效期,需要自行管理,当失效时需重新获取的方式,采用单例模式就可以很好的节约资源。
  • 多个子类想共享一个父类的线程池的业务场景,频繁创建ThreadPool应该是不合适的,其实这个时候static单例化ThreadPool,注入是比较好的选择(这个例子比较特殊,可能是设计问题高耦合,可以不看)。

单例模式确保某个类只有一个实例,而且是自身创建唯一实例,提供一个全局访问的入口。网上的单例模式的写法大致就是3种:懒汉式单例模式、饿汉式单例模式、登记式单例模式:

  • 懒汉式单例模式:在类加载时不初始化。
  • 饿汉式单例模式:在类加载时就完成了初始化,所以类加载比较慢,但获取对象的速度快。
  • 登记式单例模式:它的单例在类被装载的时候就被实例化了,内部也算是饿汉式单例模式。

登记式单例模式由于本人不太熟悉,所以在本文中只讲述前两种。

Java类加载顺序

为什么要叫饿汉式、懒汉式呢?饿汉式则可以想象成因为太饿了,在类加载时就迫不及待完成了实例化,但是如果从初始化到线程结束都没有使用过的,就是变成了资源浪费。这里需要拓展下Java类的加载机制:

/**
* @ClassName Initialization
* @Description 类的初始化顺序
* @Author MatthewHan
* @Date 2019/7/30 10:02
* @Version 1.0
**/
public class Initialization {

private static IKun iKun = new IKun();

static {
System.out.println("静态代码块");
}

{
System.out.println("非静态代码块");
}

private Initialization(){
System.out.println("构造器");
}

public static void main(String[] args){
System.out.println("top-静态函数");
new Initialization();
System.out.println("bottom-静态函数");
}
}
class IKun{

IKun(){ System.out.println("静态变量"); }

}

Java类加载顺序

从打印的结果可以看到Java类的加载顺序大致是:
静态成员/静态代码块 --> main方法 --> 非静态成员/非静态代码块 --> 构造器

饿汉式单例模式

上面讲到了,饿汉式单例是在类加载就是内部实例化对象,并且不允许外部创建实例。饿汉式单例模式的实现也比较简单,记住无参构造器私有化,内部实例化对象,外部通过static方法获取对象。

我们都知道坤坤是一个对细节把控很有追求的人,从那段舞蹈的诸多细节就能够看出来。尤其是最后的白色吊带滑落,堪称经典之举,坤坤通过有意的小失误将前面表演精心铺垫的「舞王」设定迅速推翻,营造了一种反差萌拉近与粉丝的距离,白带异常的表现也让粉丝们感同身受。

白色吊带异常

坤坤自从白色吊带表演大火🔥之后,淘宝厂商为了恰烂钱💰纷纷效仿推出「坤坤同款异常白色吊带」、「坤坤潮流异常背带」的爆款产品。谁知道厂商还没开始恰烂钱💰的时候,坤坤就机智的把「坤式白色吊带」和「白色吊带异常」申请了发明专利、外观设计专利、实用专利,将「坤式白色吊带」这个商品和「白色吊带异常」这个艺术作品的所有权把握在了自己手里。「白色吊带异常」的表演不允许外界直接模仿、抄袭,必须先向坤坤申请,支付一定费用才购买了「坤式白色吊带」对象licence后才可以表演「白色吊带异常」,并且使用完后要进行归还,这样就能确保只有一个白色吊带在外部商演能够很好的锁定。孙哥烂钱💰都恰得没这么6嗷~

/**
* @ClassName EarlySingleton
* @Description 饿汉式单例模式
* @Author MatthewHan
* @Date 2019/7/30 10:50
* @Version 1.0
**/
public class EarlySingleton {

/**
* 独享的moment
*/
private EarlySingleton(){}

/**
* 内部实例化一个白色吊带对象
*/
private static EarlySingleton suspenders = new EarlySingleton();

/**
* 全局入口点
* @return
*/
public static EarlySingleton getInstance(){
return suspenders;
}

/**
* 白带异常的演出
* @return
*/
public Suspenders slipping(){
return new Suspenders("white","白色吊带异常");
}


}

/**
* 坤坤申请专利了嗷
*/
@ToString
@Setter(value = AccessLevel.PUBLIC)
@Getter(value = AccessLevel.PUBLIC)
@AllArgsConstructor
@NoArgsConstructor
class Suspenders{


private String color;
private String action;

}
@Test
public void getInstance() {
// 向坤坤申请
EarlySingleton s = EarlySingleton.getInstance();
assertNotNull(s);
// 白色吊带异常演出
System.out.println(s.slipping());
}

共享白色吊带√

优点:

  • 线程安全

缺点:

  • 类初始化实例化对象后未被调用则是浪费资源的表现

懒汉式单例模式

坤坤虽然唱跳俱佳,但也耐不住是条懒狗🐶,只有等到电话、message、inbox连环催,他才开始发货。

/**
* @ClassName LazilySingleton
* @Description 懒汉式单例模式
* @Author MatthewHan
* @Date 2019/7/30 15:46
* @Version 1.0
**/
public class LazilySingleton {

/**
* 同样的避免外部实例化
*/
private LazilySingleton() {
}

private static LazilySingleton suspenders = null;

/**
* 线程不安全
* @return
*/
public static LazilySingleton getInstance() {
if (suspenders == null) {
suspenders = new LazilySingleton();
}
return suspenders;
}
}

优点:

  • 单例实例在第一次被使用时构建

缺点:

  • 不加锁的话,存在线程安全问题,即使加了锁,对性能也产生了影响。

注: 为什么说懒汉式单例模式是线程不安全的呢,假如有两个客户线程准备购买「坤式白色吊带」和「白色吊带异常」进行商演,第一位客户提出申请,坤坤刚回复马上交付,另一位客户的聊天窗口弹了出来也问能马上交付吗?坤坤一急立马把「白色吊带」和「白色吊带异常」的licence的交付第二位客户,交付完之后坤坤想到第一位客户也在等待,于是又把新的「白色吊带」和license交付给了第一位客户。然而现在已经有两个对象游离在外了,坤坤表示头很大。

坤坤发现自己亲自处理不但没牌面,而且容易出事情,但是交给经纪人又不放心,思来想去坤坤还是决定把这项业务交给哥哥孙笑川打理,自己则脱离去做更纯粹的音乐去了。

/**
* @ClassName Singleton
* @Description 静态内部类
* @Author MatthewHan
* @Date 2019/7/30 16:16
* @Version 1.0
**/
public class Singleton {

/**
* 把业务交给哥哥孙笑川打理
*/
private static class SunXiaoChuan{
private static Singleton suspenders = new Singleton();
}
private Singleton(){}

public static Singleton getInstance(){
return SunXiaoChuan.suspenders;
}
}

优点:

  • 线程安全
  • 单例实例在第一次被使用时构建

缺点:

  • 暂无

还是孙哥办事靠谱嗷,毕竟恰烂钱💰带师~

龙鸣
"老罗"
本文作者:Espada
版权声明:本文首发于Espada的博客,转载请注明出处!