`
coolxing
  • 浏览: 870061 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
9a45b66b-c585-3a35-8680-2e466b75e3f8
Java Concurre...
浏览量:95952
社区版块
存档分类
最新评论

java序列化机制

阅读更多

[coolxing按: 转载请注明作者和出处, 如有谬误, 欢迎在评论中指正.]

 

java的序列化机制支持将对象序列化为本地文件或者通过网络传输至别处, 而反序列化则可以读取流中的数据, 并将其转换为java对象. 被序列化的类需要实现Serializable接口, 使用ObjectInputStream和ObjectOutputStream进行对象的读写操作.

 

当然, java的序列化机制并非如此简单, 以下是个人总结的一些知识点:

 

1. 对象读取的顺序应该和写入的顺序一致, 而且读取的次数不能超过已写入对象的个数. 比如文件中仅仅存在2个对象, 就不能连续调用3次readObject()方法, 除非调用了reset, skip等对流重新定位的方法.

 

2. java序列化机制针对的是对象, 而不是类. 因此只有非静态成员变量才会被序列化成二进制数据.

 

3. 使用transient关键字修饰的成员变量不会被序列化为二进制数据.

 

4. 将对象序列化为二进制数据, 将二进制数据反序列化为java对象, 这两个操作可能位于不同的应用中, 甚至也可能在不同的计算机上进行. 需要保证这两种场合下都有class文件, 在序列化处和反序列处的class文件需要完成一致, 包括包名.

 

5. 序列化ID的作用. 上面的Person类中定义了序列化ID: private static final long serialVersionUID = 1L;

这是一个非强制定义的静态成员, 如果不定义序列化ID, 那么eclipse会给出一个黄色的警告, 这个警告可以忽略. 

考虑这样的情形: Person类定义了序列化ID, 且序列化对象时serialVersionUID的值为1, 而反序列化时serialVersionUID的值不为1, 那么此时将无法反序列化成功. 所以序列化ID可以用来限制某些用户的反序列化.

 

6. 父类的序列化问题. 根据java的对象实例化机制可知, 创建一个子类对象的过程中, 会创建其父类对象, 反序列化也不例外. 如果一个类实现了Serializable接口, 而其父类却没有实现Serializable接口, 那么在反序列化时, 虚拟机会调用父类的无参构造函数创建父类对象, 因此反序列化后父类成员变量的值为调用无参构造函数之后的值. 如果父类既没有实现Serializable接口, 也不存在无参构造函数, 那么在反序列化时将发生程序错误.

假设存在一个没有实现Serializable接口的Male类:

 

public class Male {
	private String name;
	private int age;

	public Male() {

	}

	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 "Male [name=" + name + ", age=" + age + "]";
	}

}

Male的子类MaleStudent则实现了Serializable接口:

 

public class MaleStudent extends Male implements Serializable {
	private int studentID;

	public MaleStudent(int studentID) {
		super();
		this.studentID = studentID;
	}
	
	public int getStudentID() {
		return studentID;
	}

	public void setStudentID(int studentID) {
		this.studentID = studentID;
	}

	@Override
	public String toString() {
		return "MaleStudent [studentID=" + studentID + "]";
	}
}

 

则反序列化MaleStudent对象后, 其name和age属性都发生了改变:

 

MaleStudent student = new MaleStudent(1);
		student.setName("coolxing");
		student.setAge(24);
		ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(
				"male.obj"));
		out.writeObject(student);
		out.close();

		ObjectInputStream in = new ObjectInputStream(new FileInputStream(
				"male.obj"));
		MaleStudent maleStudent = (MaleStudent) in.readObject();
		System.out.println(maleStudent.getName() + ", " + maleStudent.getAge()
				+ ", " + maleStudent.getStudentID());

 

程序的输出为:

 

name = null, age = 0, studentID = 1

可见, 父类属性值都"丢失"了, name和age都是调用Male无参构造函数之后的值. 假设将Male类中的无参构造函数删除, 再加上一个有参的构造函数, 在反序列化时将发生程序错误.

7. 自定义序列化和反序列化操作. 如果为了某些特殊需求, 需要自定义序列化和反序列化操作, 只要重写实现了Serializable接口的类中的writeObject()和readObject()方法即可, 典型的应用场景是对密码之类的敏感成员进行加密:
public class User implements Serializable {
	private static final long serialVersionUID = 1L;
	private String userName;
	private String passWord;

	public User(String userName, String passWord) {
		this.userName = userName;
		this.passWord = passWord;
	}

	public User() {
	}

	private void writeObject(ObjectOutputStream out) {
		try {
			PutField field = out.putFields();
			field.put("userName", userName);
			System.out.println("加密前: passWord = " + passWord);
			// 模拟加密
			passWord = passWord + "1";
			System.out.println("加密后: passWord = " + passWord);
			field.put("passWord", passWord);
			out.writeFields();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	private void readObject(ObjectInputStream in) {
		try {
			GetField field = in.readFields();
			userName = (String) field.get("userName", "");
			passWord = (String) field.get("passWord", "");
			System.out.println("读取的原始passWord = " + passWord);
			// 模拟解密
			passWord = passWord.substring(0, passWord.length() - 1);
			System.out.println("解密后的passWord = " + passWord);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
程序的输出为:
加密前: passWord = 1987810
加密后: passWord = 19878101
读取的原始passWord = 19878101
解密后的passWord = 1987810
User [userName=coolxing, passWord=1987810]

8. 重复存储问题. 如果将同一个对象多次写入文件, 会有怎样的结果?
File file = new File("user.obj");
		User user = new User("coolxing", "1987810");
		ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("user.obj"));
		out.writeObject(user);
		System.out.println(file.length());
		// 改变userName的值后再次将user对象存入文件
		user.setUserName("min");
		out.writeObject(user);
		System.out.println(file.length());
		
		ObjectInputStream in = new ObjectInputStream(new FileInputStream("user.obj"));
		User userFromFile1 = (User) in.readObject();
		User userFromFile2 = (User) in.readObject();
		System.out.println(userFromFile1.toString());
		System.out.println(userFromFile2.toString());
 

程序的输出结果为:

 

109

114

User [userName=coolxing, passWord=1987810]

User [userName=coolxing, passWord=1987810]

程序中调用了2次readObject()方法, 且没有出现程序错误, 由此可知确实向文件中写入了2个对象. 第二次写入user对象时, 系统发现文件中已经存在user对象, 将不再存入user对象的内容, 只写入一个引用和一些控制信息, 所以第二次写入user对象后文件的大小增加的很少, 而且userName也没有发生改变.

 

9. 如果一个类中包含非基本数据类型的成员变量, 那么不仅类本身需要实现Serializable接口, 类中的非基本数据类型也需要实现Serializable接口. java的一些核心类, 如String,

基本数据类型的包装类等都已经实现了Serializable接口, 使用的时候可以查看文档.

 

10. 对于包含集合型成员的类来说, 不仅类本身需要现Serializable接口, 集合中所存储的元素也要实现Serializable接口.

那么集合类(List, Set, Map)到底有没有实现Serializable接口呢? 这是我疑惑的地方, 文档中并没有说明. 可以确定的是,

只要集合中的元素是可序列化的, 序列化过程就不会出错.

 

关于java序列化机制的一些其他方面的叙述, 请参见http://www.ibm.com/developerworks/cn/java/j-5things1/index.html?ca=drs-cn-0504

 

5
3
分享到:
评论
4 楼 liaoqianwen123 2014-06-17  
集合类(List, Set, Map)都有实现Serializable和Cloneable接口的
3 楼 kooubyby 2011-11-13  
   很好好,很强大!继续加油!!
2 楼 coolxing 2011-10-30  
zhufeng1981 写道
讲解很详细,支持一下。

呵呵, 多谢支持
1 楼 zhufeng1981 2011-10-30  
讲解很详细,支持一下。

相关推荐

    Java高级程序设计实战教程第五章-Java序列化机制.pptx

    Java高级程序设计 第5章 Java序列化机制 5.1 应用场景 5.2 相关知识5.3 实施过程 5.4 拓展知识5.5 拓展训练 5.6 课后小结5.7 课后习题 5.8 上机实训 Java高级程序设计实战教程第五章-Java序列化机制全文共15页,当前...

    java序列化实现演示

    Java序列化机制(2)- serialVersionUID 实验 http://blog.csdn.net/suileisl/article/details/16991753

    Java序列化的机制和原理

    JAVA编程的核心知识,让人脑洞大开的利器,原来最基础的东西也就是这样的。

    通过实例了解java序列化机制

    主要介绍了通过实例了解java序列化机制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    java序列化(Serializable)的作用和反序列化.doc

    序列化是干什么的? 简单说就是为了保存在内存中的各种对象的状态(也就是实例...虽然你可以用你自己的各种各样的方法来保 存object states,但是Java给你提供一种应该比你自己好的保存对象状态的机制,那就是序列化。

    java反射机制

    Java反射机制,实现json的序列化和反序列化

    Java SE编程入门教程 java序列化(共14页).pptx

    Java SE编程入门教程 java序列化(共14页).pptx Java SE编程入门教程 java异常(共57页).pptx Java SE编程入门教程 java正则(共8页).pptx Java SE编程入门教程 properties(共3页).pptx Java SE编程入门教程 ...

    Java 对象序列化详解以及实例实现和源码下载

    Java中的序列化机制有两种实现方式: 一种是实现Serializable接口 另一种是实现Externalizable接口 区别: 实现Serializable接口 1 系统自动储存必要的信息 2 Java内建支持,易于实现,只需实现该接口即可,无须任何...

    Java序列化机制与原理的深入分析

    有关Java对象的序列化和反序列化也算是Java基础的一部分,下面对Java序列化的机制和原理进行一些介绍

    Java序列化的机制和原理[归类].pdf

    Java序列化的机制和原理[归类].pdf

    Java序列化与反序列化三种格式存取

    序列化机制在Java中有着广泛的应用,EJB、RMI等技术都是以此为基础的。  正确使用序列化机制  一般而言,要使得一个类可以序列化,只需简单实现java.io.Serializable接口即可(还要实现无参数的构造方法)。该...

    java序列化和反序列化,面试必备

    意义:序列化机制允许将实现序列化的Java对象转换为字节序列,并将字节序列保存在磁盘中,或通过网络传输,以达到以后恢复成原来的对象。序列化机制使地对象可以脱离程序的运行而独立存在。 使用场景:所有在网络上...

    使用Java序列化实现一个简单的文件备份工具.txt

    这段代码实现了一个简单的文件备份工具FileBackupTool,其中使用了Java的序列化机制。在类的构造方法中,首先接收两个参数:源文件路径和备份文件路径。然后实现了一个backup()方法,用于将源文件进行备份。在该方法...

    java中的序列化与反序列化

    序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。

    深入理解Java原生的序列化机制

    Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。下面小编和大家来一起学习一下吧

    Java Socket网络传输的序列化机制

    绍Java Socket网络传输的序列化机制

    Java对象序列化使用基础

    所谓对象序列化就是将对象的状态转换成字节流,以后可以通过这些值再生成相同状态的对象。这个过程也可以通过...像RMI、Socket、JMS、EJB它们中的一种,彼此为什么能够传递Java对象,当然都是对象序列化机制的功劳。

    Java对象的序列化和反序列化实践

    NULL 博文链接:https://snowdymy.iteye.com/blog/1745396

    Spring 实现远程访问详解——httpinvoker

    Spring httpInvoker使用标准java序列化机制,通过Http暴露业务服务。如果你的参数和返回值是比较复杂的,通过httpInvoker有巨大的优势。 1. 远程访问流程 1) 服务端定义服务接口 2) 服务端实现服务接口 3) 暴露服务...

Global site tag (gtag.js) - Google Analytics