单例模式简介
单例设计模式,就是采取一定的方法保证整个的软件系统中,对某个类只能存在一个对象实例,并且类只提供一个取得对象实例的方法(静态方法)。
单例模式有八种方式:
- 饿汉式(静态常量)
- 饿汉式(静态代码块)
- 懒汉式(线程不安全)
- 懒汉式(线程安全,同步方法)
- 懒汉式(线程安全,同步代码块)
- 双重检查
- 静态内部类
- 枚举
饿汉式(静态常量)
步骤如下:
- 构造器私有化(防止通过 new 来创建对象——申请内存空间)
- 类的内部创建对象
- 向外暴露一个静态的公有方法(getInstance)
代码实现:
1 | package com.yubulang.type01; |
2 | |
3 | /** |
4 | * 饿汉式(静态常量) |
5 | */ |
6 | public class Singleton { |
7 | // 1. 私有化构造器,防止外部new创建对象 |
8 | private Singleton() { |
9 | } |
10 | |
11 | // 2. 本类内部创建对象实例 |
12 | private final static Singleton instance = new Singleton(); |
13 | |
14 | // 提供给一个公有的静态方法,返回实例对象 |
15 | public static Singleton getInstance() { |
16 | return instance; |
17 | } |
18 | } |
测试代码:
1 | package com.yubulang.type01; |
2 | |
3 | import org.junit.Test; |
4 | |
5 | public class SingletonTest { |
6 | |
7 | public void test() { |
8 | Singleton singleton01 = Singleton.getInstance(); |
9 | Singleton singleton02 = Singleton.getInstance(); |
10 | System.out.println(singleton01 == singleton02); |
11 | } |
12 | } |
优缺点说明:
优点:这个写法简单,就是在类装载的时候完成实例化。避免了线程同步问题。
缺点:在类装载的时候就完成实例化,没有达到 Lazy Loading 的效果。如果从始至终从未使用过这个实例,会造成内存浪费。
这种方式基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,在单例模式中大多数都是调用 getInstance 方法,但是导致类装载的原因很多种,因此不能确定其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 就没有达到 lazyloading 的效果。
结论:这个单例模式可用,可能造成内存浪费
饿汉式(静态代码块)
1 | package com.yubulang.type02; |
2 | |
3 | /** |
4 | * 饿汉式(静态常量) |
5 | */ |
6 | public class Singleton { |
7 | // 私有化构造器,防止外部new创建对象 |
8 | private Singleton() { |
9 | } |
10 | |
11 | // 本类内部声明 |
12 | private static Singleton instance; |
13 | |
14 | static { |
15 | // 在静态代码块中创建单例对象 |
16 | instance = new Singleton(); |
17 | } |
18 | |
19 | // 提供给一个公有的静态方法,返回实例对象 |
20 | public static Singleton getInstance() { |
21 | return instance; |
22 | } |
23 | } |
优缺点说明:
- 这种方式和上面的方式其实类似,只不过将实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。优缺点和上面一样。
- 结论:这种单例模式可用,但是可能造成内存浪费。
总结
贪多嚼不烂,我们就先记住两种饿汉式写法。其主要的中心思想还是将类构造私有化,提供一个静态方法创建实例对象,利用类加载器加载时创建来达到可用的安全单例,但是容易造成内存浪费。