为什么很多类甚者底层源码要implements Serializable ?

在碰到异常类RuntimeException时,发现Throwable实现了 Serializable,还有我们平进的javabean一般也要实现Serializable,不明白为什么?做个小总结如下:

public class Throwable implements Serializable {/** use serialVersionUID from JDK 1.0.2 for interoperability */private static final long serialVersionUID = -3042686055658047285L;
}
  • serialization 允许你将实现了Serializable接口的对象转换为字节序列,这些字节序列可以被完全存储以备以后重新生成原来的对象。
  • serialization不但可以在本机做,而且可以经由网络操作。这个好处是很大的----因为它自动屏蔽了操作系统的差异,字节顺序等。比如,在Window平台生成一个对象并序列化之,然后通过网络传到一台Unix机器上,然后可以在这台Unix机器上正确地重构这个对象。


Object serialization主要用来支持2种主要的特性:

  1. Java的RMI(remote method invocation).RMI允许象在本机上一样操作远程机器上的对象。当发送消息给远程对象时,就需要用到serializaiton机制来发送参数和接收返回值。
  2. Java的JavaBeans.Bean的状态信息通常是在设计时配置的。Bean的状态信息必须被存起来,以便当程序运行时能恢复这些状态信息。这也需要serializaiton机制。
    总之如果在网络的环境下做类传输,应该还是implements Serializable。没有implements Serializable,你就不能通过rmi(包括ejb)提供远程调用。

再如一个C/S(socket)程序,client显示server段的数据,通常情况下你可以在server用取得socket的outputstream inputstream,将取得的数据处理成字符串到客户端,然后在客户端分拆字符串,但是这样显然降低效率,于是你可以将server 端数据包装成一个class implements Serializable,然后直接用objectoutputstream,objectinputstream直接传递。

Serizlizable 作用


对于一个存在于Java虚拟机中的对象来说,其内部的状态只保持在内存中。JVM停止之后,这些状态就丢失了。在很多情况下,对象的内部状态是需要被持久化下来的。提到持久化,最直接的做法是保存到文件系统或是数据库之中。比如:对象关系映射(Object-relational mapping)。对象序列化机制(object serialization)是Java语言内建的一种对象持久化方式,可以很容易的在JVM中的活动对象和字节数组(流)之间进行转换。除了可以很简单的实现持久化之外,另外序列化机制的另外一个重要用途是在远程方法调用中,用来对开发人员屏蔽底层实现细节。
 

基本的对象序列化


待序列化的Java类只需要实现Serializable接口即可。实际的序列化和反序列化工作是通过ObjectOuputStream和ObjectInputStream来完成的。ObjectOutputStream的writeObject方法可以把一个Java对象写入到流中,ObjectInputStream的readObject方法可以从流中读取一个Java对象。在写入和读取的时候,虽然用的参数或返回值是单个对象,但实际上操纵的是一个对象图,包括该对象所引用的其它对象,以及这些对象所引用的另外的对象。Java会自动帮你遍历对象图并逐个序列化。除了对象之外,Java中的基本类型和数组也是可以通过 ObjectOutputStream和ObjectInputStream来序列化的。
 

序列化时的对象替换


可能会希望在序列化的时候使用另外一个对象来代替当前对象。其中的动机可能是当前对象中包含了一些不希望被序列化的域。个订单系统中需要把订单的相关信息序列化之后,通过网络来传输。订单类Order引用了客户类Customer。在默认序列化的情况下,Order类对象被序列化的时候,其引用的Customer类对象也会被序列化,这可能会造成用户信息的泄露。

private static class OrderReplace implements Serializable{private static final long serialVersionUID=47832438;private String orderId;public OrderReplace(Order order){this.orderId = order.getId();}private Object readResolve(){//根据orderId查找Order 对象并返回。return null;}public Object writeReplace(){return new OrderReplace(this);}
}

这个替换对象类OrderReplace只保存了Order的ID。在Order类的writeReplace方法中返回了一个OrderReplace对象。这个对象会被作为替代写入到流中。同样的,需要在OrderReplace类中定义一个readResolve方法,用来在读取的时候再转换回 Order类对象。这样对调用者来说,替换对象的存在就是透明的。
 

序列化和对象创建


在通过ObjectInputStream的readObject方法读取到一个对象之后,这个对象是一个新的实例,但是其构造方法是没有被调用的,其中的域的初始化代码也没有被执行。调用者并不知道对象是通过一般的new操作符来创建的,还是通过反序列化所得到的。解决的办法就是在类的readObject方法里面,再执行所需的对象初始化逻辑。对于一般的Java类来说,构造方法中包含了初始化的逻辑。可以把这些逻辑提取到一个方法中,在readObject方法中调用此方法。
 

版本更新


把一个Java对象序列化之后,所得到的字节数组一般会保存在磁盘或数据库之中。在保存完成之后,有可能原来的Java类有了更新,比如添加了额外的域。这个时候从兼容性的角度出发,要求仍然能够读取旧版本的序列化数据。在读取的过程中,当ObjectInputStream发现一个对象的定义的时候,会尝试在当前JVM中查找其Java类定义。这个查找过程不能仅根据Java类的全名来判断,因为当前JVM中可能存在名称相同,但是含义完全不同的Java 类。这个对应关系是通过一个全局惟一标识符serialVersionUID来实现的。通过在实现了Serializable接口的类中定义该域,就声明了该Java类的一个惟一的序列化版本号。JVM会比对从字节数组中得出的类的版本号,与JVM中查找到的类的版本号是否一致,来决定两个类是否是兼容的。对于开发人员来说,需要记得的就是在实现了Serializable接口的类中定义这样的一个域,并在版本更新过程中保持该值不变。当然,如果不希望维持这种向后兼容性,换一个版本号即可。该域的值一般是综合Java类的各个特性而计算出来的一个哈希值。可以通过Java提供的serialver命令来生成。在Eclipse中,如果Java类实现了Serializable接口,Eclipse会提示并帮你生成这个serialVersionUID。

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;public class Book implements Serializable {// private static final long serialVersionUID = 5805574648076667828L;String name = "marsXTU";// int vale = 7;// 序列化写入文件public void save() throws IOException {FileOutputStream f = new FileOutputStream("d://book.txt");ObjectOutputStream oos = new ObjectOutputStream(f);oos.writeObject(this);oos.close();}// 反序列化从文件读取public void read() throws IOException, ClassNotFoundException {FileInputStream f = new FileInputStream("d://book.txt");ObjectInputStream ois = new ObjectInputStream(f);Book b = (Book) ois.readObject();ois.close();}public static void main(String[] args) {Book b = new Book();try {// 序列化b.save();// 读取b.read();} catch (Exception e) {e.printStackTrace();}}
}
  • 如果增加int vale = 7;,把b.save()注释掉,再次执行程序则会抛出如下异常。java.io.InvalidClassException: cn.com.chenlly.Book; local class incompatible: stream classdesc serialVersionUID = -8127777334808352611, local class serialVersionUID = 6452469819260868051
  • java 默认情况下serialVersionUID是是综合Java类的各个特性而计算出来的一个哈希值。两次的属性值一样导致serialVersionUID也不一样。
  • 如果加上private static final long serialVersionUID = 5805574648076667828L则这个类都有一个唯一标识,两次的serialVersionUID不变。反序列化中还是不会报错。
  • 如果把字符串改成int name=34; 执行逆-串行化操作时系统就不知道如何处理该值,显示出错误信息:java.io.InvalidClassException: cn.com.chenlly.Book; incompatible types for field name。
    简而言之,如果文件中确实保存了所有必需的数据,那么仍有可能读取该文件,当然前提是必须处理好串行化的UID。

为什么很多类甚者底层源码要implements Serializable ?相关推荐

  1. hashmap允许null键和值吗_hashMap底层源码浅析

    来源:https://blog.csdn.net/qq_35824590/article/details/111769203 hashmap是我们经常使用的一个工具类.那么知道它的一些原理和特性吗? ...

  2. vue 分模块打包 脚手架_Vue面试官最爱的底层源码问题,你可以这样回答!

    最近看到身边很多人都在投简历,有因为企业裁员的,有因为自己想跳槽的,原因不一,但是最终大家都会需要接触到面试这个事情.但是很多人对待面试不够认真,只会等待结果,不去努力.所以这边想整理一些懒人面试技巧 ...

  3. Map和Set,简单模拟实现哈希表以及哈希表部分底层源码的分析

    目录 Map和Set的简单介绍 降低哈希冲突发生的概率以及当冲突发生时如何解决哈希冲突 简单模拟实现哈希表--1.key为整形:2.key为引用类型 哈希表部分底层源码的分析 1.Map和Set的简单 ...

  4. 集合底层源码分析之HashMap《上》(三)

    集合底层源码分析之HashMap<上>(三) 前言 源码分析 HashMap主要属性及构造方法分析 tableSizeFor()方法源码分析 Node类源码分析 TreeNode类源码分析 ...

  5. hashmap的特性?HashMap底层源码,数据结构?Hashmap和hashtable ConcurrentHashMap区别?

    1.hashmap的特性? 允许空键和空值(但空键只有一个,且放在第一位) 元素是无序的,而且顺序会不定时改变 key 用 Set 存放,所以想做到 key 不允许重复,key 对应的类需要重写 ha ...

  6. StringBuffer类【JDK源码分析】

    StringBuffer类[JDK源码分析] 前言 推荐 说明 StringBuffer类 基本信息 属性 构造方法 部分方法 length capacity append insert revers ...

  7. HashMap 底层源码细致分析

    JDK集合HashMap 底层源码细致分析 前言 提示:对于初始 HashMap 的小伙伴来说,不推荐直接硬啃,建议先看一下如下几个视频教程之后再回头好好理解.(一遍看不懂则反复看,一小块一小块的找对 ...

  8. idea 线程内存_Java线程池系列之-Java线程池底层源码分析系列(二)

    课程简介: 课程目标:通过本课程学习,深入理解Java线程池,提升自身技术能力与价值. 适用人群:具有Java多线程基础的人群,希望深入理解线程池底层原理的人群. 课程概述:多线程的异步执行方式,虽然 ...

  9. 监听返回app_基于 Redis 消息队列实现 Laravel 事件监听及底层源码探究

    在 Laravel 中,除了使用 dispatch 辅助函数通过 Illuminate\Bus\Dispatcher 显式推送队列任务外,还可以通过事件监听的方式隐式进行队列任务推送,在这个场景下,事 ...

  10. 【Unity主程手记(摘录)】第一章(二) - Dictory 底层源码剖析

    第一章(二) - Dictory 底层源码剖析 提示:个人学习总结,如有错误,敬请指正. 文章目录 第一章(二) - Dictory 底层源码剖析 一.Dictory 1.底层数据结构 2.Add - ...

最新文章

  1. 指定ASP .NET Core Web应用端口
  2. VSCode中屏蔽文件files.exclude和屏蔽文件搜索search.exclude
  3. ASP.NET验证码C#
  4. cookie的相关知识
  5. ZOJ1221 Risk 图形的遍历
  6. 热血江湖战无止境与服务器连接不稳定,《热血江湖》V14.0“战无止境”新版玩不停...
  7. leetcode179. 最大数
  8. 阅兵活动首次出动“AI剪辑师”,你看到的短视频可能都出自于ta
  9. 英特尔商用攻略升级:企业如何趟平信息化建设这条路?
  10. 音乐平台项目的几个问题总结(头像裁剪以及跨页面播放音乐)
  11. opencv图像连通区域分析
  12. 技术专家(ai/大数据)_``我们淹没在数据中'':在专家和AI时代如何思考自己
  13. Linux【操作系统】
  14. 生意参谋transit-id生成和data解密python版
  15. 计算机设备管理没有打印机,win7电脑找不到打印机设备怎么办
  16. linux opensource,GitHub - opensourceteams/linux: linux命令
  17. python飞机大战实训报告200_飞机大战实训报告方案.doc
  18. 拷机测试需要多久_网站建设要多久?
  19. MapGuide应用程序示例——你好,MapGuide!
  20. 圣天诺LDK能做什么?

热门文章

  1. ORB-SLAM3 一张图梳理rgbd_tum.cc主流程
  2. ubuntu14.04LTS 安装后几个基本设置
  3. 机器学习笔记——感知机理解(自行取用,并不一定适合每个人)
  4. 斯坦福大学深度学习公开课cs231n学习笔记(7)神经网络防止数据过拟合:损失函数和正则化
  5. 实操教程|使用计算机视觉的方法在钢铁平面上检测焊接缺陷
  6. 重磅!2020 年算法工程师技术路线图
  7. 第十章第二节 阿基米德原理
  8. 爬虫2 urllib用法
  9. Highcharts柱形范围图使用示例
  10. 上下文路径request.getContextPath();与${pageContext.request.contextPath}