《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 ...
随机推荐
- PostGIS 递归方法
在Oracle数据库中,有可以实现递归的函数 select * from table_name start with [condition1] connect by [condition2] 最近发现 ...
- MySQL锁会不会,你就差看一看
数据库锁知识 不少人在开发的时候,应该很少会注意到这些锁的问题,也很少会给程序加锁(除了库存这些对数量准确性要求极高的情况下),即使我们不会这些锁知识,我们的程序在一般情况下还是可以跑得好好的.因为这 ...
- 安装anaconda后启动终端头部会有(base)如何解决
conda config --show conda config --set auto_activate_base False
- linux+docker+nginx如何配置环境并配置域名访问
一.环境准备 1)下载php环境包,下载地址为:https://www.php.net/downloads.php 2)安装docker,这个主要的作用就是用来安装mysql.你也可以不需要安装这个东 ...
- python模块IO
Python之模块IO io概叙 io模块提供了python用于处理各种类型I/O的主要工具,主要有三种类型的I/O:文本I/O,二进制I/O和原始I/O:这些都是通用类型,各种后备存储可使用其中的每 ...
- Linux机器相互登录
1周第4次课(3月22日)课程内容: 1.16 Linux机器相互登录 Linux相互登录可以分2种方式,一种为ssh +IP地址,然后输入对应的root密码,一种为密钥验证方式,其中一台机器放公钥, ...
- 【网络流相关】最大流的Dinic算法实现
Luogu P3376 于\(EK\)算法求最大流时每一次只求一条增广路,时间复杂度会比较高.尽管实际应用中表现比较优秀,但是有一些题目还是无法通过. 那么我们就会使用\(Dinic\)算法实现多路增 ...
- variable fonts - 更小更灵活的字体
原文链接 variable fonts(下文中vf为缩写)是数字时代制作的字体技术,用更小的文件大小在web上提供更丰富的排版,但是一项新的技术往往伴随着新的挑战和复杂未知的情况.不过,我们要拥抱技术 ...
- python3如何随机生成大数据存储到指定excel文档里
本次主要采用的是python3的第三方库xlwt,来创建一个excel文件.具体步骤如下: 1.确认存储位置,文件命名跟随时间格式 2.封装写入格式 3.实现随机数列生成 4.定位行和列把随机数写入 ...
- CoderForces-913D
You are preparing for an exam on scheduling theory. The exam will last for exactly Tmilliseconds and ...