绪论
设计模式分为三种类型,共 23 种
- 创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式。
- 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
- 行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、 解释器模式(Interpreter 模式)、状态模式、策略模式、职责链模式(责任链模式)。
单例模式
1、介绍
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。
比如 Hibernate 的 SessionFactory,它充当数据存储源的代理,并负责创建Session 对象。SessionFactory 并不是 轻量级的,一般情况下,一个项目通常只需要一个 SessionFactory 就够,这是就会使用到单例模式。
2、单例设计模式八种方式
1. 饿汉式(静态常量)
2.饿汉式(静态代码块)
3.懒汉式(线程不安全)
4.懒汉式(线程安全,同步方法)
5.懒汉式(同步代码块)
6.双重检查
7.静态内部类
8.枚举
2.1 饿汉式(静态常量)
步骤如下:
- 构造器私有化 (防止 new )
- 类的内部创建对象
- 向外暴露一个静态的公共方法。getInstance
- 代码实现
/**
* 单例模式之 饿汉式(静态变量)
* 缺点,可能并没有用到这个类,浪费内存。
*/
public class Singleton {
private Singleton(){
}
private final static Singleton singleton = new Singleton();
public static Singleton getInstance() {
return singleton;
}
}
优缺点说明:
- 优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
- 缺点:在类装载的时候就完成实例化,没有达到LazyLoading的效果。如果从始至终从未使用过这个实例,则
会造成内存的浪费 - 这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,在单例模式中大 多数都是调用 getInstance 方法,但是导致类装载的原因有很多种,因此不能确定有其他的方式(或者其他的静 态方法)导致类装载,这时候初始化 instance 就没有达到 lazy loading 的效果
- 结论:这种单例模式可用,可能造成内存浪费。
2.2 饿汉式(静态代码块)
/**
* 单例模式之 饿汉式(静态代码块)
* 缺点,可能并没有用到这个类,浪费内存。
*/
public class Singleton {
private static Singleton singleton;
private Singleton(){
}
static {
singleton = new Singleton();
}
public static Singleton getInstance() {
return singleton;
}
}
这种方式和第一种方式类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执 行静态代码块中的代码,初始化类的实例。优缺点和上面是一样的。
2.3 懒汉式(线程不安全)
/**
* 单例模式之 懒汉式(静态方法)
* 缺点,存在线程安全问题,不推荐使用
*/
public class Singleton {
private static Singleton singleton;
private Singleton(){
}
//当处于多线程的情况下,几个线程同时进来,那就失去了单例的效用了。
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
2.4 懒汉式(线程安全,同步方法)
/**
* 单例模式之 懒汉式(加锁)
* 缺点,结局了线程安全的问题,但是每次调用都要进行一次同步判断,很影响效率,不推荐!
*/
public class Singleton {
private static Singleton singleton;
private Singleton(){
}
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
2.5 懒汉式(同步代码块)
/**
* 单例模式之 懒汉式(代码块加锁)
* 这种方式并没有起到任何的效果,多线程情况下还是会存在多个对象的情况。
*/
public class Singleton {
private static Singleton singleton;
private Singleton(){
}
/**
* 当线程a, b, c进来的时候,singleton都是null,所以都进到判断里面。
* 由于加了锁,所以a先进去实例对象后,b和c在外面等着,a出去以后锁释放,b和c先后又继续实例对象了。
* 所以这种方式并不能做到单例!!!
**/
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
singleton = new Singleton();
}
}
return singleton;
}
}
2.6 双重检查
/**
* 单例模式之 懒汉式(双重检查)
* 这种方式推荐使用的。
*/
public class Singleton {
private static volatile Singleton singleton; // volatile 数据修改后做一个即时的更新
private Singleton(){
}
/**
* 当线程a, b, c进来的时候,singleton都是null,所以都进到判断里面。
* 由于加了锁,所以a先进去实例对象后,b和c在外面等着。
* a出去以后锁释放,b和c先后进去,但是因为此时singleton已经不是null了,所以直接出去了。
* 而下次再次调用这个方法的时候,在第一重判断就被拦住了,不用进行同步,又保证了效率。
**/
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
2.7 静态内部类
/**
* 单例模式之 静态内部类
* 好处:
* 1、当一个类在加载的时候,静态内部类是不会被立即加载的。
* 2、类在加载的过程中是线程安全的。
* 这种方式推荐使用的。
*/
public class Singleton {
private Singleton(){
}
private static class InnerSingleton {
private static final Singleton INSTANCE = new Singleton();
}
/**
* 这时候才去初始化静态内部类
**/
public static Singleton getInstance() {
return InnerSingleton.INSTANCE;
}
}
2.8 枚举
/**
* 单例模式之 枚举
* 避免了多线程同步问题
* 这种方式推荐使用的。
*/
public class Singleton {
public static void main(String[] args) {
SingletonEnum singletonEnum = SingletonEnum.INSTANCE;
SingletonEnum singletonEnum2 = SingletonEnum.INSTANCE;
// singletonEnum.sayHello();
System.out.println(singletonEnum == singletonEnum2); //true 表示两个对象相同
}
}
enum SingletonEnum {
INSTANCE;
public void sayHello() {
System.out.println("hello~");
}
}