对于Serializable的理解

Last Edited: Apr 04, 2019 2:53 PM

Tags: java

开始

序列化:把Java对象转换为字节序列的过程。

反序列化:把字节序列恢复为Java对象的过程。

序列化的解释

在Java中,我们可以通过多种方式来创建对象,并且只要对象没有被回收我们都可以复用该对象,但是,我们创建出来的这些**Java对象都是存在于JVM的堆内存中的。**只有JVM处于运行状态的时候,这些对象才可能存在,一单JVM停止运行,这些对象的状态也就随之丢失了。

但是在真是的应用场景中,我们需要将这些对象持久化下来,并且能够在需要的时候把对象重新读取出来。Java对象序列化就可以帮助我们实现该功能。

测试

这样说的话我们可能不怎么会理解,我们直接上手代码。现在我们有一个需求就是把一个对象保存到文件中,我们现在简单的测试一下。

创建一个最简单的Student类


public class Student {
private int id;
public Student(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
'}';
}
}

接下来创建一个类用于输出对象到文件中。

    @Test
public void main() throws Exception{
Student student = new Student(1);
FileOutputStream fos = new FileOutputStream("out.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(student);
oos.close();
}

测试结果是什么呢,直接运行的话系统会直接报 java.io.NotSerializableException

哪我们现在实现 serializable接口重新试试看,将Student类修改为

    public class Student implements Serializable {
private int id;
public Student(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
'}';
}
}

结果当然没有问题啦,成功的执行了,那么下面我们从文件中获取对象试试看。

    @Test
public void main1() throws Exception{
ObjectInputStream ois =
new ObjectInputStream(
new FileInputStream(new File("out.txt")));
Student student = (Student)ois.readObject();
ois.close();
System.out.println(student);
}

程序并不会报错,完整的输出了 Student{id=1}

在这之前,我们一直没有把 serialVersionUID 这个参数加入在程序中,现在我们在Student类中加入

    private static final long serialVersionUID = 1L;

重新运行main1 主函数试试看(这里其实用到的还是没有吧serialVersionUID参数加入Student类之前,用main函数生成的out.txt文件)

程序会报

    java.io.InvalidClassException: com.liuyanzhao.blog.test.SerializableTest.Student; local class incompatible: stream classdesc serialVersionUID = -1350335207596260519, local class serialVersionUID = 1

从上面可以看出,当我们没有定义serialVersionUID参数的时候,系统自动帮我们生成serialVersionUID 。当我们手动给serialVersionUID赋值1L之后,再重新去读取之前那个对象,serialVersionUID不同,程序报错。

我们仔细想一想,在报错信息中,已经把系统给我们自动定义的serialVersionUID给出来了,我们不就直接可以把 报错信息中的stream classdesc serialVersionUID = -1350335207596260519 弄到我们的Student类里面去。

话不多说,我们直接测试,我们把Student中的 private static final long serialVersionUID = 1L;改为

    private static final long serialVersionUID = -1350335207596260519L;

执行,main1主函数,果然,跟我们想的一样,程序成功的执行了。

根据上面的实验结果我们可以总结出来三个特点

  • 要想把一个对象写入文件,我们需要实现Serializable接口
  • 当没有定义serialVersionUID的时候,系统会自动给我们创建
  • 只有当serialVersionUID一致的时候,才能成功的从文件中读取之前保存到对象

静态变量序列化

在理解上面的测试之后,我们开始一些进阶的测试,静态变量的序列化,我们修改Student中的代码如下

    public class Student implements Serializable {
private static final long serialVersionUID = -1350335207596260519L;
private int id;
private static String name;
public Student(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public static String getName() {
return name;
}
public static void setName(String name) {
Student.name = name;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
" name = " + getName() + '}';
}
}

在上面的代码中,我们添加一个静态的name变量,并重构了toString方法,接下来就用main2主函数测试

    @Test
public void main2() throws Exception{
Student student = new Student(1);
Student.setName("XXX");
System.out.println(student);
FileOutputStream fos = new FileOutputStream("out.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(student);
oos.close();
}

运行上面的代码将对象写入到文件中之后,在次运行main1主函数读取在文件中的对象,结果如下

Student{id=1 name = null}

可以看出,静态变量并未被写入文件。贴上一段复制过来的文本

串行化只能保存对象的非静态成员交量,不能保存任何的成员方法和静态的成员变量,而且串行化保存的只是变量的值,对于变量的任何修饰符都不能保存。

如果把Student类中的name定义为static类型的话,试图重构,就不能得到原来的值,只能得到null。说明对静态成员变量值是不保存的。这其实比较容易理解,序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量。

transient关键字

简单来说,当某些变量不想被写入文件,同是又不适合使用static关键字声明,那么此时就需要用transient关键字来声明该变量。例如,把Student类中的静态变量name改为

    private transient String name;

接下来重复之前的测试方法给对象赋值,把对象写入文件,从文件中读取对象,可以得到跟static关键字定义的name一样的结果。

在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。

注:对于某些类型的属性,其状态是瞬时的,这样的属性是无法保存其状态的。例如一个线程属性或需要访问IO、本地资源、网络资源等的属性,对于这些字段,我们必须用transient关键字标明,否则编译器将报措。

序列化中的继承问题

我觉得就只有一个重点,

  • 当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口。

通过上面的一些测试,我们大概知道了Serializable接口的作用,接下来就是序列化的用途

序列化的用途

  • 想把的内存中的对象状态保存到一个文件中或者数据库中时候
  • 想把对象通过网络进行传播的时候

结语

上面的仅仅是我自己的理解,里面并没用用太多的专业词汇,因为我也不懂,嘿嘿,

参考文章

https://blog.csdn.net/u011568312/article/details/57611440

一些更好的理解

一个对象序列化的接口,一个类只有实现了Serializable接口,它的对象才是可序列化的。因此如果要序列化某些类的对象,这些类就必须实现Serializable接口。而实际上,Serializable是一个空接口,没有什么具体内容,它的目的只是简单的标识一个类的对象可以被序列化。

什么情况下需要序列化

a)当你想把的内存中的对象写入到硬盘的时候;

b)当你想用套接字在网络上传送对象的时候;

c)当你想通过RMI传输对象的时候;

再稍微解释一下:a)比如说你的内存不够用了,那计算机就要将内存里面的一部分对象暂时的保存到硬盘中,等到要用的时候再读入到内存中,硬盘的那部分存储空间就是所谓的虚拟内存。在比如过你要将某个特定的对象保存到文件中,我隔几天在把它拿出来用,那么这时候就要实现Serializable接口;

b)在进行java的Socket编程的时候,你有时候可能要传输某一类的对象,那么也就要实现Serializable接口;最常见的你传输一个字符串,它是JDK里面的类,也实现了Serializable接口,所以可以在网络上传输。

c)如果要通过远程的方法调用(RMI)去调用一个远程对象的方法,如在计算机A中调用另一台计算机B的对象的方法,那么你需要通过JNDI服务获取计算机B目标对象的引用,将对象从B传送到A,就需要实现序列化接口。

对于Serializable的理解的更多相关文章

  1. Serializable深入理解

    1.什么是序列化,解决什么问题 序列化可以对象的状态信息转换成可以持久化或者可以传输形式的过程.一般是转为字节数据.而把字节数组还原成原来同等对象的过程成为反序列化. 在Java中,对象的序列化与反序 ...

  2. Serializable的理解和使用 -----转载

    1.定义 这是一个接口,当一个类实现这个接口后,这个类就变成了一个可序列化的类,它就可以被写入流,保存起来,然后也可以用流读取,反序列化. 一般情况下,一个对象会随着程序的执行完成而消失,而有时我们需 ...

  3. 【Java/Android性能优 4】PreloadDataCache支持预取的数据缓存,使用简单,支持多种缓存算法,支持不同网络类型,扩展性强

    本文转自:http://www.trinea.cn/android/preloaddatacache/ 本文主要介绍一个支持自动向前或向后获取新数据的缓存的使用及功能.Android图片内存缓存可见I ...

  4. 【Java基础】IO 流

    IO 流 File 类 java.io.File 类是文件和文件目录路径的抽象表示形式,与平台无关. File 能新建.删除.重命名文件和目录,但 File 不能访问文件内容本身. 如果需要访问文件内 ...

  5. 对Java Serializable(序列化)的理解和总结

    我对Java Serializable(序列化)的理解和总结 博客分类: Java技术 JavaOSSocketCC++  1.序列化是干什么的?       简单说就是为了保存在内存中的各种对象的状 ...

  6. Java Serializable(序列化)的理解和总结、具体实现过程(转)

    原文地址:http://www.apkbus.com/forum.php?mod=viewthread&tid=13576&fromuid=3402 Java Serializable ...

  7. SQL Server-字字珠玑,一纸详文,完全理解SERIALIZABLE最高隔离级别(基础系列收尾篇)

    前言 对于上述锁其实是一个老生常谈的话题了,但是我们是否能够很明确的知道在什么情况下会存在上述各种锁类型呢,本节作为SQL Server系列末篇我们 来详细讲解下. Range-Lock 上述关于Ra ...

  8. 【java提高】Serializable(一)--初步理解

    Serializable(一)--初步理解 一 序列化是干什么的? 我们知道,在jvm中引用数据类型存在于栈中,而new创建出的对象存在于堆中.如果电脑断电那么存在于内存中的对象就会丢失.那么有没有方 ...

  9. 【眼见为实】自己动手实践理解数据库READ UNCOMMITED && SERIALIZABLE

    目录 准备工作 ①准备测试表和测试数据 ②关闭数据库事务自动提交 ③设置InnoDB存储引擎隔离级别 [READ UNCOMMITTED] [READ UNCOMMITTED]能解决的问题 [READ ...

随机推荐

  1. 浅谈Spring的事务隔离级别与传播性

    浅谈Spring的事务隔离级别与传播性 这篇文章以一个问题开始,如果你知道答案的话就可以跳过不看啦@(o・ェ・)@ Q:在一个批量任务执行的过程中,调用多个子任务时,如果有一些子任务发生异常,只是回滚 ...

  2. Windows10安装多个版本的PostgreSQL数据库,但是均没有自动注册Windows服务的解决方法

    1.确保正确安装了PostgreSQL数据库,注意端口号不能相同 我的安装目录如图: 其中9.6版本的端口号为5432,10版本的端口号为5433,11版本的端口号为5434.若不知道端口号,可在Po ...

  3. GIT 安装和配置

    Git(读音为/gɪt/.)是一个开源的分布式版本控制系统,可以有效.高速地处理从很小到非常大的项目版本管理. 一.安装 具体参照 安装 Git ,安装完git之后可以安装客户端工具 tortoise ...

  4. 前端获取后台传输过来是数据 {张三:12} 解析为[object object],获取其中内容

    昨天遇到前端传输过来的数据为[{张三:12},{李四:23}],后台用的是map格式,我在前端js中暂未找到直接调用对象内容的方法,故利用以下方法来获取: $.each(data.data,funct ...

  5. xampp修改mysql 启动脚本

    打开xmapp,点击mysql对应的config按钮进入my.ini文件,如图所示: 修改mysqld服务的port参数3306为你想要设置的port,如图2所示: 重新启动mysql服务即可用客户端 ...

  6. 阿里云服务器ecs配置之安装nginx

    一.简介 Nginx是一款轻量级的网页服务器.反向代理服务器.相较于Apache.lighttpd具有占有内存少,稳定性高等优势.它最常的用途是提供反向代理服务. 二 .安装 1.准备工作 Nginx ...

  7. 玩转 SpringBoot 2 之整合 JWT 上篇

    前言 该文主要带你了解什么是 JWT,以及JWT 定义和先关概念的介绍,并通过简单Demo 带你了解如何使用 SpringBoot 2 整合 JWT. 介绍前在这里我们来探讨一下如何学习一门新的技术, ...

  8. 蓝松SDK - 卡点视频制作介绍

    ---恢复内容开始--- 说明:卡点视频:是指随音频的节拍来不断的切换照片做成的一种 动感视频效果.卡点是卡的音乐中节奏切换的时间点, 在这些时间点上动态切换一个图片, 并给图片做各种动画,从而形成或 ...

  9. Django之使用redis缓存session,历史浏览记录,首页数据实现性能优化

    Redis缓存session 配置Django缓存数据到redis中 # diango的缓存配置 CACHES = { "default": { "BACKEND&quo ...

  10. redis安装及简单使用

    前言 一般企业级开发,数据库用的都是关系型数据库Mysql.Oracle及SqlServer.无一例外,在开发过程中,我们都必须通过数据库驱动来连接到数据库,之后才可以完成对数据库的增删改查等业务.而 ...