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的序列化和反序列化

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

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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Spring的基本业务流程与类的多实现

    Spring的基本业务流程与类的多实现Spring的基本业务流程与类的多实现

    2022年4月22日
    59
  • OpenWrt配置阿里云动态域名服务DDNS

    OpenWrt配置阿里云动态域名服务DDNSOpenWrt配置阿里云动态域名服务DDNSOpenWrt配置阿里云动态域名服务DDNS创建AccessKey添加权限创建A记录设置OpenWrtDDNS验证OpenWrt配置阿里云动态域名服务DDNSDDNS(DynamicDomainNameServer,动态域名服务)是将用户的动态IP地址映射到一个固定的域名解析服务上,用户每次连接网络的时候客户端程序就会通过信息传递把该主机的动态IP地址传送给位于服务商主机上的服务器程序,服务器程序负责提供DNS服务并实现动态域名解析。创建Acce

    2022年4月30日
    894
  • UPX脱壳详细分析

    UPX脱壳详细分析文章标题】:UPX脱壳详细分析【文章作者】:index09【使用工具】:UPX+OD+Stud_PE+ImportREC——————————————————————————–【详细过程】又被R公司鄙视了,每次都被相同的理由鄙视。哭……于是决定好好学一下逆向了。首先做个幼儿级的脱壳练习,当做开始吧。网上有很多类似文章,基本只写了找OEP的过程,这里稍加分

    2022年7月19日
    17
  • request jsonify

    request jsonifypython的flask框架为用户提供了直接返回包含json格式数据响应的方法,即jsonify,在开发中会经常用到。如下一段简单的flask后端代码,服务端视图函数根据请求参数返回json格式的数据到客户端。转载于:https://www.cnblogs.com/daqingzi/p/9018283.html…

    2022年5月24日
    43
  • MySQL的JDBC连接

    MySQL的JDBC连接MySQL的JDBC连接MySQL的JDBC概念MySQL的JDBCJDBC添加数据封装连接工具更新数据和事务删除数据查询数据MySQL的JDBC概念JDBC是JavaDatabaseConnective的缩写,表示使用Java去连接数据库进行数据操作的过程MySQL的JDBC创建动态项目-以eclipse为例,首先要创建动态项目连接开发包(在www.mvnrepositor…

    2022年7月17日
    13
  • cadence快捷键大全(work bertrand russell)

    一、File相关Ctrl+Shift+N:新建一个窗口,即新打开一个ericCtrl+N:新建一个窗口,即编辑代码的窗口Ctrl+O:打开文件Ctrl+Shift+S:另存为Ctrl+Q:关闭eric二、Edit相关Ctrl+Z:撤回Ctrl+Shift+Z:撤回上次的撤回(你懂得,斜眼笑)Ctrl+Y:恢复到上次保存的状态Alt+Shift+C:清楚当前代…

    2022年4月16日
    87

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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