序列化单例模式的实现————readResolve 源码解读 | Java Debug 笔记

简介: 序列化单例模式的实现————readResolve 源码解读在可序列化类中加上readResolve方法,就可以实现单例模式了!这是为什么呢?让我们一起看看源码中的奥秘吧!只有实现了序列化接口 Serializable ,才可以进行 序列化操作,测试代码class SingletonTest { */*** ** 序列化测试公共方法* *** *@param* *className* **/* private void testSerializable(String className) { if (className =

序列化单例模式的实现————readResolve 源码解读





在可序列化类中加上readResolve方法,就可以实现单例模式了!这是为什么呢?让我们一起看看源码中的奥秘吧!


只有实现了序列化接口 Serializable ,才可以进行 序列化操作,


测试代码


class SingletonTest {
  */***
   ** 序列化测试公共方法*
   *** *@param* *className*
   **/*
   private void testSerializable(String className) {
    if (className == null) {
      throw new RuntimeException("className不能为null");
    }
    Class<?> clazz = null;
    Object obj = null;
    try {
      clazz = Class.forName(className);
      Method method = clazz.getMethod("getInstance");
      obj = method.invoke(null);
    } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
      e.printStackTrace();
    }
    int lastIndexOf = className.lastIndexOf(".");
    String realName = className.substring(lastIndexOf + 1);
    String objName = realName + ".obj";
    Object s1 = null;
    Object s2 = obj;
    FileOutputStream fileOutputStream = null;
    try {
      fileOutputStream = new FileOutputStream(objName);
      ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
      objectOutputStream.writeObject(s2);
      objectOutputStream.flush();
      objectOutputStream.close();
      FileInputStream fileInputStream = new FileInputStream(objName);
      ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
      Object o = objectInputStream.readObject();
      s1 = o;
      objectInputStream.close();
      System.out.println(s1);
      System.out.println(s2);
      System.out.println(s1 == s2);
    } catch (IOException | ClassNotFoundException e) {
      e.printStackTrace();
    }
  }
  */***
   ** 序列化单例模式l*
   **/*
  @Test
  void testSerializableSingleton() {
    testSerializable("com.example.demo.singleton.SerializableSingleton");
  }
}
复制代码



序列化单例模式【测试】


先注释掉下面的代码


网络异常,图片无法展示
|


执行 testSerializableSingleton 方法,结果如下图,序列化和反序列化出来的不是同一个对象,违背了单例模式,也就是说,在这种情况下通过序列化模式可以破坏单例模式.


网络异常,图片无法展示
|


源码

readObject


接着我们来看看为什么要加上面注释掉的代码,


进入 Object o = objectInputStream.readObject();


网络异常,图片无法展示
|


进入上面红框中的方法 readObject0,它是readObject的底层实现方法,

在该方法中中找到下图


网络异常,图片无法展示
|


readOrdinaryObject


来到上图中的方法readOrdinaryObject,继续往下看


网络异常,图片无法展示
|


这里会去判断有没有这个无参构造器,有的话obj不为null,会执行下图中的代码


网络异常,图片无法展示
|


这里会去判断有没有这个 hasReadResolveMethod ,有的话会通过反射方法


invokeReadResolve去创建这个对象,最后将obj的引用地址指向当前创建的对象


rep,最后 return 出去。


invokeReadResolve


我们来看看invokeReadResolve做了什么


网络异常,图片无法展示
|


通过注释可知,它会去调用所表示的可序列化类的 readResolve 方法,在idea中通过


通过ctrl+鼠标左键点击readResolveMethod方法,选择getInheritableMethod


可以看到下图


网络异常,图片无法展示
|


网络异常,图片无法展示
|


了解到该方法是一个 参数argTypes为空,返回类型为 Object的函数,那他的修饰符(modifier)是什么呢?


网络异常,图片无法展示
|


可以知道该方法如果是 static 或者 ABSTRACT 就直接返回 null


抽象方法没有方法体,它需要非抽象子类去实现它,就直接返回null了,


那为什么static也返回null呢!希望看到该博文的大神们帮忙答疑!!🐖谢谢!!


这个我想了好久也想不出答案来。。 直到我重新看到它的方法名


getInheritableMethod:获取可以继承的方法🙃


猜测:


  1. static方法修饰后它就属于类了,无法被重写,也无法在使用时动态绑定了,如:class A 实现了序列化接口,并定义了readResolve 方法,class B 和 C 都继承了 A ,此时反序列化B,反序列化的过程会去调用这个invokeReadResolve方法,通过该方法进行反射调用,如果readResolve 方法是static 这时会找不到该方法的。🐷


对猜测进行验证,弄一个简单的继承关系测试下!如图:


网络异常,图片无法展示
|


输出结果如下图:


解析:很明显这里 getDeclaredMethods  是获取不到任何方法的,因为这个只能获取到 B 自己声明的一些方法。


而源码中(上上张图😄)是通过这个getDeclaredMethod  方法去获取的!通过反射的知识点我们知道,该方法只能获取该类自己定义的方法。


网络异常,图片无法展示
|


其他访问修饰符也是符合对应的权限才会返回该方法的。


如下表:


修饰符 当前类 同包 子类 不同包
public Y Y Y Y
protected Y Y Y N
default Y Y N N
private Y N N N



目录
相关文章
|
前端开发 Java 关系型数据库
基于Java+Springboot+Vue开发的鲜花商城管理系统源码+运行
基于Java+Springboot+Vue开发的鲜花商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的鲜花商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。技术学习共同进步
916 7
|
人工智能 安全 Java
智慧工地源码,Java语言开发,微服务架构,支持分布式和集群部署,多端覆盖
智慧工地是“互联网+建筑工地”的创新模式,基于物联网、移动互联网、BIM、大数据、人工智能等技术,实现对施工现场人员、设备、材料、安全等环节的智能化管理。其解决方案涵盖数据大屏、移动APP和PC管理端,采用高性能Java微服务架构,支持分布式与集群部署,结合Redis、消息队列等技术确保系统稳定高效。通过大数据驱动决策、物联网实时监测预警及AI智能视频监控,消除数据孤岛,提升项目可控性与安全性。智慧工地提供专家级远程管理服务,助力施工质量和安全管理升级,同时依托可扩展平台、多端应用和丰富设备接口,满足多样化需求,推动建筑行业数字化转型。
508 5
|
消息中间件 算法 安全
JUC并发—1.Java集合包底层源码剖析
本文主要对JDK中的集合包源码进行了剖析。
|
存储 Java
【源码】【Java并发】【ThreadLocal】适合中学者体质的ThreadLocal源码阅读
前言 下面,跟上主播的节奏,马上开始ThreadLocal源码的阅读( ̄▽ ̄)" 内部结构 如下图所示,我们可以知道,每个线程,都有自己的threadLocals字段,指向ThreadLocalMap
743 81
【源码】【Java并发】【ThreadLocal】适合中学者体质的ThreadLocal源码阅读
|
11月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
506 1
|
11月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
452 1
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
810 70
|
12月前
|
存储 小程序 Java
热门小程序源码合集:微信抖音小程序源码支持PHP/Java/uni-app完整项目实践指南
小程序已成为企业获客与开发者创业的重要载体。本文详解PHP、Java、uni-app三大技术栈在电商、工具、服务类小程序中的源码应用,提供从开发到部署的全流程指南,并分享选型避坑与商业化落地策略,助力开发者高效构建稳定可扩展项目。
|
JavaScript Java 关系型数据库
家政系统源码,java版本
这是一款基于SpringBoot后端框架、MySQL数据库及Uniapp移动端开发的家政预约上门服务系统。
487 6
家政系统源码,java版本
|
Java
【源码】【Java并发】【ReentrantLock】适合中学者体质的ReentrantLock源码阅读
因为本文说的是ReentrantLock源码,因此会默认,大家对AQS有基本的了解(比如同步队列、条件队列大概> 长啥样?)。 不懂AQS的小朋友们,你们好呀!也欢迎先看看这篇
336 13
【源码】【Java并发】【ReentrantLock】适合中学者体质的ReentrantLock源码阅读