JavaSE入门学习46:文件传输基础之I/O流(五)(Java序列化)

六对象的序列化和反序列化

(1)序列化和反序列化概述

Java提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的

数据、有关对象的类型的信息和存储在对象中数据的类型。

将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化,也就是说,对象的类型信息、对

象的数据,还有对象中的数据类型可以用来在内存中新建对象。

整个过程都是Java虚拟机(JVM)独立的,也就是说,在一个平台上序列化的对象可以在另一个完全不同的平台上

反序列化该对象。

(2)序列化与基本类型序列化

1)序列化

将类型int 转换成4byte或将其他数据类型转换成byte的过程叫序列化

数据---->n byte

2)反序列化

将n个byte 转换成一个数据的过程,也就是序列化的反过程。

nbyte ---> 数据

3)RandomAccessFile提供基本类型的读写方法,可以将基本类型数据序列化到文件或者将文件内容反序列化为数

据。

(3)ObjectInputStream类和ObjectOutputStream类

ObjectInputStream类和ObjectOutputStream类是高层次的数据流,它们包含序列化和反序列化对象的方法。

ObjectInputStream类的部分方法:

ObjectOutputStream类的方法:

ObjectOutputStream类包含很多写方法来写各种数据类型,但是一个特别的方法writeObject(Object o)例外。该方

法序列化一个对象,并将它发送到输出流。

相似的ObjectInputStream类包含如下反序列化一个对象的方法readObject() 。该方法从流中取出下一个对象,并

将对象反序列化。它的返回值为Object,因此,你需要将它转换成合适的数据类型。

为了演示序列化在Java中是怎样工作的,我们使用Student类,假设我们定义了如下的Student类,该类实现了

Serializable接口。Student.java源文件代码:

import java.io.*;

public class Student implements Serializable{
	private String stuno;
	private String stuname;
	private int stuage;  

	public Student(String stuno, String stuname, int stuage) {
		super();
		this.stuno = stuno;
		this.stuname = stuname;
		this.stuage = stuage;
	}

	public String getStuno() {
		return stuno;
	}

	public void setStuno(String stuno) {
		this.stuno = stuno;
	}

	public String getStuname() {
		return stuname;
	}

	public void setStuname(String stuname) {
		this.stuname = stuname;
	}

	public int getStuage() {
		return stuage;
	}

	public void setStuage(int stuage) {
		this.stuage = stuage;
	}

        @Override
	public String toString() {
		return "Student [stuno=" + stuno + ", stuname=" + stuname + ", stuage="+ stuage + "]";
	}
}

请注意,一个类的对象要想序列化成功,必须满足两个条件:

1)该类必须实现Serializable接口。

2)该类的所有属性必须是可序列化的。如果有一个属性不是可序列化的,则该属性必须注明是短暂的。

如果你想知道一个Java标准类是否是可序列化的,请查看该类的文档。检验一个类的实例是否能序列化十分简

单, 只需要查看该类有没有实现Serializable接口。

(4)序列化对象和反序列化对象

对象必须实现序列化接口 ,才能进行序列化,否则将出现异常。这个接口,没有任何方法,只是一个标准。

ObjectOutputStream类用来序列化一个对象,如下的SerializeDemo例子实例化了一个Student对象,并将该对象

序列化到一个文件中。该程序执行后,就创建了一个名为student.ser文件。该程序没有任何输出,但是你可以通过代

码研读来理解程序的作用。

注意: 当序列化一个对象到文件时, 按照Java的标准约定是给文件一个.ser扩展名。

示例1:

import java.io.*;

public class ObjectSeriaDemo1 {
	public static void main(String[] args) throws Exception{
		String file = "E:\\Java\\JavaSE\\IO\\demo\\obj.dat";
		//1对象的序列化
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
		Student stu = new Student("10001", "张三", 20);
		oos.writeObject(stu);
		oos.flush();
		oos.close();

		//2对象的反序列化
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
		Student stu1 = (Student)ois.readObject();
		System.out.println(stu1);
		ois.close();
	}
}

运行结果:

这里要注意以下要点:

readObject()方法中的try/catch代码块尝试捕获 ClassNotFoundException异常。对于JVM可以反序列化对象,它

必须是能够找到字节码的类。如果JVM在反序列化对象的过程中找不到该类,则抛出一个ClassNotFoundException异

常。

readObject()方法的返回值被转化成Student引用。

(5)transient关键字

方法声明:

private void writeObject(java.io.ObjectOutputStream s) throws IOException
private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException

经过改写的Student.java源代码;

import java.io.*;

public class Student implements Serializable{
	private String stuno;
	private String stuname;
	//该元素不会进行jvm默认的序列化,也可以自己完成这个元素的序列化
	private transient int stuage;  

	public Student(String stuno, String stuname, int stuage) {
		super();
		this.stuno = stuno;
		this.stuname = stuname;
		this.stuage = stuage;
	}

	public String getStuno() {
		return stuno;
	}

	public void setStuno(String stuno) {
		this.stuno = stuno;
	}

	public String getStuname() {
		return stuname;
	}

	public void setStuname(String stuname) {
		this.stuname = stuname;
	}

	public int getStuage() {
		return stuage;
	}

	public void setStuage(int stuage) {
		this.stuage = stuage;
	}

	@Override
	public String toString() {
		return "Student [stuno=" + stuno + ", stuname=" + stuname + ", stuage="+ stuage + "]";
	}

	private void writeObject(ObjectOutputStream s) throws IOException{
		 s.defaultWriteObject();//把jvm能默认序列化的元素进行序列化操作
		 s.writeInt(stuage);//自己完成stuage的序列化
	 }

	private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException{
		  s.defaultReadObject();//把jvm能默认反序列化的元素进行反序列化操作
		  this.stuage = s.readInt();//自己完成stuage的反序列化操作
	}
}

再次运行结果:和上述的结果一样

关于的两种序列化的性能分析可以看ArrayList源码中序列化和反序列化的问题。我不是很清楚,就不解释了。

(6)序列化中子类和父类构造函数的调用问题

在进行对象序列化和反序列化的时候存在构造函数的是否调用的问题,我们来看下面的实例。

实例:

import java.io.*;

public class ObjectSeriaDemo2 {
	public static void main(String[] args) throws Exception{
		//序列化
		ObjectOutputStream oos=new ObjectOutputStream(
new FileOutputStream("E:\\Java\\JavaSE\\IO\\demo\\obj1.dat"));
		Foo2 foo2 = new Foo2();
		oos.writeObject(foo2);
		oos.flush();
		oos.close();

		System.out.println();
		//反序列化是否递归调用父类的构造函数
		ObjectInputStream ois = newObjectInputStream(
new FileInputStream("E:\\Java\\JavaSE\\IO\\demo\\obj1.dat"));
		Foo2 foo21 = (Foo2)ois.readObject();
		System.out.println(foo21);
		ois.close();

		System.out.println("-------------------");
		//序列化
		ObjectOutputStream oos1 = new ObjectOutputStream(new
FileOutputStream("E:\\Java\\JavaSE\\IO\\demo\\obj1.dat"));
		Bar2 bar2 = new Bar2();
		oos1.writeObject(bar2);
		oos1.flush();
		oos1.close();

		//反序列化
		System.out.println();
		ObjectInputStream ois1 = new ObjectInputStream(new
FileInputStream("E:\\Java\\JavaSE\\IO\\demo\\obj1.dat"));
		Bar2 bar21 = (Bar2)ois1.readObject();
		System.out.println(bar21);
		ois1.close();

		/*
		 * 对子类对象进行反序列化操作时,
		 * 如果其父类没有实现序列化接口
		 * 那么其父类的构造函数会被调用
		 */
	}
}

/*
 *一个类实现了序列化接口,那么其子类都可以进行序列化
 */
class Foo implements Serializable{
	public Foo(){
		System.out.println("foo...");
	}
}

class Foo1 extends Foo{
	public Foo1(){
		System.out.println("foo1...");
	}
}

class Foo2 extends Foo1{
	public Foo2(){
		System.out.println("foo2...");
	}
}

class Bar{
	public Bar(){
		System.out.println("bar");
	}
}

class Bar1 extends Bar{
	public Bar1(){
		System.out.println("bar1..");
	}
}

class Bar2 extends Bar1 implements Serializable{
	public Bar2(){
		System.out.println("bar2...");
	}
}

运行结果:

时间: 2024-10-14 11:14:55

JavaSE入门学习46:文件传输基础之I/O流(五)(Java序列化)的相关文章

JavaSE入门学习45:文件传输基础之I/O流(四)

五字符流的使用 (1)编码问题 这个问题我们早就说过了,不再过多赘述. 参考:JavaSE入门学习42:文件传输基础之I/O流(一) (2)认识文本和文本文件 Java的文本(char)是16位无符号整数,是字符的unicode编码(双字节编码):而文件是byte byte byte ...的数据 :文本文件是文本(char)序列按照某种编码方案(utf-8,utf-16be,gbk等)序列化为byte的存储结果. (3)字符流(Reader Writer)---->操作的是文本文本文件 字符的

JavaSE入门学习43:文件传输基础之I/O流(二)

三RandomAccessFile类的的使用 RandomAccessFile类是java提供的对文件内容的访问类,既可以读文件,也可以写文件.RandomAccessFile类 支持随机访问文件,可以访问文件的任意位置. RandomAccessFile类的构造方法: RandomAccessFile类中的方法: (1)java文件模型 在硬盘上的文件是byte byte byte存储的,是数据的集合. (2)打开文件 有两种模式"rw"(读写),"r"(只读).

JavaSE入门学习44:文件传输基础之I/O流(三)

三字节流的使用 6)FileOutputStream--->实现了向文件中写出byte数据的方法 FileOutputStream继承了OutputStream抽象类. FileOutputStream类中的方法: 实例代码1: <span style="font-size:18px;">import java.io.*; public class FileOutDemo1{ public static void main(String[] args) throws

JavaSE入门学习7:Java基础语法之语句(下)

继续接着Java基础语法来:JavaSE入门学习5:Java基础语法(一)和JavaSE入门学习6:Java基础语法(二). 语句 Java经常使用的3种循环:while.do...while,for. (5)Java循环语句之while 语法: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" > 运行

JavaSE入门学习6:Java基础语法之运算符和语句(上)

继续接着上篇:JavaSE入门学习5:Java基础语法(一)来看Java的基础语法. 五运算符 运算符是一种"功能"符号,用以通知Java进行相关的运算.比方.我们须要将变量age的值设置为20.这时候就 须要一个"=",告诉程序须要进行赋值操作. Java 语言中经常使用的运算符可分为例如以下几种:算术运算符,赋值运算符,比較运算符,逻辑运算符,条件运符. (1)算术运算符 算术运算符主要用于进行主要的算术运算.如加法.减法.乘法.除法等. Java 中经常使用的

JavaSE入门学习4:搭建Java开发环境(二)

在上一篇的博文JavaSE入门学习3:搭建Java开发环境(一)中说到我们配置了Path变量的一种方式,再来说说第 二种配置方式,这种配置方式是必须掌握的. path环境变量配置方式2 我们为什么要将第二种配置形式呢?那第一种的配置方式有什么缺点吗?假设我的计算机上安装了两个JDK,一 个是1.7,一个1.8,我刚才配置的是1.8的,那么我需要配置1.7,需要重新配置Path环境变量,有可能在修改Path环 境变量的同时误删其它的东西,所以不建议使用第一种. path环境变量的参照形配置方式的步

【RL-TCPnet网络教程】第38章 TFTP简单文件传输基础知识

第38章      TFTP简单文件传输基础知识 本章节为大家讲解TFTP(Trivial File Transfer Protocol,简单文件传输协议)的基础知识,方便后面章节的实战操作. (本章的知识点主要整理自网络) 38.1  初学者重要提示 38.2  TFTP基础知识参考资料 38.3  TFTP基础知识点 38.4  总结 38.1  初学者重要提示 TFTP简单文件传输协议在实际项目中有比较重要的实用价值,需要初学者对TFTP的基础知识也有个认识. 38.2  TFTP基础知识

JavaSE入门学习21:Java面向对象之接口(interface)(二)

一接口实现的多态 在上一篇博文:JavaSE入门学习20:Java面向对象之接口(interface)(一)中提到了接口的实现存在多态性,那么 这一篇主要就要分析接口实现的多态. 实例一 Test.java源文件代码: public class Test{ public static void main(String[] args){ //实现接口Singer Singer s1 = new Student("Amy"); s1.sing(); s1.sleep(); s1.study

JavaSE入门学习24:Java面向对象补充

一Java中的Object类 Object类是所有Java类的父类,如果一个类没有使用extends关键字明确标识继承另外一个类,那么这个类默认 继承Object类. public class Person{ // } //等价于 public class Person extends Object{ // } Object类中的方法,适合所有子类. 1)toString()方法 在Object类中定义有public String toString()方法,其返回值是String类型,描述当前对