Java单例模式

一、单例模式

单例模式是一种设计模式,用于确保一个类只有一个实例,并提供全局访问点以访问该实例。这在需要控制某些资源(如数据库连接、线程池、缓存等)的访问时非常有用。

1.使用场景:

1.当一个类的实例需要被多个客户端共享访问时。
2.当系统只需要一个公共的访问点来管理全局配置或共享资源时。
3.当需要限制某个类只能有一个实例,以节省系统资源。

2.实现要素

1.私有化构造方法(Private Constructor): 通过将类的构造方法设为私有,可以防止其他类直接通过 new 关键字来实例化该类。
2.静态方法或者静态变量(Static Method or Variable): 单例类通常会提供一个静态方法来获取该类的唯一实例。此外,通常还会有一个静态变量来保存该类的实例。
3.延迟实例化(Lazy Initialization): 单例类在首次被使用时才会被实例化,而不是在类加载时就创建实例。这种方式称为懒汉式单例模式。
4.线程安全性(Thread Safety): 如果单例类在多线程环境中被使用,需要确保它是线程安全的。否则,可能会导致多个线程同时创建多个实例,违反了单例模式的初衷。
5.防止反射攻击(Prevent Reflection Attack): 可以通过在构造方法中添加逻辑,防止通过反射机制来绕过单例模式的限制,强制创建多个实例。

3.饿汉式

启动时随着类的加载会构造一个对象,如果不使用就会出现资源浪费,如果加载内容复杂也会出现加载缓慢,影响服务进程。

public class SingletonHungry {

    private SingletonHungry(){

    }
    //随着类的加载而加载
    public static SingletonHungry getSingletonHungry(){
        return new SingletonHungry();
    }
}

4.懒汉式

创建静态对象,静态方法判空,使用时创建对象。但是会出现多线程问题。

public class SingletonLazy {
    //私有构造方法: 单例类的构造方法必须私有化,以防止外部类直接实例化该类。
    //静态变量: 单例类通常会包含一个静态变量来保存该类的唯一实例。
    //静态方法: 提供一个静态方法用于获取该类的实例,该方法负责判断实例是否已经被创建,如果没有则创建新实例并返回。
    private SingletonLazy(){
        System.out.println(Thread.currentThread().getName());
    }
    private static SingletonLazy singletonLazy;
    private static SingletonLazy getSingletonLazy(){
        if (singletonLazy == null) {
            singletonLazy = new SingletonLazy();
        }
        return singletonLazy;
    }

    public static void main(String[] args) {
        for (int i=0;i<10;i++){
            new Thread(()->{
                SingletonLazy.getSingletonLazy();
            },"线程" + i).start();
        }
    }
}

解决线程问题

private static volatile SingletonLazy singletonLazy;
//线程问题解决方案:
//1.在静态方法上加synchronized关键字
//2.lock锁
//3.双重锁,下面就是双重锁
private static SingletonLazy getSingletonLazy(){
    if (singletonLazy == null){
    	//只会有一个对象
        synchronized (SingletonLazy.class) {
            if (singletonLazy == null) {
                singletonLazy = new SingletonLazy();//不是一个原子性操作
                /**
                 * 1.分配内存
                 * 2.执行构造方法,初始化对象
                 * 3.把对象指向所在的空间
                 * 会出现指令重排 所以需要加volatile
                 */
            }
        }
    }
    return singletonLazy;
}

5.静态内部类

public class SingletonStatic {

    private SingletonStatic(){

    }
    public static SingletonStatic getSingletonStatic(){
        return InnerClass.singletonStatic;
    }

    public static class InnerClass{
        private static final SingletonStatic singletonStatic = new SingletonStatic();
    }
}

6.反射破解单例模式

第一次破坏: 通过SingletonReflex singletonReflex3 = SingletonReflex.getSingletonReflex();获取对象之后反射再次获取对应对象
解决方案: 在构造器内加锁

public class SingletonReflex {
    //第一次破坏:通过SingletonReflex singletonReflex3 = SingletonReflex.getSingletonReflex();获取对象之后反射
    //获取无参构造器
    //Constructor<SingletonReflex> declaredConstructor = SingletonReflex.class.getDeclaredConstructor(null);
    //无视private
    // declaredConstructor.setAccessible(true);
    //SingletonReflex singletonReflex1 = declaredConstructor.newInstance();
    //解决方案 在构造器加一个锁
    //    synchronized (SingletonReflex.class){
    //        if (singletonReflex != null){
    //            throw new RuntimeException("不要用反射破坏单例模式");
    //        }
    //    }
    private SingletonReflex() {
        synchronized (SingletonReflex.class){
            if (singletonReflex != null){
                throw new RuntimeException("不要用反射破坏单例模式");
            }
        }
    }
    public volatile static SingletonReflex singletonReflex;
    public static SingletonReflex getSingletonReflex() {
        if (singletonReflex == null) {
            synchronized (SingletonReflex.class) {
                if (singletonReflex == null) {
                    singletonReflex = new SingletonReflex();
                }
            }
        }
        return singletonReflex;
    }
    public static void main(String[] args) {
        try {
            SingletonReflex singletonReflex3 = SingletonReflex.getSingletonReflex();
            //获取无参构造器
            Constructor<SingletonReflex> declaredConstructor = SingletonReflex.class.getDeclaredConstructor(null);
            //无视private
            declaredConstructor.setAccessible(true);
            SingletonReflex singletonReflex1 = declaredConstructor.newInstance();
            SingletonReflex singletonReflex2 = declaredConstructor.newInstance();
            System.out.println(singletonReflex1);
            System.out.println(singletonReflex2);
            System.out.println(singletonReflex3);
        } catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
                 InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }
}

第二次破坏: 对象都是通过反射获取。
解决方案: 红绿灯模式,定义一个默认值在锁内判断

public class SingletonReflex2 {
    private static boolean mbb = false;
    private SingletonReflex2() {
        synchronized (SingletonReflex2.class){
            if (!mbb){
                mbb = true;
            }else {
                throw new RuntimeException("破坏异常");
            }
        }
    }
    public volatile static SingletonReflex2 singletonReflex;
    public static SingletonReflex2 getSingletonReflex() {
        if (singletonReflex == null) {
            synchronized (SingletonReflex2.class) {
                if (singletonReflex == null) {
                    singletonReflex = new SingletonReflex2();
                }
            }
        }
        return singletonReflex;
    }
    public static void main(String[] args) {
        try {
            //获取无参构造器
            Constructor<SingletonReflex2> declaredConstructor = SingletonReflex2.class.getDeclaredConstructor(null);
            //无视private
            declaredConstructor.setAccessible(true);
            SingletonReflex2 singletonReflex1 = declaredConstructor.newInstance();
            SingletonReflex2 singletonReflex2 = declaredConstructor.newInstance();
            System.out.println(singletonReflex1);
            System.out.println(singletonReflex2);
        } catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
                 InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }
}

第三次破坏 内部的公有元素也是可以修改的

Field mbb1 = SingletonReflex2.class.getDeclaredField("mbb");
mbb1.setAccessible(true);
mbb1.set("mbb",false);

解决方法: 反射无法破坏枚举单例

public enum EnumSingleton {
    INSTANCE;
    private EnumSingleton(){

    }
    private EnumSingleton getInstance(){
        return INSTANCE;
    }

    public static void main(String[] args) throws Exception{
        EnumSingleton instance = EnumSingleton.INSTANCE;
        Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class,int.class);
        declaredConstructor.setAccessible(true);
        EnumSingleton enumSingleton = declaredConstructor.newInstance();
        System.out.println(instance);
        System.out.println(enumSingleton);
    }
}

获取枚举内的信息

public enum UserAgreementEnum {
    //1
    ONE("", 1),
    //2
    TOW("", 2),
    //3
    THREE("", 3),
    FOUR("",4),
    FIVE("",5)
    ;
    private final int number;

    private final String desc;

    public String getDesc() {
        return desc;
    }

    public int getNumber() {
        return number;
    }

    UserAgreementEnum(String desc, int number) {
        this.desc = desc;
        this.number = number;
    }

    /**
     * 通过value取枚举
     * @param number
     * @return
     */
    public static UserAgreementEnum getEnum(int number){
        for (UserAgreementEnum enums : UserAgreementEnum.values()) {
            if (enums.getNumber() == (number)) {
                return enums;
            }
        }
        return null;
    }

    /**
     * 通过number取描述
     * @param number
     * @return
     */
    public static String getDescByValue(int number) {
        for (UserAgreementEnum enums : UserAgreementEnum.values()) {
            if (enums.getNumber() == number) {
                return enums.getDesc();
            }
        }
        return "";
    }

}

上述不足之处,望海涵

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值