模式千万条,生命就一条,可以下班了。
本系列已经开源至 GitHub,repository 地址。
最初只是为了做个人笔记,参考了前人的笔记和博客,在这里我用更接潮流、更接地气的例子来帮助加深理解记忆。
由于本人技术水平也有限,着重点在于思想的理解,若出现任何错误、不恰当内容,欢迎各位前来 issues 指正。
感谢任何分享、开源学习教程的前辈,正是有你们这一群乐于奉献的人才让整个生态变得生机勃勃、让这个行业日新月异。
设计模式的思想
新开个系列,把前人写的设计模式好好地学习钻研下,这里做点笔记。这里参考的博文地址
什么是设计模式?放在两年前的我,不但不了解它,也不会去重视它。我只在乎能够猥琐实现,程序运行不报错。但是在版本快速迭代、需求明天就改、框架稳定升级的现在,自己也写了不少代码,积累了一些经验和知识,在快速成长的过程中,愈发觉得优秀的开发工程师就是会比平庸的开发工程师在设计、建模的过程中去花更多时间去思考、去推理。其实我觉得我也算是考虑问题比较全面、比较细致的人了(大雾)。
这里又可以引申出面向对象和面向过程,优秀的开发工程师可以把面向过程的程序写得非常内聚、可扩展性好、具备一定的复用性;而平庸的程序员用面向对象的语言一样也能把程序写得松散随意、毫无抽象与建模、模块耦合严重、维护性差。而设计模式也是考究程序员对业务的建模能力,以及对架构的宏观掌握能力,面向对象来说,以对象模型为核心,丰富模型的内涵,扩展模型的外延,通过模型的行为组合去共同解决某一力问题,抽象的能力必不可少。
啥是面向对象?总结就是四大特性:封装、继承、多态、抽象。这里不细讲了,留到之后在总结一篇 post 吧。
单例模式介绍
我们先不谈什么是单例模式,我想其实很多人其实最关心的是什么时候需要用到单例模式?使用单例模式之后有什么提升与益处?
首先我们知道单例单例,可以简单理解为单个实例,而实例化是指在面向对象的编程中,把用类创建对象的过程称为实例化。是将一个抽象的概念类,具体到该类实物的过程。
那么什么情况下会优先、或者强制使用单例模式创建实例呢:
- 数据库连接池,注意: 这里的单例指的是数据库连接池对象,而不是单个连接对象,这点一定要分清。
Spring
中的Bean
,获取实例的时候都是默认单例模式,所以多线程是要注意(面试题警告 ⚠)。API
接口中的token
、id
的获取,比如百度 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 类的加载顺序大致是:
静态成员/静态代码块
--> 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;
}
}
优点:
- 线程安全
- 单例实例在第一次被使用时构建
缺点:
- 暂无
还是孙哥办事靠谱嗷,毕竟恰烂钱 💰 带师~
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于