单例设计模式
正常情况下一个类可以创建多个对象
public static void main(String[] args) {
// 正常情况下一个类可以创建多个对象
Person p1 = new Person();
Person p2 = new Person();
Person p3 = new Person();
}
但是有些时候的某些类, 我们希望只能创建单一的一个对象, 这时候我们需要使用到单例设计模式, 下面我们来介绍一下单例设计模式.
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
单例设计模式的作用
- 目的: 保证一个类仅有一个实例,并提供一个访问它的全局访问点。
- 解决的问题: 一个全局使用的类频繁地创建与销毁。
- 什么时候使用: 当您想控制实例数目,节省系统资源的时候。
单例设计模式的实现步骤
- 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
- 在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型的成员变量。
- 定义一个静态方法返回这个唯一对象。
饿汉式
饿汉单例设计模式就是使用类的时候已经将对象创建完毕,不管以后会不会使用到该实例化对象,先创建了再说。很着急的样子,故被称为“饿汉模式”。
代码如下:
public class Singleton {
// 1.将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
private Singleton() {}
// 2.在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型的成员变量。
private static final Singleton instance = new Singleton();
// 3.定义一个静态方法返回这个唯一对象。
public static Singleton getInstance() {
return instance;
}
}
懒汉式
懒汉单例设计模式就是调用getInstance()方法时实例才被创建,先不急着实例化出对象,等要用的时候才例化出对象。不着急,故称为“懒汉模式”。
懒汉式-普通方式【不能用】
(1) 实现步骤
- 私有构造方法
- 创建一个对象的声明, 但是不实例化.
- 对外暴露一个用于获取对象的静态方法
- 判断对象是否为空
- 如果为空就实例化
- 返回当前对象
(2) 代码实现
package com.itheima._01single.demo02;
/**
*
* 懒汉式
* 懒: 上来不着急创建对象, 什么时候用, 什么时候创建
*
*/
public class Singleton {
// 1. 私有构造
private Singleton() {}
// 2. 只声明本类的引用, 后面不创建对象
private static Singleton instance;
// 3. 提供公共的用来获取本类对象的方法
public static Singleton getInstance() {
// 判断: 如果instance为null => 创建对象
if (instance == null) {
// 为了看到效果, 所以让线程睡一会
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 什么时候用, 什么时候创建
instance = new Singleton();
}
// 如果instance不为null, 直接返回
return instance;
}
}
(3) 优缺点
- 优点: 起到了懒加载的效果
- 缺点: 多线程环境下会出现问题, 可能创建出多个实例, 不符合单例的原则.
结论: 在实际开发中不要使用这种方式.
懒汉式 - 同步方法【能用】
(1) 实现步骤
-
私有构造方法
-
类的内部创建对象
-
对外暴露一个用于获取对象的静态同步方法
-
判断对象是否为空
-
如果为空就实例化
-
返回当前对象
-
(2) 代码实现
package com.itheima._01single.demo03;
/**
* 懒汉式 - 同步方法
*/
public class Singleton {
// 1. 私有构造
private Singleton() {
}
// 2. 声明引用
private static Singleton instance; // 实例
// 3. 获取对象的方法
public static synchronized Singleton getInstance() {
// 判断, 如果为null, 创建对象
if (instance == null) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new Singleton();
}
// 如果不为null, 直接返回
return instance;
}
}
(3) 优缺点
优点: 解决了多线程中创建多个对象的问题
缺点: 效率太低, 每次执行都要进入同步方法.
总结: 实际开发中不推荐使用
懒汉式 - 同步代码块【过渡】
(1) 实现步骤
-
私有构造方法
-
类的内部创建对象
-
对外暴露一个用于获取对象的静态方法
-
判断对象是否为空
-
在同步代码块中进行判断, 如果为空就实例化
-
返回当前对象
-
(2) 代码实现
package com.itheima._01single.demo04;
/**
*
* 懒汉式 - 同步代码块
*
* 这种实现方式是有问题的, 我要用这种方式给大家引出最终的BOSS
*
* 刚才使用同步方法的时候, 可以保证只创建一个对象, 但是每一次都要先获取锁
*
* 1. 不每一次上来就直接先获取锁
* 2. 等判断完为null, 再获取锁, 创建对象
*
*/
public class Singleton {
// 1. 私有构造
private Singleton() {}
// 2. 声明引用
private static Singleton instance;
// 3. 公共方法
// 把获取锁对象的时机延后, 且不满足条不会获取锁(减少了获取锁的次数)
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
}
(3) 优缺点
- 缺点: 想提高同步方法的效率, 但是又出现了多线程中创建多个对象的问题
实际开发中不能使用
双重检查(Double-Check)【推荐使用】
(1) 实现步骤
- 私有构造方法
- 类的内部创建对象(使用volatile修饰)
- 对外暴露一个用于获取对象的静态方法
- 判断对象是否为空
- 同步代码块
- 在同步代码块中再次判断对象是否为空
- 如果为空再实例化
- 最后返回当前对象
(2) 代码实现
package com.itheima._01single.demo05;
/**
*
* 双重检查
*
* 1. 两个if(instance == null) 的判断
* 2. 加上volatile关键字, 保证共享数据可可见性
*
*/
public class Singleton {
// 1. 私有构造
private Singleton() {}
// 2. 声明引用
private static volatile Singleton instance;
// 3. 提供获取对象的方法
public static Singleton getInstance() {
// 如果对象不存在
if (instance == null) {
// 线程1, 判断是null || 线程2, 判断是null
// 同步锁
// 问题: 多线程中有可能创建多个对象
synchronized (Singleton.class) {
if (instance == null) {
// 创建对象
instance = new Singleton();
}
}
}
return instance;
}
}
(3) 优缺点
- 进行两次null检查, 可以保证线程安全
- 实例化的代码只会执行一次, 后面再次访问的时候直接return实例化的对象, 避免了同步的多次执行.
- 线程安全, 懒加载, 效率较高
总结: 实际开发中, 推荐使用这种单例设计模式
双重检查单例的图解
多例设计模式
多例模式,是一种常用的软件设计模式。通过多例模式可以保证系统中,应用该模式的类有固定数量的实例。多例类要自我创建并管理自己的实例,还要向外界提供获取本类实例的方法。
实现步骤
1.创建一个类, 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
2.在类中定义该类被创建的总数量
3.在类中定义存放类实例的list集合
4.在类中提供静态代码块,在静态代码块中创建类的实例
5.提供获取类实例的静态方法
实现代码如下
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Multition {
// 定义该类被创建的总数量
private static final int maxCount = 3;
// 定义存放类实例的list集合
private static List instanceList = new ArrayList();
// 构造方法私有化,不允许外界创建本类对象
private Multition() {
}
static {
// 创建本类的多个实例,并存放到list集合中
for (int i = 0; i < maxCount; i++) {
Multition multition = new Multition();
instanceList.add(multition);
}
}
// 给外界提供一个获取类对象的方法
public static Multition getMultition(){
Random random = new Random();
// 生成一个随机数
int i = random.nextInt(maxCount);
// 从list集合中随机取出一个进行使用
return (Multition)instanceList.get(i);
}
}
测试结果
public static void main(String[] args) {
// 编写一个循环从中获取类对象
for (int i = 0; i < 10; i++) {
Multition multition = Multition.getMultition();
System.out.println(multition);
}
}
多例模式可以保证系统中一个类有固定个数的实例, 在实现需求的基础上, 能够提高实例的复用性.