对于Serializable的理解
对于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的理解的更多相关文章
- Serializable深入理解
1.什么是序列化,解决什么问题 序列化可以对象的状态信息转换成可以持久化或者可以传输形式的过程.一般是转为字节数据.而把字节数组还原成原来同等对象的过程成为反序列化. 在Java中,对象的序列化与反序 ...
- Serializable的理解和使用 -----转载
1.定义 这是一个接口,当一个类实现这个接口后,这个类就变成了一个可序列化的类,它就可以被写入流,保存起来,然后也可以用流读取,反序列化. 一般情况下,一个对象会随着程序的执行完成而消失,而有时我们需 ...
- 【Java/Android性能优 4】PreloadDataCache支持预取的数据缓存,使用简单,支持多种缓存算法,支持不同网络类型,扩展性强
本文转自:http://www.trinea.cn/android/preloaddatacache/ 本文主要介绍一个支持自动向前或向后获取新数据的缓存的使用及功能.Android图片内存缓存可见I ...
- 【Java基础】IO 流
IO 流 File 类 java.io.File 类是文件和文件目录路径的抽象表示形式,与平台无关. File 能新建.删除.重命名文件和目录,但 File 不能访问文件内容本身. 如果需要访问文件内 ...
- 对Java Serializable(序列化)的理解和总结
我对Java Serializable(序列化)的理解和总结 博客分类: Java技术 JavaOSSocketCC++ 1.序列化是干什么的? 简单说就是为了保存在内存中的各种对象的状 ...
- Java Serializable(序列化)的理解和总结、具体实现过程(转)
原文地址:http://www.apkbus.com/forum.php?mod=viewthread&tid=13576&fromuid=3402 Java Serializable ...
- SQL Server-字字珠玑,一纸详文,完全理解SERIALIZABLE最高隔离级别(基础系列收尾篇)
前言 对于上述锁其实是一个老生常谈的话题了,但是我们是否能够很明确的知道在什么情况下会存在上述各种锁类型呢,本节作为SQL Server系列末篇我们 来详细讲解下. Range-Lock 上述关于Ra ...
- 【java提高】Serializable(一)--初步理解
Serializable(一)--初步理解 一 序列化是干什么的? 我们知道,在jvm中引用数据类型存在于栈中,而new创建出的对象存在于堆中.如果电脑断电那么存在于内存中的对象就会丢失.那么有没有方 ...
- 【眼见为实】自己动手实践理解数据库READ UNCOMMITED && SERIALIZABLE
目录 准备工作 ①准备测试表和测试数据 ②关闭数据库事务自动提交 ③设置InnoDB存储引擎隔离级别 [READ UNCOMMITTED] [READ UNCOMMITTED]能解决的问题 [READ ...
随机推荐
- Spring 梳理-启用MVC
启用注解启动的Spring MVC xml <mvc:annotation-dirven> <mvc:annotation-driven /> 是一种简写形式,完全可以手动配置 ...
- JavaScript 类型 检测
前言 ECMAScript中有5种数据类型,分别为Number,Boolean,Null,Undifined和String,以及一种复杂的数据类型Object(由名值对组成,是这门语言所有对象的基础类 ...
- 夯实Java基础系列22:一文读懂Java序列化和反序列化
本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...
- Drill 学习笔记之 入门体验
简介: Apache Drill是一个低延迟的分布式海量数据(涵盖结构化.半结构化以及嵌套数据)交互式查询引擎.分布式.无模式(schema-free) 是Google Dremel的开源实现,本质是 ...
- MongoDB 学习笔记之 分析器和explain
MongoDB分析器: 检测MongoDB分析器是否打开: db.getProfilingLevel() 0表示没有打开 1表示打开了,并且如果查询的执行时间超过了第二个参数毫秒(ms)为单位的最大查 ...
- mybatis入门百分百
今天重新返回来看自己的mybatis,总结了一些更好入门的办法,下面用最简单的方法带领大家入门. 此处先引入类包的关系图片 1.构建一个==普通==maven项目 构建好之后向pom.xml添加一下依 ...
- Java11月18日
动手动脑 实验一: 1. 下边的程序运行结果是什么? 2.你如何解释会得到这样的输出? 3.计算机是不会出错的,之所以得到这样的运行结果也是有原因的那么从这些运行结果中,你能总结出Java的哪些语法特 ...
- springboot配置logback日志
springboot配置logback日志 java web 下有好几种日志框架,比如:logback,log4j,log4j2(slj4f 并不是一种日志框架,它相当于定义了规范,实现了这个规范的日 ...
- Python_深拷贝和浅拷贝
深拷贝与浅拷贝 import copy v = 123 v1 = copy.copy(v) #浅拷贝 v2 = copy.deepcopy(v) #深拷贝 **拷贝只拷贝可变数据类型,浅拷贝只拷贝第一 ...
- Python3_基础
目录 数据类型 变量 数据类型的转换 算术操作符 输入 字符串常用方法 数据类型 我们先来看看三种常见的数据类型 字符串 str 在Python中,字符串一般都用引号引起来,不管是用单引号还是双引号都 ...