Java设计模式——单例模式

文章介绍了单例模式的概念和原则,包括私有构造、静态方法获取实例、线程安全以及反序列化保护。详细阐述了几种单例模式的实现方式,如懒汉式、饿汉式、线程安全的懒汉式、内部类实现和双重校验锁。并指出枚举实现是单例模式的最佳实践,因为它线程安全且防止序列化和反射攻击。
摘要由CSDN通过智能技术生成

        简单定义:单例模式在设计模式中属于创建式模式,要保证一个类只有一个实例,并且提供一个可以访问它的全局变量。

单例模式的原则:

1. 私有构造 (防止类通过常规方法实例化)

2. 以静态方法或者枚举返回实例。 (保证实例的唯一性)

3. 确保实例只有一个,尤其是多线程环境。(保证创建实例的线程安全)

4. 确保反序列化时不会重新构造对象。(在有序列化反序列化的场景下防止单例被莫名破坏)

单例模式实现方法的总结

 懒汉式写法

package com.b_single;


class People {
    private static People people = null;
    //1.对构造方法私有化
    private People () {
        
    }
    //2.写一个静态方法  使用类名直接调用的   不能使用对象来调用的  因为私有化构造方法了
    public static  People getInstance () {
        if (people == null) {
            people = new People();
        }
        return people;
    }
}
public class Demo1 {
    public static void main(String[] args) {
        //当一new就会新建对象,创建唯一的对象,对当前类的无参构造方法加private修饰
        People people = People.getInstance();
        People people1 = People.getInstance();
        System.out.println(people);
        System.out.println(people1);
    }
}

饿汉式写法

package com.b_single;

class SingleCat {
    private static final SingleCat singleCat = new SingleCat();
    private SingleCat() {

    }
    public static SingleCat getInstance() {
        return singleCat;
    }

}
public class Demo3 {
    public static void main(String[] args) {

    }
}

懒汉式-线程安全

只需要对 getUniqueInstance() 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了多次实例化 uniqueInstance 的问题。

public static synchronized Singleton getUniqueInstance() {
    if (uniqueInstance == null) {
        uniqueInstance = new Singleton();
    }
    return uniqueInstance;
}

但是当一个线程进入该方法之后,其它试图进入该方法的线程都必须等待,因此性能上有一定的损耗。

使用类的内部类(线程安全)

public class Singleton_04 {
private static class SingletonHolder {
private static Singleton_04 instance = new Singleton_04();
}
private Singleton_04() {
}
public static Singleton_04 getInstance() {
return SingletonHolder.instance;
}
}

使用类的静态内部类实现的单例模式,既保证了线程安全有保证了懒加载,同时不会因为加 锁的方式耗费性能。 这主要是因为JVM虚拟机可以保证多线程并发访问的正确性,也就是⼀个类的构造方法在多线 程环境下可以被正确的加载。 此种方式也是非常推荐使用的⼀种单例模式。

双重校验锁-线程安全 uniqueInstance

只需要被实例化一次,之后就可以直接使用了。加锁操作只需要对实例化那部分 的代码进行,只有当 uniqueInstance 没有被实例化时,才需要进行加锁。 双重校验锁先判断 uniqueInstance 是否已经被实例化,如果没有被实例化,那么才对实例化语句 进行加锁。

public class Singleton {
    private volatile static Singleton uniqueInstance;
    private Singleton() {
    }
    public static Singleton getUniqueInstance() {
        if (uniqueInstance == null) {
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

枚举类型实现

单例模式最佳实践,实现简单,并且能面对复杂序列化或者反射攻击时,防止实例化多次。

public enum Singleton {
    uniqueInstance;
}

优点:线程安全,自由串行化,单一实例

缺点:存在继承场景下是不能使用的

考虑以下单例模式的实现,该 Singleton 在每次序列化的时候都会创建一个新的实例,为了保证只 创建一个实例,必须声明所有字段都是 transient,并且提供一个 readResolve() 方法。

public class Singleton implements Serializable {
    private static Singleton uniqueInstance;
    private Singleton() {
    }
    public static synchronized Singleton getUniqueInstance() {
        if (uniqueInstance == null) {
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}

如果不用枚举类型实现单例模式,会出现反射攻击,因为通过setAccessible() 方法可以将私有构造 函数的访问权限从private修改成public,然后调用构造函数从而实例化对象,如果要防止这样的攻击, 需要在构造函数中添加实例化第二个对象的代码 从上面的讨论可以看出,解决序列化和反射攻击很麻烦,而枚举实现不会出现这两种问题,所以说 枚举实现单例模式是最佳实践。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值