Java序列化(一)

 Java序列化,一个日常开发中比较少用到的技术。正常情况下,JVM启动后,我们可以创建对象生存其内,JVM关闭后,我们创建过的对象都随之销毁,资源释放。但有些时候可能要求在JVM停止之后,某些对象需要保存起来,以便将来再重新读取它们。举个例子,应用服务器的HttpSession对象,Session是指浏览器与服务器之间的一次会话,对应的是服务器中的一个Session对象,而客户端中保存一个jsessionid,这里回忆一下Session的知识,之前有文章详细的解析过Session,见Session深度解析。那么当某种情况下,我们不得不重启服务器的时候,就需要把之前所有的Session对象保存起来,服务器重启之后,将这些Session对象再重新加载过来,这样避免了之前浏览器与服务器建立的会话失效,在浏览器那看来,就好象服务器没有关闭过一样(假设服务器重启期间用户没有操作)。这就用到了Java序列化技术,关于这个例子,我们可以拿Tomcat来测试一下,注意要用正常的手段来关闭服务器(shutdown.bat),而非强制关闭,强制关闭没有序列化的过程。下面来看一个序列化的程序。        首先创建一个可序列化的JavaBean类,Name.java。

import java.io.Serializable;

/**
 * 可序列化的类,需要实现Serializable接口
 * @author 爽
 *
 */
public class Name implements Serializable {

	private String firstname;
	
	private String lastname;
	
	public Name() {
		System.out.println("无参构造器");
	}

	public Name(String firstname, String lastname) {
		System.out.println("全参构造器");
		this.firstname = firstname;
		this.lastname = lastname;
	}

	public String getFirstname() {
		return firstname;
	}

	public void setFirstname(String firstname) {
		this.firstname = firstname;
	}

	public String getLastname() {
		return lastname;
	}

	public void setLastname(String lastname) {
		this.lastname = lastname;
	}
	
	@Override
	public String toString() {
		return "我的名字是" + firstname + "," + lastname;
	}
	
}

       再实现一个序列化的工具类,Serializations.java。

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

/**
 * 序列化工具类
 * @author 爽
 *
 */
public class Serializations {

	/**
	 * 序列化对象到指定路径文件
	 * @param outPath 文件路径
	 * @param outObj 需要序列化的对象
	 * @throws IOException 当I/O发生异常时
	 */
	public static void serialize(String outPath, Object outObj) throws IOException {
		ObjectOutputStream oos = null;
		try {
			oos = new ObjectOutputStream(new FileOutputStream(outPath));
			oos.writeObject(outObj);
		} finally {
			if(oos != null)    oos.close();
		}
	}
	
	/**
	 * 从文件中逆序列化出对象
	 * @param inPath 文件路径
	 * @return 你序列化出的对象
	 * @throws IOException 当I/O发生异常时
	 * @throws ClassNotFoundException 当文件中不存在序列化的对象时
	 */
	public static Object deserialize(String inPath) throws IOException, ClassNotFoundException {
		ObjectInputStream ois = null;
		try {
			ois = new ObjectInputStream(new FileInputStream(inPath));
	        return ois.readObject();
		} finally {
			if(ois != null)    ois.close();
		}
	}
}

       最后创建两个个测试类,来使用一下序列化方法和逆序列化方法,WriteObject.java。

import java.io.IOException;

public class WriteObject {

	public static void main(String[] args) throws IOException {
		Name name = new Name("科比", "布莱恩特");
		Serializations.serialize(args[0], name);
	}

}

       运行后,指定目录下会生成相应文件,其内包含了name对象信息。

       ReadObject.java。

import java.io.IOException;

public class ReadObject {

	public static void main(String[] args) throws ClassNotFoundException, IOException {
		Object obj = Serializations.deserialize(args[0]);
		System.out.println(obj);
	}

}

       运行后,输出:

我的名字是科比,布莱恩特

       我们成功的将name对象序列化到了指定文件中,并且通过逆序列化得到一个和原对象属性相同的对象。注意,逆序列化出的对象没有使用该对象的构造器(由输出结果可以证明),并且和原对象不相等。对象的默认序列化机制:序列化时,对象的类、类的签名,以及类及其所有超类型的非瞬态(non-transient)和非静态(non-static)字段的值都将被写入。逆序列化时,对象的类、类的签名,以及类及其所有超类型的非瞬态(non-transient)和非静态(non-static)字段的值都将被读取。如果我们想某个成员变量不被序列化,可以在其前面加入transient关键字。如:

private transient String lastname;

       如果对象所属类在对象序列化之后做了修改,比如修改属性名称、类型、修饰符等等,再次逆序列化就会发生异常,如我们将lastname前加入transient,使用ReadObject.java进行逆序列化, 将会抛出如下异常:

Exception in thread "main" java.io.InvalidClassException: Name; local class incompatible: stream classdesc serialVersionUID = 3999552307707967101, local class serialVersionUID = -4860856635192050881
	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:604)
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1601)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1514)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1750)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1347)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:369)
	at Serializations.deserialize(Serializations.java:43)
	at ReadObject.main(SerializationsTest.java:14)

       异常的大概描述是说流中的类的版本号和本地类的版本号不一致,这里要引入一个序列化版本号(serialVersionUID)的概念,serialVersionUID是一个64位的值,在类中需要声明为private static final long,它可以人为来维护,也可以通过JVM实现的算法来生成,安装JDK后,可以通过%JAVA_HOME%/bin/serialver.exe来生成serialVersionUID。在逆序列化时,会将从对象流中读取的类信息和当前classpath下的相应类的类信息(Name.class)进行比对,比对的媒介就是serialVersionUID,如果对象中没有声明serialVersionUID,那么该值就会通过默认的算法生成,两端不一致时,就会抛出上面的异常,逆序列化失败。

       当编写一个可序列化的类时(Name.java),可以给serialVersionUID赋一个即简单又易理解的值,如:

private static final long serialVersionUID = 1L;

       如果对该类进行了更改,可能需要同时更新serialVersionUID,如:

private static final long serialVersionUID = 2L;

       但有时我们可能即使更改了类之后,仍然要保持之前序列化的可逆性,也就是对之前的序列化文件做个兼容,那么就不能更新serialVersionUID的值,这时更改前生成的序列化文件依然可逆序列化,那么其更新的字段会以字段类型的预设值逆序列化,避开不兼容的问题。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏积累沉淀

Java类加载原理机制

1.类的加载过程 JVM将类加载过程分为三个步骤:装载(Load),链接(Link)和初始化(Initialize)链接又分为三个步骤,如下图所示: ? 1...

19810
来自专栏前端架构与工程

jQuery源码——.html()方法原理解析

在将字符串转化为html碎片时,一般会将字符串作为容器的innerHTML属性赋值。但innerHTML有很多局限性,比如我们想转化的字符串中有<script>...

1958
来自专栏流媒体人生

shell中的括号(小括号,中括号,大括号)

    ①命令组。括号中的命令将会新开一个子shell顺序执行,所以括号中的变量不能够被脚本余下的部分使用。括号中多个命令之间用分号隔开,最后一个命令可以没有...

731
来自专栏Java帮帮-微信公众号-技术文章全总结

JavaWeb03-轻松理解JS(Java真正的全栈开发)

? 一.js常用对象 ljs中的常见对象有以下几个: Boolean Number String Array 数组 Date 日期 Math 数学 RegEx...

26612
来自专栏java一日一条

Java虚拟机体系结构,你知道吗?

众所周知,Java支持平台无关性、安全性和网络移动性。而Java平台由Java虚拟机和Java核心类所构成,它为纯Java程序提供了统一的编程接口,而不管下层操...

701
来自专栏开心的学习之路

JavaScript概览

近日的项目需要用JavaScript完成,于是决定通读《JavaScript高级程序设计第三版》,书是2012年的,比较老了,但是可以用来快速了解JavaScr...

2884
来自专栏java一日一条

Java虚拟机体系结构,你知道吗?

众所周知,Java支持平台无关性、安全性和网络移动性。而Java平台由Java虚拟机和Java核心类所构成,它为纯Java程序提供了统一的编程接口,而不管下层操...

562
来自专栏MasiMaro 的技术博文

VC 在调用main函数之前的操作

title: VC 在调用main函数之前的操作 tags: [VC++, 反汇编, C++实现原理] date: 2018-09-16 10:36:23 ...

1012
来自专栏小灰灰

Shell学习笔记

1. 变量 声明变量 以 a-zA-Z 开头,不包含特殊字符 等号两边没有空格 不与保留字符重名 PATH="/user/yihui" 使用 变量前加 $ 符号...

17910
来自专栏微信公众号:Java团长

Java虚拟机体系结构

  众所周知,Java支持平台无关性、安全性和网络移动性。而Java平台由Java虚拟机和Java核心类所构成,它为纯Java程序提供了统一的编程接口,而不管下...

822

扫码关注云+社区