Java对象的序列化(Serialization)和反序列化详解

Java对象的序列化(Serialization)和反序列化详解1.序列化和反序列化序列化(Serialization)是将对象的状态信息转化为可以存储或者传输的形式的过程,一般将一个对象存储到一个储存媒介,例如档案或记忆体缓冲等,在网络传输过程中,可以是字节或者XML等格式;而字节或者XML格式的可以还原成完全相等的对象,这个相反的过程又称为反序列化;2.Java对象的序列化和反序列化在Java中,我们可以通过多种方式来创建对象,并且只要对象…

大家好,又见面了,我是你们的朋友全栈君。

####1.序列化和反序列化
序列化(Serialization)是将对象的状态信息转化为可以存储或者传输的形式的过程,一般将一个对象存储到一个储存媒介,例如档案或记忆体缓冲等,在网络传输过程中,可以是字节或者XML等格式;而字节或者XML格式的可以还原成完全相等的对象,这个相反的过程又称为反序列化;

####2.Java对象的序列化和反序列化
在Java中,我们可以通过多种方式来创建对象,并且只要对象没有被回收我们都可以复用此对象。但是,我们创建出来的这些对象都存在于JVM中的堆(heap)内存中,只有JVM处于运行状态的时候,这些对象才可能存在。一旦JVM停止,这些对象也就随之消失;

但是在真实的应用场景中,我们需要将这些对象持久化下来,并且在需要的时候将对象重新读取出来,Java的序列化可以帮助我们实现该功能。

对象序列化机制(object serialization)是java语言内建的一种对象持久化方式,通过对象序列化,可以将对象的状态信息保存未字节数组,并且可以在有需要的时候将这个字节数组通过反序列化的方式转换成对象,对象的序列化可以很容易的在JVM中的活动对象和字节数组(流)之间进行转换。

在JAVA中,对象的序列化和反序列化被广泛的应用到RMI(远程方法调用)及网络传输中;

####3.序列化及反序列化相关接口及类
Java为了方便开发人员将java对象序列化及反序列化提供了一套方便的API来支持,其中包括以下接口和类:

java.io.Serializable
java.io.Externalizable
ObjectOutput
ObjectInput
ObjectOutputStream
ObjectInputStream

####4.Serialization接口详解
Java类通过实现java.io.Serialization接口来启用序列化功能,未实现此接口的类将无法将其任何状态或者信息进行序列化或者反序列化。可序列化类的所有子类型都是可以序列化的。序列化接口没有方法或者字段,仅用于标识可序列化的语义。

当试图对一个对象进行序列化时,如果遇到一个没有实现java.io.Serialization接口的对象时,将抛出NotSerializationException异常。

如果要序列化的类有父类,要想将在父类中定义过的变量序列化下来,那么父类也应该实现java.io.Serialization接口。

下面是一个实现了java.io.Serialization接口的类:

package common.lang;

import java.io.Serializable;

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

public class User1 implements Serializable{

	private String name;
	private int age;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	
	@Override
	public String toString() {
		return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
								   .append("name", name)
								   .append("age", age)
								   .toString();
	}
}

通过下面的代码进行序列化及反序列化:

package common.lang;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SerializableDemo1 {

	public static void main(String[] args) throws Exception, IOException {
		//初始化对象
		User1 user = new User1();
        user.setName("yaomy");
        user.setAge(23);
        System.out.println(user);
        //序列化对象到文件中
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("template"));
        oos.writeObject(user);
        oos.close();
        //反序列化
        File file = new File("template");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
        User1 newUser = (User1)ois.readObject();
        System.out.println(newUser.toString());
	}
}

####5.Java还提供了另一个序列化接口java.io.Externalizable
为了了解Externalizable接口和Serializable接口的区别先来看代码,我们将上面的User1类改为实现java.io.Externalization接口;

package common.lang;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

public class User1 implements Externalizable{

	private String name;
	private int age;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	
	@Override
	public String toString() {
		return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
								   .append("name", name)
								   .append("age", age)
								   .toString();
	}
	@Override
	public void writeExternal(ObjectOutput out) throws IOException {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
		// TODO Auto-generated method stub
		
	}
}

package common.lang;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SerializableDemo1 {

	public static void main(String[] args) throws Exception, IOException {
		//初始化对象
		User1 user = new User1();
        user.setName("yaomy");
        user.setAge(23);
        System.out.println(user);
        //序列化对象到文件中
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("template"));
        oos.writeObject(user);
        oos.close();
        //反序列化
        File file = new File("template");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
        User1 newUser = (User1)ois.readObject();
        System.out.println(newUser.toString());
        ois.close();
	}
}

输出结构是:

common.lang.User1@6ef64f64[
  name=yaomy
  age=23
]
common.lang.User1@184c9860[
  name=<null>
  age=0
]

通过上面的实例可以发现,对User1进行序列化然后再进行反序列化之后对象的属性都恢复成了默认值,也就是说之前的哪个对象的状态并没有被持久化下来,这就是Externalization和Serialization接口之间的区别;

Externalizable继承了Serializable,该接口中定义了两个抽象方法:writeExternal()与readExternal()。当使用Externalizable接口来进行序列化与反序列化的时候需要开发人员重写writeExternal()与readExternal()方法。由于上面的代码中,并没有在这两个方法中定义序列化实现细节,所以输出的内容为空。还有一点值得注意:在使用Externalizable进行序列化的时候,在读取对象时,会调用被序列化类的无参构造器去创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中。所以,实现Externalizable接口的类必须要提供一个public的无参的构造器。

按照要求修改之后的代码是:

package common.lang;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

public class User1 implements Externalizable{

	private String name;
	private int age;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	
	@Override
	public String toString() {
		return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
								   .append("name", name)
								   .append("age", age)
								   .toString();
	}
	@Override
	public void writeExternal(ObjectOutput out) throws IOException {
		out.writeObject(name);
		out.writeInt(age);
		
	}
	@Override
	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
		name = (String)in.readObject();
		age = in.readInt();
		
	}
}

package common.lang;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SerializableDemo1 {

	public static void main(String[] args) throws Exception, IOException {
		//初始化对象
		User1 user = new User1();
        user.setName("yaomy");
        user.setAge(23);
        System.out.println(user);
        //序列化对象到文件中
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("template"));
        oos.writeObject(user);
        oos.close();
        //反序列化
        File file = new File("template");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
        User1 newUser = (User1)ois.readObject();
        System.out.println(newUser.toString());
        ois.close();
	}
}

输出结果是:

common.lang.User1@6cd66725[
  name=yaomy
  age=23
]
common.lang.User1@19160e64[
  name=yaomy
  age=23
]

这样就可以将之前的对象状态保存下来了,如果User类中没有无参数的构造函数,在运行时会抛出异常:java.io.InvalidClassException;

####6.静态变量的序列化

静态变量序列化代码:

public class Test implements Serializable {
 
    private static final long serialVersionUID = 1L;
 
    public static int staticVar = 5;
 
    public static void main(String[] args) {
        try {
            //初始时staticVar为5
            ObjectOutputStream out = new ObjectOutputStream(
                    new FileOutputStream("result.obj"));
            out.writeObject(new Test());
            out.close();
 
            //序列化后修改为10
            Test.staticVar = 10;
 
            ObjectInputStream oin = new ObjectInputStream(new FileInputStream(
                    "result.obj"));
            Test t = (Test) oin.readObject();
            oin.close();
             
            //再读取,通过t.staticVar打印新的值
            System.out.println(t.staticVar);
             
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

main 方法中,将对象序列化后,修改静态变量的数值,再将序列化对象读取出来,然后通过读取出来的对象获得静态变量的数值并打印出来,打印出来的是10还是5?

最后的输出是 10,对于无法理解的读者认为,打印的 staticVar 是从读取的对象里获得的,应该是保存时的状态才对。之所以打印 10 的原因在于序列化时,并不保存静态变量,这其实比较容易理解,序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量。

####7.Transient 关键字使用
Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。

参考:Java序列化高级进阶
参考:深入分析Java的序列化和反序列化

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/149402.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • gg 修改器游戏被保护_GG修改器sky光遇脚本下载app_GG修改器光遇脚本2020最新版下载 安卓版 V411.41.41…

    gg 修改器游戏被保护_GG修改器sky光遇脚本下载app_GG修改器光遇脚本2020最新版下载 安卓版 V411.41.41…GG修改器sky光遇脚本是一款知名的手机游戏修改软件,这款手机软件十分强劲,拥有它你能随时一键游戏修改值,加快手机游戏这些作用,使你在这儿感受到一站式的游戏辅助软件感受。这个脚本app不需要root权限就可以快速运行,有需要的朋友们赶紧来下载GG修改器sky光遇脚本2020最新版app吧。GG修改器sky光遇脚本app特色1.本专用工具永久免费、没有广告、绝对安全可靠;2.根据改动已经运作的游戏里…

  • 操作系统存储管理之虚拟存储与分页式虚拟存储系统

    操作系统存储管理之虚拟存储与分页式虚拟存储系统虚拟存储实现思路在实际运行过程,把有关作业的全部信息都装入主存储器后,作业执行时实际上不是同时使用全部信息的,有些部分运行一遍便再也不用,甚至有些部分在作业执行的整个过程中都不会被使用到(如错误处理部分)。进程在运行时不用的,或暂时不用的,或某种条件下才用的程序和数据,全部驻留于内存中是对宝贵的主存资源的一种浪费,大大降低了主存利用率。于是,提出了这样的问题:作业提交时,先全部进入辅助存储器,作

  • php mysql 经纬度_mysql,php和js根据经纬度计算距离

    php mysql 经纬度_mysql,php和js根据经纬度计算距离根据经纬度计算距离公式图片来自互联网对上面的公式解释如下:Lung1Lat1表示A点经纬度,Lung2Lat2表示B点经纬度;a=Lat1–Lat2为两点纬度之差b=Lung1-Lung2为两点经度之差;6378.137为地球半径,单位为千米;计算出来的结果单位为千米,若将半径改为米为单位则计算的结果单位为米。计算精度与谷歌地图的距离精度差不多,相差范围在0.2米以下。参数说明…

  • win11游戏性能提升_win10怎么设置显卡高性能

    win11游戏性能提升_win10怎么设置显卡高性能其实打造一款高性能系统主要要做到以下三点:一、安装纯净的原版系统。二、打开系统的高性能模式,对部分设置进行优化。三、养成良好的使用习惯,在安装软件时,避免某些引起系统卡顿的软件和全家桶、广告软件。安装完纯净版的windows11系统后,可以进行一下的设置优化:一、切换高性能电源计划二、调整性能选项三、调整隐私安全常规设置四、调整隐私安全诊断和反馈设置五、关闭隐私安全搜索权限六、关闭游戏模式七、任务栏设置一、切换高性能电源计划打开系统“开始菜单->所有应用->windows工具

  • 别再写满屏的try-catch了,真丑,全局异常处理不会吗?

    点击上方“全栈程序员社区”,星标公众号 重磅干货,第一时间送达 作者:巨人大哥 www.cnblogs.com/jurendage/p/11255197.html 本文讲得比较细,…

  • http错误状态码_HTTP常用的14种状态码

    http错误状态码_HTTP常用的14种状态码一些常见的状态码为:200-服务器成功返回网页404-请求的网页不存在503-服务不可用详细分解:1xx(临时响应)表示临时响应并需要请求者继续执行操作的状态代码。代码说明100(继续)请求者应当继续提出请求。服务器返回此代码表示已收到请求的第一部分,正在等待其余部分。101(切换协议)请求者已要求服务器切换协议,服务器已确认并准备切换。…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号