序列化的作用:为了不同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基础知识》序列化与反序列化详解的更多相关文章

  1. Java基础知识➣序列化与反序列化(四)

    概述 Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据.有关对象的类型的信息和存储在对象中数据的类型. 将序列化对象写入文件之后,可以从文件 ...

  2. 《Java基础——break与continue用法详解》

    Java基础--break与continue用法详解       1. break语句: 规则: 1. 仅用于循环语句和switch语句当中,用于跳出循环. 2. 当只有一层循环时,则直接跳出循环,不 ...

  3. 【Java基础】序列化与反序列化深入分析

    一.前言 复习Java基础知识点的序列化与反序列化过程,整理了如下学习笔记. 二.为什么需要序列化与反序列化 程序运行时,只要需要,对象可以一直存在,并且我们可以随时访问对象的一些状态信息,如果程序终 ...

  4. JAVA基础之——序列化和反序列化

    1 概念 序列化,将java对象转换成字节序列的过程. 反序列化,将字节序列恢复成java对象的过程. 2 为什么要序列化? 2.1 实现数据持久化,当对象创建后,它就会一直在,但是在程序终止时,这个 ...

  5. Java基础篇(JVM)——字节码详解

    这是Java基础篇(JVM)的第一篇文章,本来想先说说Java类加载机制的,后来想想,JVM的作用是加载编译器编译好的字节码,并解释成机器码,那么首先应该了解字节码,然后再谈加载字节码的类加载机制似乎 ...

  6. Java中的序列化Serialable高级详解

    来自[http://blog.csdn.net/jiangwei0910410003/article/details/18989711] 引言 将 Java 对象序列化为二进制文件的 Java 序列化 ...

  7. [java基础] 002 - 位运算符的详解和妙用

    一:位运算符详解 位运算符主要用来对操作数二进制的位进行运算.按位运算表示按每个二进制位(bit)进行计算,其操作数和运算结果都是整型值. Java 语言中的位运算符分为位逻辑运算符和位移运算符两类, ...

  8. JAVA基础之序列化与反序列化

    序列化和反序列化: 把对象转化为字节序列的过程称为序列化: 把字节序列恢复为对象的过程称为对象的反序列化: 方法: Java.io.ObjectOutputStream代表对象的输出流,writeOb ...

  9. vue.js基础知识篇(6):组件详解

    第11章:组件详解 组件是Vue.js最推崇也最强大的功能之一,核心目标是可重用性. 我们把组件代码按照template.style.script的拆分方式,放置到对应的.vue文件中. 1.注册 V ...

随机推荐

  1. ansible start canal

    - name: Start canal server shell: source /etc/profile && nohup /opt/canal/bin/startup.sh

  2. RocketMQ一个新的消费组初次启动时从何处开始消费呢?

    目录 1.抛出问题 1.1 环境准备 1.2 消息发送者代码 1.3 消费端验证代码 2.探究CONSUME_FROM_MAX_OFFSET实现原理 2.1 CONSUME_FROM_LAST_OFF ...

  3. (三)OpenStack---M版---双节点搭建---Keystone安装和配置

    ↓↓↓↓↓↓↓↓视频已上线B站↓↓↓↓↓↓↓↓ >>>>>>传送门 1.创建keystone数据库 2.创建随机密码作为管理员令牌 3.安装openstack-ke ...

  4. Amazon Lightsail部署LAMP应用程序之部署实验室基础架构

    一.在Lightsail中创建LAMP堆栈实例 1.在AWS管理控制台的"服务"下拉选项中单击"Lightsail". 2.在语言方面选择 "英语&q ...

  5. 手动模拟实现Spring IOC功能(基于javaConfig风格)

    以下文中spring特指spring frameWork项目,不含其它:如spring cloud等. 作为刚开始研究spring源码的小白,对于spring两大核心功能之一的IOC,虽说大致了解了B ...

  6. NIO-概览

    目录 NIO-概览 目录 前言 什么是NIO 通道 缓冲区 选择器 其他 管道 FileLock 参考文档 NIO-概览 目录 NIO-概览 前言 本来是想学习Netty的,但是Netty是一个NIO ...

  7. java学习引言

    Java学习之路:不走弯路,就是捷径 0.引言 软件开发之路是充满荆棘与挑战之路,也是充满希望之路.Java学习也是如此,没有捷径可走.梦想像<天龙八部>中虚竹一样被无崖子醍醐灌顶而轻松获 ...

  8. 【Android - 自定义View】之MeasureSpec简介

    MeasureSpec是View测量过程中的一个重要的类,它被用来将View的尺寸规格(SpecSize)和尺寸模式(SpecMode)封装在一起,并提供打包和解包的方法. MeasureSpec虽然 ...

  9. Django如何启动源码分析

    Django如何启动源码分析 启动 我们启动Django是通过python manage.py runsever的命令 解决 这句话就是执行manage.py文件,并在命令行发送一个runsever字 ...

  10. Linux集群介绍、keepalived介绍及配置高可用集群

    7月3日任务 18.1 集群介绍18.2 keepalived介绍18.3/18.4/18.5 用keepalived配置高可用集群扩展heartbeat和keepalived比较http://blo ...