Java设计模式之单例模式以及如何防止通过反射破坏单例模式

单例模式

单例模式使用场景

​ 什么是单例模式?保障一个类只能有一个对象(实例)的代码开发模式就叫单例模式
​ 什么时候使用? 工具类!(一种做法,所有的方法都是static,还有一种单例模式让工具类只有一个实例) 某类工厂(SqlSessionFactory)

实现方式

1. 饿汉

/**
 * 饿汉模式(迫切加载)
 */
public class Singleton01 {

    //构造私有化
    private Singleton01(){

    }

    //2 创建一个private对象
    private static final Singleton01 INSTANCE = new Singleton01();//这个地方就体现饿汉,一来就创建对象给他

    //3 提供一个static方法来获取你的这个单实例对象
    public static Singleton01 newInstance(){
        return INSTANCE;
    }
}

测试代码

public class MyTest {
    public static void main(String[] args) {
        Singleton01 singleton01 = Singleton01.newInstance();
        Singleton01 singleton02 = Singleton01.newInstance();
        System.out.println(singleton01.equals(singleton02));
    }
}

输出为true说明两个对象是相同的

2. 懒汉(懒汉太懒了,要用的时候才能创建。)

/**
 * 懒汉模式(懒加载) 单线程
 */
public class Singleton02 {

    private Singleton02() {
    }

    //2 创建一个private对象
    private static Singleton02 INSTANCE;

    //3 提供一个static方法来获取你的这个单实例对象 只适合在单线程中
    public static Singleton02 newInstance() {
    	if(INSTANCE == null){
    		INSTANCE = new Singleton02()
    	}
    	return INSTANCE;
 	}
}

测试代码

public class MyTest {
    public static void main(String[] args) {
        Singleton02 singleton03 = Singleton02.newInstance();
        Singleton02 singleton04 = Singleton04.newInstance();
        System.out.println(singleton03.equals(singleton04));
    }
}

输出为true说明两个对象是相同的
懒汉模式 只要不调用newInstance()方法 INSTANCE就一直为空 只用调用了才会创建

测试代码(如果多线程 就不是单例了)

public class MyTest {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> Singleton02.newInstance()).start();
        }
    }
}

运行结果
在这里插入图片描述

说明有不同的线程都调用到了构造方法
解决方法:加锁
1.方法上加锁
public static synchronized Singleton02 newInstance()
缺点:锁住整个方法 里面还有业务逻辑 效率降低很多
2.synchronized代码块

/**
 * 懒汉模式(懒加载)
 */
public class Singleton02 {

    private Singleton02() {
    }

    //2 创建一个private对象
    private static Singleton02 INSTANCE;

    //3 提供一个static方法来获取你的这个单实例对象
    //方案1:方法锁,里面还有业务逻辑
    //public static synchronized  Singleton02 newInstance(){
    public static Singleton02 newInstance() {
        //方案2:锁代码块
        //synchronized (Singleton02.class){ // 100个线程哪怕有一个已经创建了也要排队
        if (INSTANCE == null) {
            //多线程来了? T1 T2
            synchronized (Singleton02.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton02();
                }
            }
        }
        //}

        //此处有10W行代码....
        return INSTANCE;
    }

    //普通方法
    public String getConnection() {
        return "I am connection!";
    }
}

在这里插入图片描述

只会有一个线程调用到构造方法

写到这里是不是觉得单例模式已经可以了?
但是这个代码也不是绝对安全的,不绝对是单例 利用反射去创建类的对象可以将单例进行破坏 写个测试代码

public class MyTest {
public static void main(String[] args) throws Exception{
        //正常获取
        Singleton02 instance = Singleton02.newInstance();
        //通过反射获取
        Class<? extends Singleton02> aClass = instance.getClass();
        //注意:构造方法是私有的
        Constructor<? extends Singleton02> declaredConstructor = aClass.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        Singleton02 instance02 = declaredConstructor.newInstance();
        System.out.println(instance);
        System.out.println(instance02);
    }
}

在这里插入图片描述

生成了两个对象,说明已经通过反射将单例破坏掉了

修改代码

/**
 * 懒汉模式(懒加载)
 */
public class Singleton02 {

    private Singleton02() {
        synchronized (Singleton02.class) {
            if (INSTANCE != null){
                throw new RuntimeException("防止反射破坏单例");
            }
        }
    }

    //2 创建一个private对象
    private static Singleton02 INSTANCE;

    //3 提供一个static方法来获取你的这个单实例对象
    //方案1:方法锁,里面还有业务逻辑
    //public static synchronized  Singleton02 newInstance(){
    public static Singleton02 newInstance() {
        //方案2:锁代码块
        //synchronized (Singleton02.class){ // 100个线程哪怕有一个已经创建了也要排队
        if (INSTANCE == null) {
            //多线程来了? T1 T2
            synchronized (Singleton02.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton02();
                }
            }
        }
        //}

        //此处有10W行代码....
        return INSTANCE;
    }

    //普通方法
    public String getConnection() {
        return "I am connection!";
    }
}

在这里插入图片描述

那到这一步反射就不能搞破坏了吗?
答案是可以的 为什么?
因为第一次创建的时候使用的是正常的方式肯定会调用构造方法
将第一个打印放到反射创建之前就会打印出第一个的对象 第二次通过反射再拿一个对象就不行 如果我两次都使用反射来获取对象

public class MyTest {
public static void main(String[] args) throws Exception{
Class<? extends Singleton02> aClass = Singleton02.class;
        //注意:构造方法是私有的
        Constructor<? extends Singleton02> declaredConstructor = aClass.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        Singleton02 instance01 = declaredConstructor.newInstance();
        Singleton02 instance02 = declaredConstructor.newInstance();
        System.out.println(instance01);
        System.out.println(instance02);
	}
}

在这里插入图片描述

ok 单例又被破坏
还有没有其他方法?
不管是反射还是正常都会调用构造方法 那就先搞一个字段flag来进行校验

package org.jhy._01singleton;

/**
 * 懒汉模式(懒加载)
 */
public class Singleton02 {

    private static Boolean flag = false;

    private Singleton02() {
//        synchronized (Singleton02.class) {
//            if (INSTANCE != null){
//                throw new RuntimeException("防止反射破坏单例");
//            }
//        }
        if (flag == false) {
            flag = true;
        } else {
            throw new RuntimeException("防止反射破坏单例");
        }
    }

    //2 创建一个private对象
    private static Singleton02 INSTANCE;

    //3 提供一个static方法来获取你的这个单实例对象
    //方案1:方法锁,里面还有业务逻辑
    //public static synchronized  Singleton02 newInstance(){
    public static Singleton02 newInstance() {
        //方案2:锁代码块
        //synchronized (Singleton02.class){ // 100个线程哪怕有一个已经创建了也要排队
        if (INSTANCE == null) {
            //多线程来了? T1 T2
            synchronized (Singleton02.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton02();
                }
            }
        }
        //}

        //此处有10W行代码....
        return INSTANCE;
    }

    //普通方法
    public String getConnection() {
        return "I am connection!";
    }
}

public class MyTest {
public static void main(String[] args) throws Exception{
//通过反射获取
        Class<? extends Singleton02> aClass = Singleton02.class;
        //注意:构造方法是私有的
        Constructor<? extends Singleton02> declaredConstructor = aClass.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        Singleton02 instance01 = declaredConstructor.newInstance();
        //获取字段名
        Field flag = aClass.getDeclaredField("flag");
        flag.setAccessible(true);
        //获取之后复位 认为又是第一次
        flag.set(instance01,false);
        Singleton02 instance02 = declaredConstructor.newInstance();
        System.out.println(instance01);
        System.out.println(instance02);
	}
}

在这里插入图片描述

ok,单例又被破坏了
那么反射真的就无所不能了吗???

让我们来看看newInstance()这个方法的源码
在这里插入图片描述

不能通过反射创建枚举的对象,所以用枚举就能防止反射破坏单例

public enum Singleton03 {

    INSTANCE;

    public void testMethod(){
        System.out.println("执行了单例类的方法");
    }
}

// Test.java
class Test {
    public static void main(String[] args) {
        //演示如何使用枚举写法的单例类
        Singleton03.INSTANCE.testMethod();
        System.out.println(Singleton03.INSTANCE);

        Singleton03 instance01 = Singleton03.INSTANCE;
        Singleton03 instance02 = Singleton03.INSTANCE;
        System.out.println(instance01.equals(instance02));
    }
}

结果显然为true 而且枚举类里面就不能用反射的方法 枚举里面只有一个实例那就是单例

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/263458.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Notepad++如果同一个窗口,打开了很多文件,需另开一个窗口

如果一个窗口已经这么多了&#xff0c;需要另外再一个 这里打开

效果图云渲染是什么意思?如何渲染出照片级别的效果图?

​在当前的建筑规划、室内装修以及电影视效制作等行业内&#xff0c;制作高质量的效果图起着至关重要的作用&#xff0c;因为它能够给予观众或客户极为逼真和吸引人的视觉体验。在此篇文章中&#xff0c;我们将深入了解什么是云端效果图渲染&#xff0c;并探讨如何运用Renderbu…

C#中如何稳定精确地每隔5ms执行某个函数?

C#中如何稳定精确地每隔5ms执行某个函数&#xff1f; 在开始前我有一些资料&#xff0c;是我根据自己从业十年经验&#xff0c;熬夜搞了几个通宵&#xff0c;精心整理了一份「C#的资料从专业入门到高级教程工具包」&#xff0c;点个关注&#xff0c;全部无偿共享给大家&#xf…

技术丨 浅谈以太网系统级刷写测试

在研发阶段&#xff0c;我们一般将ECU直连至刷写上位机进行软件更新&#xff0c;但在整车环境中&#xff0c;各ECU之间通过线束连接在一起&#xff0c;如果此时想对某个单独的样件进行软件更新&#xff0c;把零件拆下来再接到上位机上进行软件更新是不现实的。 作为测试工程师…

QT编写应用的界面自适应分辨率的解决方案

博主在工作机上完成QT软件开发&#xff08;控件大小与字体大小比例正常&#xff09;&#xff0c;部署到客户机后&#xff0c;发现控件大小与字体大小比例失调&#xff0c;具体表现为控件装不下字体&#xff0c;即字体显示不全&#xff0c;推测是软件不能自适应分辨率导致的。 …

泰克TDS3054B示波器 2 或 4 条通道

产品特点 500 MHz 、300 MHz 、100 MHz 三种带宽 取样速率高达 5 GS/s TDS3054B 数字示波器带宽500MHZ TDS3054B TDS3054B 数字示波器带宽500MHZ TDS3054B 2 或 4 条通道 全 VGA 彩色 LCD 显示 25 种自动测量功能 9 位垂直分辨率 TDS3054B 数字示波器带宽500MHZ TDS3054B TDS…

【图神经网络 · 科研笔记5】异构信息网络,利用注意力选择元路径;利用进化邻域和社群实现自监督动态图嵌入,交叉监督对比学习;近期科研思维导图小汇总;

记录部分科研文献阅读相关内容【划重点】,主题“图神经网络”,仅学习使用。 🎯作者主页: 追光者♂🔥 🌸个人简介: 📝[1] CSDN 博客专家📝 🏆[2] 人工智能领域优质创作者🏆 🌟[3] 2023年城市之星领跑者TOP1(哈尔滨)🌿 🌿[4] 2022年度…

【深入探讨人工智能】网络研讨系列介绍

笔者于2023年7月27日受邀代表开源社参加 Linux 基金会和 Open Source Initiative&#xff08;OSI - 开源促进会&#xff09;在日内瓦联合举办的 Open Source Congress 2023&#xff08;全球开源峰会&#xff09;。 国内同时有开放原子开源基金会孙文龙理事长率团参加。短短一天…

15个热门的开源数据可视化项目

数据可视化(即 BI仪表盘)是图形表示的数据。它涉及产生将表示的数据之间的关系传达给图像查看者的图像。这种通信是通过在可视化过程中使用图形标记和数据值之间的系统映射来实现的。该映射建立了如何在视觉上表示数据值,确定图形标记的属性(例如大小或颜色)如何以及在多大程…

Zookeeper集群搭建,四字命令监控,Leader选举原理以及数据如何同步

Java学习面试指南&#xff1a;https://javaxiaobear.cn 1、集群角色 Leader&#xff1a; 领导者。 事务请求&#xff08;写操作&#xff09;的唯一调度者和处理者&#xff0c;保证集群事务处理的顺序性&#xff1b;集群内部各个服务器的调度者。对于create、setData、delete…

七星创客系统模式:轻松打造千万会员体系

在当今快速变化的商业环境中&#xff0c;企业需要一种全新的模式来应对挑战&#xff0c;而七星创客模式正是这样一种具有吸引力的解决方案。七星创客模式是一种创新的商业模式&#xff0c;它通过将传统销售模式与现代科技手段相结合&#xff0c;为企业带来更多的机遇和可能性。…

教你如何完美录制电脑端游戏视频

在如今的互联网时代&#xff0c;游戏视频已成为了一个非常受欢迎的内容形式。无论是玩家和内容创作者想要分享自己的游戏技巧&#xff0c;还是游戏制作公司想要宣传自己的新作品&#xff0c;录制游戏视频已经成为了一种流行的趋势。 那么&#xff0c;如何在电脑上录制游戏视频…

JS逆向基础

JS逆向基础 一、什么是JS逆向&#xff1f;二、接口抓包三、逆向分析 一、什么是JS逆向&#xff1f; 我们在网站进行账号登录的时候对网页源进行抓包就会发现我们输入的密码在后台会显示为一串由字母或数字等符号&#xff0c;这就是经过加密呈现的一段加密文字&#xff0c;而分…

搭建接口自动化测试框架python+requests+pytest

安装python&#xff08;最好是比较新比较稳定的版本&#xff09;&#xff0c;然后是python的解释器或者叫编译器pycharm安装后新建一个项目&#xff0c;以此项目为基础&#xff0c;安装依赖搭建框架。打开pycharm&#xff0c;点击左上角的File->New project->弹出如下界面…

业务逻辑漏洞有哪些?漏洞攻击防御及代码示例

文章目录 简介危害成因攻击防御代码示例1. 未经验证的重要操作2. 认证绕过3. 逻辑时间窗口漏洞4. 负载测试漏洞 修复 业务逻辑漏洞是指软件或系统的逻辑设计上的缺陷&#xff0c;这些缺陷可能被攻击者利用&#xff0c;从而导致意料之外的行为。下面是对业务逻辑漏洞的简介、危害…

在线客服系统性价比比较:哪家更适合您的需求?

众多的在线客服系统中&#xff0c;哪家性价比更高&#xff1f;这大概是很多企业要考虑和考量的问题。如果您是一名公司采购人员&#xff0c;正在寻找一款功能全面、价格合适的在线客服系统&#xff0c;那么这篇文章或许能为您提供一些参考价值。 Zoho Desk工单管理系统&#x…

微信小程序promise封装

一. 在utils文件夹内创建一个request.js 写以下封装的 wx.request() 方法 const baseURL https:// 域名 ; //公用总路径地址 export const request (params) > { //暴露出去一个函数&#xff0c;并且接收一个外部传入的参数let dataObj params.data || {}; //…

软文营销的重要性,媒介盒子为你解答

信息茧房环立且用户注意力稀缺的时代下&#xff0c;品牌方唯有在内容上精耕细作才能吸引用户。软文营销能够帮助公司吸引流量&#xff0c;建立与消费者的信任联系&#xff0c;同时增加转化并产生更多的业务。接下来媒介盒子就和大家聊聊&#xff1a;软文营销的重要性。 一、 可…

【论文阅读】FreeU: Free Lunch in Diffusion U-Net

FreeU: 无需训练直接提升扩散模型生成效果。 paper&#xff1a;https://arxiv.org/abs/2309.11497 code&#xff1a;GitHub - ChenyangSi/FreeU: FreeU: Free Lunch in Diffusion U-Net 1. 介绍 贡献&#xff1a; •研究并揭示了U-Net架构在扩散模型中去噪的潜力&#xff0…