设计模式学习笔记:单例模式 Singleton

栏目: IT技术 · 发布时间: 5年前

内容简介:单例模式:饿汉、懒汉、枚举类!!!!

设计模式学习笔记:单例模式 Singleton

单例模式:饿汉、懒汉、枚举类!!!!

设计模式学习笔记:单例模式 Singleton

单例模式是 设计模式 中比较经常听说的设计模式,也是比较容易掌握的设计模式。基本上接触过设计模式的人别的模式不一定能说出来,但是一般“单例模式”和“工厂模式”是都能说出来的。

很多时候,我们都会以为单例模式是比较好掌握的,但是后来在我的学习当中,我发现还是有很多问题是没有考虑到的,甚至是想象不到的。

单例模式是要使类的实例在内存中只有一份。听起来挺容易的,但是这个还真是没有想象的那么简单。我的代码使用 Java 来进行描述。

通常情况下,在使用 Java 来完成 单例模式 的时候,都知道存在两种写法,一种是饿汉模式,另一种是懒汉模式。所谓饿汉模式,就是在类加载入内存之后,直接实例化一个对象出来;懒汉模式是在需要的时候再去实例化一个对象出来。

为什么有饿汉模式和懒汉模式呢?这得从它们的加载时机来考虑。很多人认为,饿汉模式在类进入内存就实例化一个对象有些不妥,因为没有使用,为什么要着急实例化呢,所以就出现了懒汉模式。懒汉模式是在需要的时候才去实例化类的对象,但是懒汉模式会因为多线程的问题,会导致实例化多个对象出来,而此时就需要解决多线程同步的问题。解决多线程同步的问题,就需要用到锁,那么就又带来了效率上的问题。

说了这么多,那么来看看,到底如何来使用 Java 语言完成一个 单例模式。

饿汉模式

先来看看饿汉模式的代码:

public class Singleton01 {


private static final Singleton01 INSTANCE = new Singleton01();


// 构造函数为 private

private Singleton01() {}


public static Singleton01 getInstance() { return INSTANCE; }


// 此处模拟类中处理业务的方法

public void m() {

System.out.println("m");

}


public static void main(String[] args) {

Singleton01 s1 = Singleton01.getInstance();

Singleton01 s2 = Singleton01.getInstance();


// 两个引用指向的是一个对象

System.out.println(s1 == s2);

}

}


单例模式的第一步就是将 构造方法 的访问修饰符设置为 private,使得外部无法直接实例化。然后在类中定义一个静态的 getInstance 方法用来获取实例。

使用饿汉模式的单例,在类加载到内存后,静态变量只实例化一次,JVM 保证其线程的安全。

其缺点是,不管该类是否要使用,都会马上得到一个实例。因此,这就有了懒汉模式。

懒汉模式

懒汉模式的单例的代码:

public class Singleton06 {

private static volatile Singleton06 INSTANCE;


private Singleton06() {}


public static Singleton06 getInstance() {

if (INSTANCE == null) {

// 双重检查

synchronized (Singleton06.class) {

if (INSTANCE == null) {

try {

Thread.sleep(1);

} catch (InterruptedException e) {

e.printStackTrace();

}


INSTANCE = new Singleton06();

}

}

}


return INSTANCE;

}


public void m() {

System.out.println("m");

}


public static void main(String[] args) {

for (int i = 0; i < 100; i ++) {

new Thread(()->{

System.out.println(Singleton06.getInstance().hashCode());

}).start();

}

}

}


以上就是懒汉模式的代码。

在懒汉模式中,使用了 synchronized 来解决方法“不可重入”的问题,其中使用 Thread.sleep 来让线程休息一下,从而让出线程所占用的 CPU  而产生线程的切换。可以把第一个 if 判断和 synchronized 两行删掉,只留下最里面的 if 语句块的内容,就会发现会实例化多个对象了。

实例化多个对象

在 Java 中提供了反射的机制,即使使用单例模式,仍然可以实例化出多个对象。无论是上面的饿汉模式,还是懒汉模式,都可以实例化多个实例。

这里使用第一个饿汉模式的代码进行测试,测试代码如下:

Class<?> aClass = Class.();

Singleton01 s3 = (Singleton01) aClass.newInstance();

代码很简单,只有上面两句,但是这样就已经实例化出了一个对象,且通过 s3 可以调用该类中的方法。

因此这样,就可以实例化对象出来了,内存中就有了一个类的多个实例了。

枚举类的单例

枚举在很多语言中都有,一般情况就是定义一些有限的常量。其实,枚举类中可以定义方法。看一下枚举类的单例代码,代码如下:

public enum Singleton08 {

INSTANCE;


public void m() {

System.out.println("m");

}


public static void main(String[] args) {

for (int i = 0; i < 100; i ++) {

new Thread(()->{

System.out.println(Singleton08.INSTANCE.hashCode());

}).start();

}

}

}

以上代码,仍然通过 new Thread 多线程来得到其实例。

但是,通过输出可以看出,其 hashCode 始终是一样的。

接着使用上面的反射来获取枚举类的实例,代码如下: 

Class<?> aClass = ;

{

aClass = Class.();

Singleton01 s3 = (Singleton01) aClass.newInstance();

然后代码执行到 newInstance 方法时会报错,提示访问异常。

因为枚举类没有定义构造函数,因此无法实例化。

也就是说使用枚举类,即可以保证线程的安全,也可以防止反射来实例化。算是一种完美的方法。

最后

看似简单的单例模式,其中竟然也蕴含着这么多的知识点,学完真是受益非浅。虽然只是一个单例模式,掌握了一种设计模式,但是从各种实现中,又学到了很多其他的知识。比如,类实例化的时机,多线程方法的不可重入,枚举类的另类用法等。

所以,知识如果能够串联起来,那么才能把学到的知识融会贯通,真正掌握和吸收。

这就是我关于设计模式中单例模式的一篇笔记。

设计模式学习笔记:单例模式 Singleton


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

构建之法(第三版)

构建之法(第三版)

邹欣 / 人民邮电出版社 / 2017-6 / 69.00元

软件工程牵涉的范围很广, 同时也是一般院校的同学反映比较空洞乏味的课程。 但是,软件工程 的技术对于投身 IT 产业的学生来说是非常重要的。作者有在世界一流软件企业 20 年的一线软件开 发经验,他在数所高校进行了多年的软件工程教学实践,总结出了在 16 周的时间内让同学们通过 “做 中学 (Learning By Doing)” 掌握实用的软件工程技术的教学计划,并得到高校师生的积极反馈。在此 ......一起来看看 《构建之法(第三版)》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

SHA 加密
SHA 加密

SHA 加密工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具