《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 ...
随机推荐
- 堆 堆排序 优先队列 图文详解(Golang实现)
引入 在实际应用中,我们经常需要从一组对象中查找最大值或最小值.当然我们可以每次都先排序,然后再进行查找,但是这种做法效率很低.哪么有没有一种特殊的数据结构,可以高效率的实现我们的需求呢,答案就是堆( ...
- Mybaits学习总结
一.Mybatis介绍 邮箱:1727292697 MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的 ...
- Django--导出项目依赖库requirements.txt
虚拟环境下: 1.导出项目依赖库: pip freeze > requirements.txt 2.使用 pip 一次性安装项目的所有依赖项,方法是在命令行中输入: pip install - ...
- MySQL 、PDO对象
目录 1, singleton 2, pdo与db 3, singleton获取pdo 4, pdo实现db增删改查 5, pdo异常处理exception 6, pdo预处理prepare 7, p ...
- Stream系列(七)distinct方法使用
EmployeeTestCase.java package com.example.demo; import lombok.Data; import lombok.ToString; import l ...
- 使用 buildx 构建多平台 Docker 镜像
原文链接:使用 buildx 构建多平台 Docker 镜像 在工作和生活中,我们可能经常需要将某个程序跑在不同的 CPU 架构上,比如让某些不可描述的软件运行在树莓派或嵌入式路由器设备上.特别是 D ...
- Kafka 0.10.0.1 consumer get earliest partition offset from Kafka broker cluster - scala code
Return: Map[TopicPartition, Long] Code: val props = new Properties() props.put(ConsumerConfig.BOOTST ...
- 2019-2020-1 20199304《Linux内核原理与分析》第六周作业
第五章 系统调用的三层机制(下) 1.往MenuOS中添加命令 (1)首先进入LinuxKernel文件夹,将menu目录删除.然后再git clone克隆下载更新了版本之后的menu目录(包含tim ...
- MySql CPU彪高到百分之1000的排查思路
You need to enable JavaScript to run this app. 原文内容来自于LZ(楼主)的印象笔记,如出现排版异常或图片丢失等情况,可查看当前链接:https:// ...
- cf1119d Frets On Fire 前缀和+二分
题目:http://codeforces.com/problemset/problem/1119/D 题意:给一个数n,给出n个数组的第一个数(a[0]=m,a[1]=m+1,a[2]=m+2,... ...