实体类为什么要实现Serializable序列化的作用

实体类为什么要实现Serializable序列化的作用客户端访问了某个能开启会话功能的资源 web 服务器就会创建一个与该客户端对应的 HttpSession 对象 每个 HttpSession 对象都要站用一定的内存空间 如果在某一时间段内访问站点的用户很多 web 服务器内存中就会积累大量的 HttpSession 对象 消耗大量的服务器内存 即使用户已经离开或者关闭了浏览器 web 服务器仍要保留与之对应的 HttpSession 对象 在他们超时

微信搜索:“二十同学” 公众号,欢迎关注一条不一样的成长之路       

客户端访问了某个能开启会话功能的资源, web服务器就会创建一个与该客户端对应的HttpSession对象,每个HttpSession对象都要站用一定的内存空间。如果在某一时间段内访问站点的用户很多,web服务器内存中就会积累大量的HttpSession对象,消耗大量的服务器内存,即使用户已经离开或者关闭了浏览器,web服务器仍要保留与之对应的HttpSession对象,在他们超时之前,一直占用web服务器内存资源。

        web服务器通常将那些暂时不活动但未超时的HttpSession对象转移到文件系统或数据库中保存,服务器要使用他们时再将他们从文件系统或数据库中装载入内存,这种技术称为Session的持久化。

         将HttpSession对象保存到文件系统或数据库中,需要采用序列化的方式将HttpSession对象中的每个属性对象保存到文件系统或数据库中;将HttpSession对象从文件系统或数据库中装载如内存时,需要采用反序列化的方式,恢复HttpSession对象中的每个属性对象。所以存储在HttpSession对象中的每个属性对象必须实现Serializable接口。

serialVersionUID 的作用

serialVersionUID 用来表明类的不同版本间的兼容性

       Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。

      当实现java.io.Serializable接口的实体(类)没有显式地定义一个名为serialVersionUID,类型为long的变量时,Java序列化机制会根据编译的class自动生成一个serialVersionUID作序列化版本比较用,这种情况下,只有同一次编译生成的class才会生成相同的serialVersionUID 。

       如果我们不希望通过编译来强制划分软件版本,即实现序列化接口的实体能够兼容先前版本,未作更改的类,就需要显式地定义一个名为serialVersionUID,类型为long的变量,不修改这个变量值的序列化实体都可以相互进行串行化和反串行化。












      引起这个疑问,还是从Hibernate使用查询缓存说起;对象实例除了存在于内存,二级缓存还会将对象写进硬盘在需要的时候再读取出来使用,此时就必须提到一个概念:序列化

      程序在运行时实例化出对象,这些对象存在于内存中,随着程序运行停止而消失,但如果我们想把某些对象(一般都是各不相同的属性)保存下来或者传输给其他进程,在程序终止运行后这些对象仍然存在,可以在程序再次运行时读取这些对象的信息,或者在其他程序中利用这些保存下来的对象信息恢复成实例对象。这种情况下就要用到对象的序列化和反序列化。

 其实很早就知道的,在Java中常见的几个类,如:Interger/String等,都实现了java.io.Serializable接口。这个序列化接口没有任何方法和域,仅用于标识序列化语意;实现 Serializable 接口的类是可序列化的,没有实现此接口的类将不能被序列化和反序列化。序列化类的所有子类本身都是可序列化的,不再需要显式实现 Serializable 接口。只有经过序列化,才能兼容对象在磁盘文本以及在网络中的传输,以及恢复对象的时候反序列化等操作。

问题一:为何要实现序列化?

答:序列化就是对实例对象的状态(State 对象属性而不包括对象方法)进行通用编码(如格式化的字节码)并保存,以保证对象的完整性和可传递性。

简而言之:序列化,就是为了在不同时间或不同平台的JVM之间共享实例对象

// 经常使用如下: public static void main(String[] args) throws Exception { File file = new File("user.ser"); ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream(file)); User user = new User("zhang", 18, Gender.MALE); oout.writeObject(user); oout.close(); ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file)); Object newUser = oin.readObject(); oin.close(); System.out.println(newUser); }

  如没有 实现Serializable接口,在序列化时,使用ObjectOutputStream的write(object)方法将对象保存时将会出现异常。其实 java.io.Serializable 只是一个没有属性和方法的空接口,但是问题来了。。

问题二:为何一定要实现 Serializable 才能进行序列化呢?

使用 ObjectOutputStream 来持久化对象, 对于此处抛出的异常,查看该类中实现如下:

private void writeObject0(Object obj, boolean unshared) throws IOException { // ... // remaining cases if (obj instanceof String) { writeString((String) obj, unshared); } else if (cl.isArray()) { writeArray(obj, desc, unshared); } else if (obj instanceof Enum) { writeEnum((Enum) obj, desc, unshared); } else if (obj instanceof Serializable) { writeOrdinaryObject(obj, desc, unshared); } else { if (extendedDebugInfo) { throw new NotSerializableException( cl.getName() + "\n" + debugInfoStack.toString()); } else { throw new NotSerializableException(cl.getName()); } } // ... }

 从此可知, 如果被写对象类型是String、数组、Enum、Serializable,就可以进行序列化,否则将抛出NotSerializableException。 

最后提点注意:

1、在序列化对象时,不仅会序列化当前对象本身,还会对该对象引用的其它对象也进行序列化,如此引用传递序列化。如果一个对象包含的成员变量是容器类等并深层引用,那么序列化过程开销也较大。

2、当字段被声明为 transient 后,默认序列化机制就会忽略该字段。(还有方法就是自定义writeObject方法,见下代码示例)

3、在单例类中添加一个readResolve()方法(直接返回单例对象),以保证在序列化过程仍保持单例特性。

此外补充一下,

 在路径下jdk中还有另外一种形式的对象持久化,即:外部化(Externalization)。

public interface Externalizable extends java.io.Serializable { void writeExternal(ObjectOutput out) throws IOException; void readExternal(ObjectInput in) throws IOException, ClassNotFoundException; }

  外部化和序列化是实现同一目标的两种不同方法。

  通过 Serializable 接口对对象序列化的支持是jdk内支持的 API ,但是java.io.Externalizable的所有实现者必须提供读入和写出的具体实现,怎么实现完全由你自定义。序列化(Serializable )会自动存储所有必要的信息(如属性以及属性类型等),用以反序列化成原来一样的实例,而外部化(Externalizable)则只保存被存储实例中你需要的信息。

示例代码如下:

public class User implements Externalizable { private String name; transient private Integer age; // 屏蔽字段 private Gender gender; public User() { System.out.println("none constructor"); } public User(String name, Integer age, Gender gender) { System.out.println("arg constructor"); this.name = name; this.age = age; this.gender = gender; } // 实现读写 private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeInt(age); // 屏蔽gender } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); age = in.readInt(); } // 具体重写 @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(name); out.writeInt(age); // 屏蔽gender } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { name = (String) in.readObject(); age = in.readInt(); } }

 注意,用Externalizable进行序列化,当读取对象时,会调用被序列化类的无参构造器创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中。实现Externalizable接口的类必须要提供一个无参的构造器,且访问权限为 public。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/219941.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 下午9:32
下一篇 2026年3月17日 下午9:32


相关推荐

  • 当前Activity跳转到另外一个Activity「建议收藏」

    当前Activity跳转到另外一个Activity「建议收藏」当前Activity跳转到另外一个ActivityStep1:创建firest_layout.xml文件.添加一个Button:<Buttonandroid:id="@+id/button1"android:layout_width="wrap_content"android:layout_height="wrap_content"android…

    2022年5月11日
    52
  • git常用命令

    git常用命令Git常用命令汇总以及其它相关操作(转载)一、常用的git命令–文件目录操作命令mkdir*创建一个空目录*指目录名pwd显示当前目录的路径。cat*查看*文件内容gitr

    2022年7月4日
    27
  • 验证二叉搜索树 leetcode_二叉树 面试题

    验证二叉搜索树 leetcode_二叉树 面试题重写equal()时为什么也得重写hashCode()之深度解读equal方法与hashCode方法渊源 原创  2016年05月08日 23:14:19 标签:java equal方法重写 /java /重写equals方法和hashCode方 10077 转载请注明出处: http://blog.csdn.net/javazejian/art…

    2022年8月8日
    9
  • apache安装ssl证书_apache ssl证书配置

    apache安装ssl证书_apache ssl证书配置1、apache错误提示libz.a:couldnotreadsymbols:Badvalue”重新安装openssl加上-fPIC和enable-shared参数./config-fPIC–prefix=/usr/local/openssl1.0.1 enable-shared2、apache的httpd.conf缺乏LoadModulessl_module

    2025年11月30日
    6
  • MixMatch文章解读+算法流程+核心代码详解

    MixMatch文章解读+算法流程+核心代码详解MixMatch 本博客仅做算法流程疏导 具体细节请参见原文原文查看原文点这里 Github 代码 Github 代码点这里解读 MixMatch 抓住了半监督算法的两个重要观点 第一是熵最小化 第二是一致性正则化 结合这两个观点的算法就形成了 MixMatch 熵最小化半监督算法的一个常见假设就是分类的决策边界不应该通过数据分布的高密度区域 这句话简单的理解可以想象一个聚类模型 其决策边界一定是在簇与簇之间的稀疏边界上 不可能穿过一个簇的中心 高密度区域 而实现这一点的一种方法就是要求分类器对未标记数

    2026年3月20日
    2
  • phps2021永久激活码_最新在线免费激活

    (phps2021永久激活码)2021最新分享一个能用的的激活码出来,希望能帮到需要激活的朋友。目前这个是能用的,但是用的人多了之后也会失效,会不定时更新的,大家持续关注此网站~IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.net/100143.html…

    2022年3月26日
    55

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号