单例模式是创建者模式中最简单的设计模式,也是23种设计模式中最简单的。
它提供了一种创建对象的最佳方式。
什么是单例模式
保证整个系统中一个类只有一个对象的实例,实现这种功能的方式就叫单例模式。
就是说之前创建对象是使用 new 对象 这种方式,现在用单例模式,就不用你去new了,单例类已经给你创建好了,直接使用就可以了,并且所有的类实例都是同一个。
单例模式特点
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
为什么要用单例模式?
单例模式节省公共资源
比如:大家都要喝水,但是没必要每人家里都打一口井是吧,通常的做法是整个村里打一个井就够了,大家都从这个井里面打水喝。
对应到我们计算机里面,像日志管理、打印机、数据库连接池、应用配置。
所以一个全局使用的类频繁地创建与销毁的情况下可以使用单例模式。
spring中的Bean实例默认就是一个单例。
注意:
只要有反射,任何类都不安全,任何private都是纸老虎,反射可以破坏单例。通过类名.class获得反射对象, 通过
Constructor<类名> declaredConstructor =类名.class.getDeclaredConstructor(null); 获得空的构造函数,再通过
declaredConstructor.setAccessible(true) 开放私有权限, 然后再
类名 instance =declaredConstructor.newInstance() 创建实例。
枚举类型的类来解决反射破坏单例的问题。
本文介绍4种最常用的单例模式
单例模式有两大类:
饿汉式:类加载就会导致该单实例对象被创建
懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建。可以理解成为懒加载。
饿汉式:
1饿汉式(静态变量方式)
推荐指数:⭐⭐⭐⭐
最常见的单例模式。
/*** 饿汉式 * 静态变量创建类的对象 */
public class Singleton {
//私有构造方法
private Singleton() {}
//在成员位置创建该类的对象
private static Singleton instance = new Singleton();
//对外提供静态方法获取该对象
public static Singleton getInstance() {
return instance;
}
}
说明:
该方式在成员位置声明Singleton类型的静态变量,并创建Singleton类的对象instance。
优点:编写简单,使用方便。
缺点:instance对象是随着类的加载而创建的。如果该对象足够大的话,而一直没有使用就会造成内存的浪费。
2饿汉式 (枚举方式)
推荐指数:⭐⭐⭐⭐
枚举类实现单例模式是极力推荐的单例实现模式,因为枚举类型是线程安全的,并且只会装载一次,设计者充分的利用了枚举的这个特性来实现单例模式,枚举的写法非常简单,而且枚举类型是所用单例实现中唯一一种不会被破坏的单例实现模式。
/*** 枚举方式 */
public enum Singleton {
INSTANCE;
}
INSTANCE就是一个单例了。
特点:线程安全,唯一一种不能被序列化和反射手段破坏。
3懒汉式(双重检查锁)
推荐指数:⭐⭐⭐
深入研究指数:⭐⭐⭐⭐⭐
/*** 双重检查方式 */
public class Singleton {
//私有构造方法
private Singleton() {}
private static volatile Singleton instance;
//对外提供静态方法获取该对象
public static Singleton getInstance() {
//第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实例
if(instance == null) {
synchronized (Singleton.class) {
//抢到锁之后再次判断是否为空
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
synchronized锁和volatile保证创建对象的线程安全。
但是这样却不可不免的带来一些性能损耗。
我认为这种双重检查锁方式非常有必要大家去研究学习一下,对线程安全和JMM都能有所了解。—>大家可以看这篇文章
双重检查锁Double Checked Locking Pattern的非原子操作下的危险性
4懒汉式(静态内部类方式)
推荐指数:⭐⭐⭐⭐
是否多线程安全:是
实现难度:一般
静态内部类单例模式中实例由内部类创建,由于 JVM 在加载外部类的过程中, 是不会加载静态内部类的, 只有内部类的属性/方法被调用时才会被加载, 并初始化其静态属性。静态属性由于被static 修饰,保证只被实例化一次,并且严格保证实例化顺序。
/*** 静态内部类方式 */
public class Singleton {
//私有构造方法
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
//对外提供静态方法获取该对象
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
静态内部类单例模式是一种优秀的单例模式,是开源项目中比较常用的一种单模式。在没有加任何锁的情况下,保证了多线程下的安全,并且没有任何性能影响和空间的浪费。(有空间上的需要的就用这种单例模式就非常适合)
经验之谈:
经验之谈:一般情况下,建议使用第 1种饿汉方式(静态变量方式)。只有在要明确实现 lazy loading 效果时,才会使用第4种懒汉式(静态内部类方式)。如果涉及到反序列化创建对象时,可以尝试使用枚举方式。如果有其他特殊的需求,可以考虑使用 双检锁 方式。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/63398.html