破坏单例模式--存在的问题---问题的解决

news2025/4/8 20:09:52

目录

破坏单例模式--存在的问题---问题的解决

 问题演示

破坏单例模式:

序列化

反射

序列化反序列化:

代码:

运行结果:

反射  

代码:

运行结果:

问题的解决

序列化、反序列方式破坏单例模式的解决方法

代码: 

运行结果:

反射方式破解单例的解决方法

代码:

运行结果:

JDK源码解析-Runtime类

Runtime类就是使用的单例设计模式。


破坏单例模式--存在的问题---问题的解决

  •  问题演示

    • 破坏单例模式:

使上面定义的单例类(Singleton)可以创建多个对象,枚举方式除外。有两种方式,分别是:

  • 序列化

  • 反射

  • 序列化反序列化:

    • 代码:

Singleton类:

package com.dong.demo;

import java.io.Serializable;

public class Singleton implements Serializable {
    public Singleton() {
    }
    private static class  singletonHolder{
        private static final Singleton INSTANCE=new Singleton();
    }
    public static Singleton getInstance(){
        return singletonHolder.INSTANCE;
    }
}

Client类:

package com.dong.demo;

import java.io.*;

public class Client {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
       // writeObject2File();
        readObject1File();
        readObject1File();
    }

    //此文件读取数据(对象)
    public static void readObject1File() throws IOException, ClassNotFoundException {
        //创建文件输出流对象
        ObjectInputStream ois=new ObjectInputStream(new FileInputStream("D:\\蓝桥杯\\设计模式作业\\530\\a.txt"));
        //读取对象
        Singleton singleton = (Singleton) ois.readObject();

        System.out.println(singleton);

        //释放资源
        ois.close();
    }

    //从文件中写数据(对象)
    public static void writeObject2File() throws IOException {
        //获取Singleton对象
        Singleton singleton=Singleton.getInstance();
        //创建对象输出流对象
        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("D:\\蓝桥杯\\设计模式作业\\530\\a.txt"));
        //写对象
        oos.writeObject(singleton);
        //释放资源
        oos.close();
    }
}
  • 运行结果:

两对象不一样,因此,序列化破坏了单例模式。

表明序列化和反序列化已经破坏了单例设计模式。

  • 序列化会破坏单例模式的原因是在反序列化的过程中,会通过构造器重新创建一个新的对象。反序列化的过程是将二进制数据解码为Java对象,这个过程会创建一个新的对象,并不会保留旧对象的状态。因此,如果使用序列化和反序列化来实现单例模式,就可能破坏单例模式的唯一性。
  • 在上述代码中,每次读取对象时都会创建一个新对象并返回,与最初创建的对象并不相同。具体原因是:在Singleton类中定义了私有的构造方法,使得无法在外部调用构造方法创建对象,而在内部通过静态内部类singletonHolder的方式创建Singleton的唯一实例,并将该实例赋值给静态final变量INSTANCE。但是,当通过反序列化的方式创建对象时,虚拟机会自动调用Singleton类的无参构造方法创建一个新的对象,从而导致创建了多个不同的实例,打破了Singleton模式的设计初衷。

  • 反射  

代码:

Singleton类:

package com.dong.demo002;

import java.io.Serializable;

public class Singleton implements Serializable {
    public Singleton() {
    }
    private static class  singletonHolder{
        private static final Singleton INSTANCE=new Singleton();
    }
    public static Singleton getInstance(){
        return singletonHolder.INSTANCE;
    }
}

Client类:  

package com.dong.demo002;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 反射破坏单例模式
 */
public class Client {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
       //Singleton.class.getDeclaredConstructor().setAccessible(true);

        //获取Singleton的字节码对象
        Class singletonClass = Singleton.class;
        //获取无参构造方法对象
        Constructor cons = singletonClass.getDeclaredConstructor();
        //取消范文检查
        cons.setAccessible(true);
        //创建Singleton对象
        Singleton s1 = (Singleton) cons.newInstance();
        Singleton s2= (Singleton) cons.newInstance();

        System.out.println(s1==s2);
    }
}

运行结果:

上面代码运行结果是false,表明反射已经破坏了单例设计模式  

注意:枚举方式不会出现这两个问题。

问题的解决

  • 序列化、反序列方式破坏单例模式的解决方法

    • 在Singleton类中添加readResolve()方法,在反序列化时被反射调用,如果定义了这个方法,就返回这个方法的值,如果没有定义,则返回新new出来的对象。

代码: 

Singleton类:

package com.dong.demo001;

import java.io.Serializable;

public class Singleton implements Serializable {
    public Singleton() {
    }
    private static class  singletonHolder{
        private static final Singleton INSTANCE=new Singleton();
    }
    public static Singleton getInstance(){
        return singletonHolder.INSTANCE;
    }

    public  Object readResolve(){
        return singletonHolder.INSTANCE;
    }
}

Client类:【不变】

package com.dong.demo001;

import java.io.*;

public class Client {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
       // writeObject2File();
        readObject1File();
        readObject1File();
    }

    //此文件读取数据(对象)
    public static void readObject1File() throws IOException, ClassNotFoundException {
        //创建文件输出流对象
        ObjectInputStream ois=new ObjectInputStream(new FileInputStream("D:\\蓝桥杯\\设计模式作业\\530\\a.txt"));
        //读取对象
        Singleton singleton = (Singleton) ois.readObject();

        System.out.println(singleton);

        //释放资源
        ois.close();
    }

    //从文件中写数据(对象)
    public static void writeObject2File() throws IOException {
        //获取Singleton对象
        Singleton singleton=Singleton.getInstance();
        //创建对象输出流对象
        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("D:\\蓝桥杯\\设计模式作业\\530\\a.txt"));
        //写对象
        oos.writeObject(singleton);
        //释放资源
        oos.close();
    }
}

运行结果:

源码解析:

ObjectInputStream类

public final Object readObject() throws IOException, ClassNotFoundException{
    ...
    // if nested read, passHandle contains handle of enclosing object
    int outerHandle = passHandle;
    try {
        Object obj = readObject0(false);//重点查看readObject0方法
    .....
}
    
private Object readObject0(boolean unshared) throws IOException {
	...
    try {
		switch (tc) {
			...
			case TC_OBJECT:
				return checkResolve(readOrdinaryObject(unshared));//重点查看readOrdinaryObject方法
			...
        }
    } finally {
        depth--;
        bin.setBlockDataMode(oldMode);
    }    
}
    
private Object readOrdinaryObject(boolean unshared) throws IOException {
	...
	//isInstantiable 返回true,执行 desc.newInstance(),通过反射创建新的单例类,
    obj = desc.isInstantiable() ? desc.newInstance() : null; 
    ...
    // 在Singleton类中添加 readResolve 方法后 desc.hasReadResolveMethod() 方法执行结果为true
    if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) {
    	// 通过反射调用 Singleton 类中的 readResolve 方法,将返回值赋值给rep变量
    	// 这样多次调用ObjectInputStream类中的readObject方法,继而就会调用我们定义的readResolve方法,所以返回的是同一个对象。
    	Object rep = desc.invokeReadResolve(obj);
     	...
    }
    return obj;
}
  • 反射方式破解单例的解决方法

代码:

package com.dong.demo002;

import java.io.Serializable;

public class Singleton implements Serializable {
    private static boolean flag=false;
    public Singleton() {
        synchronized (Singleton.class) {
            if (flag) {
                throw new RuntimeException("不能创建多个对象!!!!!!");
            }
            flag = true;
        }
    }
    private static class  singletonHolder{
        private static final Singleton INSTANCE=new Singleton();
    }
    public static Singleton getInstance(){
        return singletonHolder.INSTANCE;
    }
}
package com.dong.demo002;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 反射破坏单例模式
 */
public class Client {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
       //Singleton.class.getDeclaredConstructor().setAccessible(true);

        //获取Singleton的字节码对象
        Class singletonClass = Singleton.class;
        //获取无参构造方法对象
        Constructor cons = singletonClass.getDeclaredConstructor();
        //取消范文检查
        cons.setAccessible(true);
        //创建Singleton对象
        Singleton s1 = (Singleton) cons.newInstance();
        Singleton s2= (Singleton) cons.newInstance();

        System.out.println(s1==s2);
    }
}

运行结果:

 或:

反射方式破解单例的解决方法

public class Singleton {

    //私有构造方法
    private Singleton() {
        /*
           反射破解单例模式需要添加的代码
        */
        if(instance != null) {
            throw new RuntimeException();
        }
    }
    
    private static volatile Singleton instance;

    //对外提供静态方法获取该对象
    public static Singleton getInstance() {

        if(instance != null) {
            return instance;
        }

        synchronized (Singleton.class) {
            if(instance != null) {
                return instance;
            }
            instance = new Singleton();
            return instance;
        }
    }
}

说明:

这种方式比较好理解。当通过反射方式调用构造方法进行创建创建时,直接抛异常。不运行此中操作。


JDK源码解析-Runtime类

Runtime类就是使用的单例设计模式。

  1. 通过源代码查看使用的是哪儿种单例模式

从上面源代码中可以看出Runtime类使用的是饿汉式(静态属性)方式来实现单例模式的。

2.使用Runtime类中的方法  

public class RuntimeDemo {
    public static void main(String[] args) throws IOException {
        //获取Runtime类对象
        Runtime runtime = Runtime.getRuntime();

        //返回 Java 虚拟机中的内存总量。
        System.out.println(runtime.totalMemory());
        //返回 Java 虚拟机试图使用的最大内存量。
        System.out.println(runtime.maxMemory());

        //创建一个新的进程执行指定的字符串命令,返回进程对象
        Process process = runtime.exec("ipconfig");
        //获取命令执行后的结果,通过输入流获取
        InputStream inputStream = process.getInputStream();
        byte[] arr = new byte[1024 * 1024* 100];
        int b = inputStream.read(arr);
        System.out.println(new String(arr,0,b,"gbk"));
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Elasticsearch 8.X 性能优化参考 —— 筑梦之路

Elasticsearch 是实现用户无缝搜索体验的关键工具。它通过提供快速、准确和相关的搜索结果,彻底改变了用户与应用程序的互动方式。然而,要确保 Elasticsearch 部署达到最佳性能,就必须关注关键指标,并对诸如索引、缓存、查询、搜索…

monorepo 项目 Unable to resolve path to module ‘xxx‘.eslintimport/no-unresolved

同事问了一个问题,他现在参加了一个项目,这个项目是个monorepo 项目。 也就是多包管理、前后端一起都是js 写的。 问题 后端express 项目里配置的路径别名,eslint 不认识,报错。 Unable to resolve path to module /app/Prisma.e…

Phaser笔记-鼠标点击获取坐标键盘控制sprite

代码如下: import phaser;class PlayGame extends Phaser.Scene {constructor() {super("PlayGame");}preload() {this.load.spritesheet(run, assets/_Run.png, {frameWidth: 120, frameHeight: 80});this.load.spritesheet(idle, assets/_Idle.png, {f…

GEE:对Sentinel-2遥感影像进行处理,水体提取与可视化

作者:CSDN _养乐多_ 本文介绍了通过Google Earth Engine平台,并使用哨兵数据提取水体掩膜的方法和代码。通过裁剪和去除云等处理步骤,最终得到具有水体掩膜的影像,并进行可视化和导出。这种方法基于归一化水体指数(N…

MIT 6.S081 (BOOK-RISCV-REV1)教材第一章内容

MIT 6.S081 教材第一章内容 引言第一章 操作系统接口进程和内存I/O和文件描述符管道文件系统真实世界 引言 MIT 6.S081 2020 操作系统 本文为MIT 6.S081课程第一章教材内容翻译加整理。 本课程前置知识主要涉及: C语言(建议阅读C程序语言设计—第二版)RISK-V汇编推荐阅读: 程…

JavaScript 数据透视表 DHTMLX Pivot Crack

DHTMLX Pivot JavaScript 数据透视表 - 强大的数据汇总和报告 使用我们的高速 JavaScript/HTML5 Pivot 组件可视化您的复杂数据,从而提高您的商业智能。 它可以帮助您以方便的方式汇总大型数据集。 主要特征 纯 JavaScript 库,可轻松与任何服务器端集成…

Linux安装Mysql8,过程详细(离线安装mysql)

因公司需要,需要在服务器上安装一个mysql,但是机器没有外网权限,所以记录一下安装过程,供大家参考。 官网下载安装包 地址:https://dev.mysql.com/downloads/ 如图示,选择MySQL Community Server 如图示&a…

git新手将网页设计代码提交到github上

以下是将代码提交到Github上的一些步骤。如果中途遇到问题或不会的需要我帮忙,可以文章底部联系我。 1. 创建Github账户 首先,您需要在Github上注册一个账户。 如果您已经有账户了,请跳过这一步。 2. 创建一个新的repository 在您的Githu…

格式化数字的实用命令:numfmt

在 Linux 系统中,numfmt 是一个用于格式化数字的实用工具。它可以将数字转换为不同的表示方式,如十进制、二进制、字节单位等。本文将详细介绍 numfmt 命令的使用方法,并提供一些适合初学者的示例。 Numfmt 命令语法 numfmt 命令的基本语法如…

搭建stm32电机控制代码框架(四)——单路PWM生成

STM32中单路PWM的生成一般是基于某一个通用定时器,本次小实验选取TIM2通用定时器,选择PA5作为PWM端口输出。配置步骤如下: 第一步:配置外部晶振与基本的时钟,如下图所示。 (1)选择外部晶振 &a…

关于java k8s容器环境中的jvm配置与优化

1. 前言 环境 版本 备注 k8s v1.22 配置cpu/mem limit、健康/就绪检查 openjdk 8 openjdk version "1.8.0_342" k8s容器化(docker)环境更好的解决了 java app 运行环境的封装问题。但存在着一些限制,比如 Java 并不能发现…

流马平台连接数据库

新增数据库配置 在环境管理处对于环境下新增数据库配置 编辑SQL 在用例管理-步骤编辑-逻辑控件处添加前置SQL,编辑SQL 选择数据库名称,查询语句,填写要保存的变量名,并在下方填写查询语句 示例从user表中取出username和passwo…

Semi-supervised Learning(半监督学习)

目录 Introduction Why semi-supervised learning help? Semi-supervised Learning for Generative Model Supervised Generative Model Semi-supervised Generative Model Low-density Separation Assumption Self Training Entropy-based Regularization(基…

docker启动MYSQL8并挂载数据目录

1.创建需要docker挂载的数据目录 mkdir -p /datah/mydata/mysql/conf mkdir -p /datah/mydata/mysql/data mkdir -p /datah/mydata/mysql/log mkdir -p /datah/mydata/mysql/mysql-files 2. 在目录下面 /datah/mydata/mysql/conf 创建配置文件 my.cnf [client] default-charact…

VH6501使用

目录 1.VH6501接口介绍 2.使用场景 2.1当VH6501作为硬件接口卡作通信/监测使用时,使用CH1的任意接口与总线连接即可 2.2针对单节点干扰时,使用CH1的任意接口与被测节点连接即可 2.3针对多节点干扰时,需要通过CH1的两个接口将VH6501串联到…

C++ ---- 模板

目录 泛型编程 函数模板 函数模板语法 模板使用 函数模板原理 函数模板的实例化 隐式实例化 显示实例化 模板参数的匹配原则 类模板 类模板的定义语法 类模板的实例化 非类型模板参数 类模板的特化 全特化 半特化(部分特化) 两个参数偏…

排查Javascript内存泄漏案例(一)

Chrome DevTools里的Performance面板和Memory面板可以用来定位内存问题。 如何判断应用发生内存泄漏? 为了证明螃蟹的听觉在腿上,一个专家捉了只螃蟹并冲它大吼,螃蟹很快就跑了。然后捉回来再冲它吼,螃蟹又跑了。最后专家把螃蟹的…

浅谈 ChatGPT —— 现代巴别塔

theme: nico 一、用 ChatGPT 一搜就到你这了 ChatGPT 在去年 11 月发布以后,上线 5 天后就有了 100 万用户,上线两个月后已有上亿用户,可谓一炮而红。起初我对 ChatGPT 是没有什么感知的,我单纯认为人工智能还没有发展到完全超越人…

数字经济等相关概念与官方文档

一、数字经济 数字经济,作为一个内涵比较宽泛的概念,凡是直接或间接利用数据来引导资源发挥作用,推动生产力发展的经济形态都可以纳入其范畴。在技术层面,包括大数据、云计算、物联网、区块链、人工智能、5G通信等新兴技术。在应…

美食菜谱类APP小程序开发功能有哪些?

想要开发出一款靠谱好用的美食菜谱APP小程序系统,需要具备哪些基本功能呢? 1、视频教学。对于美食的教学教学方法最直接受用的就是视频教学,用户浏览起来更加方便而且可以直接跟着视频操作,效果更佳。用户也可以自己拍摄制…