本文是基于Linux环境运行,读者阅读前需要具备一定Linux知识

对象序列化

对象序列化的目标是将对象保存到磁盘中,或允许在网络中直接传输对象。对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,通过网络将这种二进制流传输到另一个网络节点,其他程序一旦获取到这种二进制流,都可以将这种二进制流恢复成原来的Java对象

对象序列化是将一个Java对象写入IO流,与此对应的是,对象反序列化是从IO流中恢复Java对象,如果需要让某个对象支持序列化机制,必须让其实现如下两个接口:

  • Serializable
  • Externalizable

代码1-1中,声明一个Person对象,Person对象实现了Serializable接口,将对象序列化到磁盘文件

代码1-1

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class Person implements Serializable {

	private String name;
	private int age;

	public Person() {
		super();
		System.out.println("Person()构造方法");
	}

	public Person(String name, int age) {
		super();
		System.out.println("Person(String name, int age)构造方法");
		this.name = name;
		this.age = age;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}

}

public class WriteObject {

	public static void main(String[] args) {
		if (args == null || args.length == 0) {
			throw new RuntimeException("请输入对象序列化路径");
		}
		Person p = new Person("小红", 10);
		ObjectOutputStream oos = null;
		try {
			oos = new ObjectOutputStream(new FileOutputStream(args[0]));
			oos.writeObject(p);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				if (oos != null) {
					oos.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

}

 

代码1-2位从磁盘文件读取Person对象

代码1-2

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

public class ReadObject {

	public static void main(String[] args) {
		if (args == null || args.length == 0) {
			throw new RuntimeException("请输入对象序列化路径");
		}
		ObjectInputStream ois = null;
		try {
			ois = new ObjectInputStream(new FileInputStream(args[0]));
			Person p = (Person) ois.readObject();
			System.out.println(p);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				if (ois != null) {
					ois.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

}

 

代码1-1、代码1-2运行结果:

root@lejian:/home/software/.io# touch object
root@lejian:/home/software/.io# java WriteObject object 
Person(String name, int age)构造方法
root@lejian:/home/software/.io# java ReadObject object 
Person [name=小红, age=10]

需要指出一点的是:Person类虽然声明两个构造器,一个有参数,一个没有参数,但在反序列读取Java对象时,并没有看到程序调用Person任何一个构造器,这表明反序列化机制无须通过构造器来初始化Java对象

 

对象引用的序列化

如果某各类的属性类型不是基本类型或者String类型,且没有实现可序列化接口,则该类型属性类是不可序列化

代码1-3

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class Teacher implements Serializable {
	private String name;
	private Student student;

	public Teacher(String name, Student student) {
		super();
		this.name = name;
		this.student = student;
	}

	public String getName() {
		return name;
	}

	public Student getStudent() {
		return student;
	}

	@Override
	public String toString() {
		return "Teacher [name=" + name + ", student=" + student + "]";
	}

}

class Student implements Serializable {
	private String name;
	private int age;

	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + "]";
	}

}

public class WriteTeacher {

	public static void main(String[] args) {
		if (args == null || args.length == 0) {
			throw new RuntimeException("请输入对象序列化路径");
		}
		ObjectOutputStream oos = null;
		try {
			oos = new ObjectOutputStream(new FileOutputStream(args[0]));
			Student student = new Student("小明", 15);
			Teacher teacher1 = new Teacher("王老师", student);
			Teacher teacher2 = new Teacher("张老师", student);
			System.out.println("student:" + student);
			System.out.println("teacher1:" + teacher1);
			System.out.println("teacher2:" + teacher2);
			oos.writeObject(teacher1);
			oos.writeObject(teacher2);
			oos.writeObject(student);
			oos.writeObject(teacher2);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				if (oos != null) {
					oos.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

}

 

代码1-4

import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class ReadTeacher {

	public static void main(String[] args) {
		if (args == null || args.length == 0) {
			throw new RuntimeException("请输入对象序列化路径");
		}
		ObjectInputStream ois = null;
		try {
			ois = new ObjectInputStream(new FileInputStream(args[0]));
			Teacher teacher1 = (Teacher) ois.readObject();
			System.out.println("teacher1:" + teacher1);
			Teacher teacher2 = (Teacher) ois.readObject();
			System.out.println("teacher2:" + teacher2);
			Student student = (Student) ois.readObject();
			System.out.println("student:" + student);
			Teacher teacher3 = (Teacher) ois.readObject();
			System.out.println("teacher3:" + teacher3);
			System.out.println("teacher2 == teacher3:" + (teacher2 == teacher3));
			System.out.println("teacher1.student == teacher2.student:" + (teacher1.getStudent() == teacher2.getStudent()));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

 

代码1-3、1-4运行结果:

root@lejian:/home/software/.io# touch object
root@lejian:/home/software/.io# java WriteTeacher object 
student:Student [name=小明, age=15]
teacher1:Teacher [name=王老师, student=Student [name=小明, age=15]]
teacher2:Teacher [name=张老师, student=Student [name=小明, age=15]]
root@lejian:/home/software/.io# java ReadTeacher object 
teacher1:Teacher [name=王老师, student=Student [name=小明, age=15]]
teacher2:Teacher [name=张老师, student=Student [name=小明, age=15]]
student:Student [name=小明, age=15]
teacher3:Teacher [name=张老师, student=Student [name=小明, age=15]]
teacher2 == teacher3:true
teacher1.student == teacher2.student:true

 当程序序列化一个Teacher对象时,如果该Teacher对象持有一个Student对象的引用,为了在反序列化时可以正常恢复该Teacher对象,程序会顺带序列化Teacher对象所引用的Student对象,如果Student对象非空且没有实现Serializable接口,则在序列化时会抛出NotSerializableException异常

代码1-3中,程序先序列化teacher1,则会将teacher1所引用的student对象一起序列化,再序列化teacher2,同样会序列化teacher2所引用的student对象,最后再序列化student对象,这个过程中,程序似乎针对student对象序列化了三次,如果程序对student对象序列化了三次,那么在反序列化时,将会得到三个student对象,teacher1和teacher2所引用的student对象不是同一个,与最初的效果不一致,这将违背Java序列化机制的初衷

为了解决这一问题,Java序列化机制采用了一种特殊的序列化算法,大致如下:

  1. 所有保存到磁盘中的对象都有一个序列化编号
  2. 当程序试图序列化一个对象时,程序会先检查该对象是否被序列化过,只有该对象从未在本次虚拟机中被序列化过,系统才会将该对象转换成字节序列并输出
  3. 如果某个对象被序列化过,程序将直接输出一个序列化编号,而不是重新序列化该对象

由于Java序列化机制使然,如果多次序列化同一个Java对象时,只有第一次序列化才会把该Java对象转换成字节序列并输出,可能会引起一个潜在的问题,当对象的某些属性或状态修改后,再次序列化该对象,程序只是输出前面的序列化编号,而该对象被改变的值却不会同步到输出流中,如代码1-5

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

class Student implements Serializable {

	private String name;
	private int age;

	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = 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;
	}

}

public class SerializeMutable {

	public static void main(String[] args) {
		if (args == null || args.length == 0) {
			throw new RuntimeException("请输入对象序列化路径");
		}
		ObjectOutputStream oos = null;
		ObjectInputStream ois = null;
		try {
			oos = new ObjectOutputStream(new FileOutputStream(args[0]));
			ois = new ObjectInputStream(new FileInputStream(args[0]));
			Student student = new Student("小明", 15);
			oos.writeObject(student);
			student.setName("小王");
			oos.writeObject(student);
			Student student1 = (Student) ois.readObject();
			Student student2 = (Student) ois.readObject();
			System.out.println("student1 == student2 : " + (student1 == student2));
			System.out.println("student1.name = " + student1.getName());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				if (oos != null) {
					oos.close();
				}
				if (ois != null) {
					ois.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

}

 

代码1-5运行结果:

root@lejian:/home/software/.io# touch object
root@lejian:/home/software/.io# java SerializeMutable object 
student1 == student2 : true
student1.name = 小明

 

posted on 2016-12-30 11:15  百里琰  阅读(362)  评论(0编辑  收藏  举报