《Java基础知识》序列化与反序列化详解
序列化的作用:为了不同jvm之间共享实例对象的一种解决方案.由java提供此机制。
序列化应用场景:
1. 分布式传递对象。
2. 网络传递对象。
3. tomcat关闭以后会把session对象序列化到SESSIONS.ser文件中,等下次启动的时候就把这些session再加载到内存中。
完整案例:
import java.io.Serializable;
public class Box implements Serializable {
public Box(){
System.out.println("调用构造Box方法");
}
private String width;
private String height;
public String getWidth() {
return width;
}
public void setWidth(String width) {
this.width = width;
}
public String getHeight() {
return height;
}
public void setHeight(String height) {
this.height = height;
}
@Override
public String toString() {
return "Box{" +
"width=" + width +
", height=" + height +
'}';
}
}
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; public class SerializableDemo {
public static void main(String[] args){
Box myBox = new Box();
myBox.setWidth("50");
myBox.setHeight("30"); try{
File file = new File("src\\demo\\knowledgepoints\\file\\foo.ser"); //把对象信息写入文件中。
ObjectOutputStream oout = new ObjectOutputStream (new FileOutputStream(file));
oout.writeObject(myBox);
oout.close(); //把对象信息从文件中获取出来。
ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
Box newMyBox = (Box)oin.readObject(); // 没有强制转换到Person类型
oin.close();
System.out.println(newMyBox);
}catch(Exception ex){
ex.printStackTrace();
}
}
}
运行结果:

序列化相关注意事项:
a)序列化时,只对对象的状态进行保存,而不管对象的方法;
b)当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;
c)当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;
d) 被序列化的对象需要 实现(Serializable)接口;
e) 案例:构造方法没有执行,说明生成新对象,不是通过New出来的;
Java 对象被序列化需要实现(Serializable)接口,原因:
在 我们将对象序列化的类 ObjectOutputStream 的方法 writeObject0 中可以找到答案。
以下是JDK8的部分源码,红色字体部分就是原因。
private void writeObject0(Object obj, boolean unshared)
throws IOException
{
boolean oldMode = bout.setBlockDataMode(false);
depth++;
try {
// handle previously written and non-replaceable objects
int h;
if ((obj = subs.lookup(obj)) == null) {
writeNull();
return;
} else if (!unshared && (h = handles.lookup(obj)) != -1) {
writeHandle(h);
return;
} else if (obj instanceof Class) {
writeClass((Class) obj, unshared);
return;
} else if (obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
return;
} // check for replacement object
Object orig = obj;
Class<?> cl = obj.getClass();
ObjectStreamClass desc;
for (;;) {
// REMIND: skip this check for strings/arrays?
Class<?> repCl;
desc = ObjectStreamClass.lookup(cl, true);
if (!desc.hasWriteReplaceMethod() ||
(obj = desc.invokeWriteReplace(obj)) == null ||
(repCl = obj.getClass()) == cl)
{
break;
}
cl = repCl;
}
if (enableReplace) {
Object rep = replaceObject(obj);
if (rep != obj && rep != null) {
cl = rep.getClass();
desc = ObjectStreamClass.lookup(cl, true);
}
obj = rep;
} // if object replaced, run through original checks a second time
if (obj != orig) {
subs.assign(orig, obj);
if (obj == null) {
writeNull();
return;
} else if (!unshared && (h = handles.lookup(obj)) != -1) {
writeHandle(h);
return;
} else if (obj instanceof Class) {
writeClass((Class) obj, unshared);
return;
} else if (obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
return;
}
} // 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());
}
}
} finally {
depth--;
bout.setBlockDataMode(oldMode);
}
}
如何将序列化控制在自己手中?
1. 通过关键字【transient】实现,字段值不被序列化。
改一下Box类:
import java.io.Serializable;
public class Box implements Serializable {
public Box(){
System.out.println("调用构造Box方法");
}
// 关键字:transient 控制width不被序列化,保护数据。
private transient String width;
private String height;
public String getWidth() {
return width;
}
public void setWidth(String width) {
this.width = width;
}
public String getHeight() {
return height;
}
public void setHeight(String height) {
this.height = height;
}
@Override
public String toString() {
return "Box{" +
"width=" + width +
", height=" + height +
'}';
}
}
运行结果:

这个box的width值被擦除了。
2. writeObject()方法与readObject()方法。
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable; public class Box implements Serializable { public Box(){
System.out.println("调用构造Box方法");
} // 关键字:transient 控制width不被序列化,保护数据。
private transient String width;
private String height; public String getWidth() {
return width;
} public void setWidth(String width) {
this.width = width;
} public String getHeight() {
return height;
} public void setHeight(String height) {
this.height = height;
} private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeChars(width);
} private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
width = in.readLine();
} @Override
public String toString() {
return "Box{" +
"width=" + width +
", height=" + height +
'}';
}
}
运行结果:

加入writeObject()方法与readObject()方法后,width值又回来了,这两个方法都是私有的,已经可以基本猜测是通过反射调用方法,赋值的。
3.Externalizable 接口,自定义实现写入和读取序列化对象
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput; public class BoxTmp implements Externalizable {
public BoxTmp(){
System.out.println("调用构造Box方法");
} private String width;
private String height; public String getWidth() {
return width;
} public void setWidth(String width) {
this.width = width;
} public String getHeight() {
return height;
} public void setHeight(String height) {
this.height = height;
} @Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeChars(width+","+width);
} @Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
String str = in.readLine();
this.width = str.split(",")[0];
this.height = str.split(",")[1];
} @Override
public String toString() {
return "BoxTmp{" +
"width='" + width + '\'' +
", height='" + height + '\'' +
'}';
}
}
public class ExternalizableDemo {
public static void main(String[] args){
BoxTmp boxTmp = new BoxTmp();
boxTmp.setWidth("50");
boxTmp.setHeight("30");
try{
File file = new File("src\\demo\\knowledgepoints\\file\\foo.txt");
//把对象信息写入文件中。
ObjectOutputStream oout = new ObjectOutputStream (new FileOutputStream(file));
oout.writeObject(boxTmp);
oout.close();
//把对象信息从文件中获取出来。
ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
BoxTmp newBoxTmp = (BoxTmp)oin.readObject(); // 没有强制转换到Person类型
oin.close();
System.out.println(newBoxTmp);
}catch(Exception ex){
ex.printStackTrace();
}
}
}
运行结果:

Externalizable接口中的writeExternal和readExternal 方法可以用来自定义实现值的传递,覆盖等其他操作。
同时构造方法被执行了,说明这个类是被new出来后,由你支配。
4. readResolve()方法
import java.io.ObjectStreamException;
import java.io.Serializable; public class Box implements Serializable { public static Box box = new Box(); public Box(){} public Box(String height,String width){
this.height = height;
this.width = width;
} public static Box getInstance() {
if(box == null){
box = new Box("20","10");
}
return box;
} private String width;
private String height; public String getWidth() {
return width;
} public void setWidth(String width) {
this.width = width;
} public String getHeight() {
return height;
} public void setHeight(String height) {
this.height = height;
} private Object readResolve() throws ObjectStreamException {
return Box.box;
}
}
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; public class SerializableDemo {
public static void main(String[] args){
Box myBox = Box.getInstance();
try{
File file = new File("src\\demo\\knowledgepoints\\file\\foo.ser"); //把对象信息写入文件中。
ObjectOutputStream oout = new ObjectOutputStream (new FileOutputStream(file));
oout.writeObject(myBox);
oout.close(); //把对象信息从文件中获取出来。
ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
Box newMyBox = (Box)oin.readObject(); // 没有强制转换到Person类型
oin.close();
System.out.println("newMyBox == myBox : "+(newMyBox == myBox));
}catch(Exception ex){
ex.printStackTrace();
}
}
}
运行结果:

Box类中实现readResolve() 方法可以实现单例对象还是同一个。
参考资料
https://www.cnblogs.com/qq3111901846/p/7894532.html
《Java基础知识》序列化与反序列化详解的更多相关文章
- Java基础知识➣序列化与反序列化(四)
概述 Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据.有关对象的类型的信息和存储在对象中数据的类型. 将序列化对象写入文件之后,可以从文件 ...
- 《Java基础——break与continue用法详解》
Java基础--break与continue用法详解 1. break语句: 规则: 1. 仅用于循环语句和switch语句当中,用于跳出循环. 2. 当只有一层循环时,则直接跳出循环,不 ...
- 【Java基础】序列化与反序列化深入分析
一.前言 复习Java基础知识点的序列化与反序列化过程,整理了如下学习笔记. 二.为什么需要序列化与反序列化 程序运行时,只要需要,对象可以一直存在,并且我们可以随时访问对象的一些状态信息,如果程序终 ...
- JAVA基础之——序列化和反序列化
1 概念 序列化,将java对象转换成字节序列的过程. 反序列化,将字节序列恢复成java对象的过程. 2 为什么要序列化? 2.1 实现数据持久化,当对象创建后,它就会一直在,但是在程序终止时,这个 ...
- Java基础篇(JVM)——字节码详解
这是Java基础篇(JVM)的第一篇文章,本来想先说说Java类加载机制的,后来想想,JVM的作用是加载编译器编译好的字节码,并解释成机器码,那么首先应该了解字节码,然后再谈加载字节码的类加载机制似乎 ...
- Java中的序列化Serialable高级详解
来自[http://blog.csdn.net/jiangwei0910410003/article/details/18989711] 引言 将 Java 对象序列化为二进制文件的 Java 序列化 ...
- [java基础] 002 - 位运算符的详解和妙用
一:位运算符详解 位运算符主要用来对操作数二进制的位进行运算.按位运算表示按每个二进制位(bit)进行计算,其操作数和运算结果都是整型值. Java 语言中的位运算符分为位逻辑运算符和位移运算符两类, ...
- JAVA基础之序列化与反序列化
序列化和反序列化: 把对象转化为字节序列的过程称为序列化: 把字节序列恢复为对象的过程称为对象的反序列化: 方法: Java.io.ObjectOutputStream代表对象的输出流,writeOb ...
- vue.js基础知识篇(6):组件详解
第11章:组件详解 组件是Vue.js最推崇也最强大的功能之一,核心目标是可重用性. 我们把组件代码按照template.style.script的拆分方式,放置到对应的.vue文件中. 1.注册 V ...
随机推荐
- Java中的工具类究竟如何命名?
先来几个例子 JDK自带工具类 Arrays.asList(); Objects.equals(); Collections.sort(); Spring框架工具类 StringUtils.isEmp ...
- Linux配置SSH和Xshell连接服务器
>>>>>Ubuntu安装和配置ssh教程 SSH分为客户端 openssh-client 和服务器 openssh-server,可以利用以下命令确认电脑 上是否安装了 ...
- pycham永久激活及conda环境部署
1.pycham安装 一般不选择最新版本,我用的是2018.3,选择 Professional专业版 1.1 官网地址: https://www.jetbrains.com/pycharm/downl ...
- scrapy 五大核心组件-分页
scrapy 五大核心组件-分页 分页 思路 总的原理和之前是一样的,但是由于框架的原因,要遵循他框架的使用方式,每次更改他的url,并指定回调函数 # -*- coding: utf-8 -*- i ...
- 【并发编程】Object的wait、notify和notifyAll方法
本博客系列是学习并发编程过程中的记录总结.由于文章比较多,写的时间也比较散,所以我整理了个目录贴(传送门),方便查阅. 并发编程系列博客传送门 方法简介 wait方法 wait方法是Object类中的 ...
- JS的静态类型检测,有内味儿了
我们知道 TypeScript 2.3 以后的版本支持使用--checkJs对.js文件进行类型检查和错误提示. 但是由于 JavaScript 是弱类型语言,在编写代码的时候,是无法检测变量的类型的 ...
- nginx、redis在Centos7中,加入开机自启动
nginx加入开机启动 cat <<EOF >> /etc/systemd/system/nginx.service> [Unit]> Description=ng ...
- Python一秒搭建ftp服务器,帮助你在局域网共享文件【华为云技术分享】
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/devcloud/article/detai ...
- Frida用法之函数操作
Frida接口功能介绍 Frida是个so级别的hook框架,它可以帮助开发.安全人员对指定的进程的so模块进行分析.它主要提供了功能简单的Python接口和功能丰富的JS接口,使得hook函数和 ...
- git 使用详解(9)-- 分支的新建与合并 git branch -d、merge、 --merged/--no-merged/-v
现在让我们来看一个简单的分支与合并的例子,实际工作中大体也会用到这样的工作流程: 开发某个网站. 为实现某个新的需求,创建一个分支. 在这个分支上开展工作. 假设此时,你突然接到一个电话说有个很严重的 ...