[Java开发之路](9)对象序列化与反序列化

1. 对象序列化

当你创建对象时。仅仅要你须要。它会一直存在,可是程序终止时,不管何时它都不会继续存在。虽然这样做是很有意义的,可是在某些情况下。假设程序不执行时扔能存在而且保存其信息,那将对我们很实用。这样,在下次程序执行时,该对象将被重建而且拥有的信息与程序上次执行时它所拥有的信息同样。

当然,我们也能够通过将信息写入文件或者数据库,可是假设能将一个对象声明为是"持久性"的,并为我们处理掉全部的细节,这将会显得十分方便。

Java的序列化是将那些实现了Serializable接口的对象转换为一个字节序列,并可以在以后须要的时候将这个字节序列全然恢复为原来的对象。我们可以在windows系统机器上创建一个对象。将其序列化。通过网络将它发送给一台执行Unix系统的计算机,然后在那里准确的又一次组装恢复为原来的对象。所以说不必操心数据在不同机器上的表示会不同。也不必关心字节的顺序或者其它不论什么细节。

这意味着序列化机制能自己主动弥补不同操作系统之间的差异。

对象序列化是为了支持两种特性。

一是Java的远程方法调用(RMI),它使存活于其它计算机上的对象使用起来就像存活于本机上一样。

当向远程对象发送消息时,须要通过对象序列化来传输參数和返回值。二是Java Beans。使用一个bean时。普通情况下是在设计阶段对它的状态信息进行配置。这样的状态信息必须保存下来,并在程序启动时进行后期恢复(这样的详细工作就是由对象序列化完毕的)。

要序列化一个对象。首先要创建一个OutputStream对象,然后将其封装在一个ObjectOutputStream对象内。

这时,仅仅需调用writeObject()就可以将对象序列化,并将其发送给 OutputStream(对象序列化是基于字节的,因要使用InputStream和OutputStream继承层次结构)。

要反向进行该过程(将一个序列化还原为一个对象),须要将一个InputStream封装在ObjectInputStream内,然后调用readObject()。和往常一样。我们最后获得的是一个引用,它指向一个向上转型的Object,所以必须向下转型才干直接设置它们。


package com.qunar.bean;

 

import java.io.Serializable;

/**

 * 学生实体类

 * @author sjf0115

 *

 */

public class Student implements Serializable{

 

	/**

	 * 

	 */

	private static final long serialVersionUID = 1L;

	// 姓名

	private String name;

	// 学号

	private String ID;

	// 年龄

	private int age;

	// 学校

	private String school;

 

	/**

	 * @param name

	 * @param iD

	 * @param age

	 * @param school

	 */

	public Student(String name, String id, int age, String school) {

		super();

		this.name = name;

		ID = id;

		this.age = age;

		this.school = school;

	}

	

	public String getName() {

		return name;

	}

	public void setName(String name) {

		this.name = name;

	}

	public String getID() {

		return ID;

	}

	public void setID(String iD) {

		ID = iD;

	}

	public int getAge() {

		return age;

	}

	public void setAge(int age) {

		this.age = age;

	}

	public String getSchool() {

		return school;

	}

	public void setSchool(String school) {

		this.school = school;

	}

	@Override

	public String toString() {

		return "姓名:" + name + "  学号:" + ID + "  年龄:" + age + "  学校:" + school;

	}

}

package com.qunar.io;

 

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

 

import com.qunar.bean.Student;

 

public class SeriaCode {

 

	public static void main(String[] args) {

		// 对象序列化数据保存位置

		String path = "D:\\seria.dat";

		try {

			// 创建FileOutputStream对象

			FileOutputStream fileOutputStream = new FileOutputStream(path);

			// 创建ObjectOutputStream对象

			ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);

			// 序列化对象

			Student student = new Student("xiaosi","130346",25,"西安电子科技大学");

			// 进行对象序列化  Student对象要实现序列化接口

			objectOutputStream.writeObject(student);

			objectOutputStream.flush();

			objectOutputStream.close();

			

			// 创建FileInputStream对象

			FileInputStream fileInputStream = new FileInputStream(path);

			// 创建ObjectInputStream对象

			ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);

			// 反序列化为对象

			Student stu = (Student)objectInputStream.readObject();

			objectInputStream.close();

			System.out.println("Stu->"+stu);

			

		} catch (FileNotFoundException e) {

			e.printStackTrace();

		} catch (IOException e) {

			e.printStackTrace();

		} catch (ClassNotFoundException e) {

			e.printStackTrace();

		}

	}

}

2.寻找类

将一个对象从它的序列化状态中恢复出来,有哪些工作是必须的?举例来说。假如我们将一个对象序列化,并通过网络将其作为文件传送给还有一台计算机,那么还有一台计算机上的程序能够仅仅利用该文件内容来还原这个对象吗?


package com.qunar.io.serial;

 

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.IOException;

import java.io.ObjectInputStream;

 

public class SerialCode2 {

 

	public static void main(String[] args) {

		try {

			// 创建FileInputStream对象

			FileInputStream fileInputStream = new FileInputStream("seria.dat");

			// 创建ObjectInputStream对象

			ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);

			// 反序列化为对象

			Object object = objectInputStream.readObject();

			objectInputStream.close();

			System.out.println(object.getClass());

		} catch (FileNotFoundException e) {

			e.printStackTrace();

		} catch (IOException e) {

			e.printStackTrace();

		} catch (ClassNotFoundException e) {

			e.printStackTrace();

		}

	}

}

如果在执行上面程序之前,将Student类删除,在执行,会得到:

java.lang.ClassNotFoundException: com.qunar.io.Student

at java.net.URLClassLoader$1.run(Unknown Source)

at java.net.URLClassLoader$1.run(Unknown Source)

at java.security.AccessController.doPrivileged(Native Method)

at java.net.URLClassLoader.findClass(Unknown Source)

at java.lang.ClassLoader.loadClass(Unknown Source)

at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)

at java.lang.ClassLoader.loadClass(Unknown Source)

at java.lang.Class.forName0(Native Method)

at java.lang.Class.forName(Unknown Source)

at java.io.ObjectInputStream.resolveClass(Unknown Source)

at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source)

at java.io.ObjectInputStream.readClassDesc(Unknown Source)

at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)

at java.io.ObjectInputStream.readObject0(Unknown Source)

at java.io.ObjectInputStream.readObject(Unknown Source)

at com.qunar.io.serial.SerialCode2.main(SerialCode2.java:17)

打开文件和读取Student对象中内容都须要Student的class对象,而虚拟机找不到Student.calss,这样会导致抛出ClassNotFoundException异常。所以必须保证虚拟机可以找到相关的.class文件。

从这里就能够证明得到:不能仅仅利用序列化字节数据文件来得到原先对象。还必须相应类的.class文件。

3. 序列化控制

默认的序列化机制并不难控制。然而,假设有特殊的须要那又该怎么办?比如,或许考虑特殊的安全问题,并且你不希望对象的某一部分被序列化;或者一个对象还原以后,某子对象须要又一次创建。从而不必将该子对象序列化。

为了应对这些特殊的情况。可通过Externalizable接口(取代实现Serializable接口)来对序列化过程进行控制。这个Externalizable接口继承了Serializable接口,同一时候还增添了两个方法:writeExternal()和readExternal()。

这两个方法会在序列化和反序列化还原过程中自己主动调用。以便运行一些特殊操作。


package com.qunar.io;

 

import java.io.Externalizable;

import java.io.IOException;

import java.io.ObjectInput;

import java.io.ObjectOutput;

 

public class Fruit implements Externalizable{

 

	public Fruit(){

		System.out.println("Fruit constructor...");

	}

	@Override

	public void writeExternal(ObjectOutput out) throws IOException {

		System.out.println("Fruit writeExternal...");

	}

	@Override

	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

		System.out.println("Fruit readExternal...");

	}

 

}

package com.qunar.io;

 

import java.io.Externalizable;

import java.io.IOException;

import java.io.ObjectInput;

import java.io.ObjectOutput;

 

public class Fruit2 implements Externalizable{

 

	Fruit2(){

		System.out.println("Fruit2 constuctor...");

	}

	@Override

	public void writeExternal(ObjectOutput out) throws IOException {

		System.out.println("Fruit writeExternal...");

	}

	@Override

	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

		System.out.println("Fruit readExternal...");

	}

 

}

package com.qunar.io;

 

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 FruitSerialCode {

 

	public static void main(String[] args) {

		try {

			// 创建FileOutputStream对象

			FileOutputStream fileOutputStream = new FileOutputStream("fruit.out");

			// 创建ObjectOutputStream对象

			ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);

			// 序列化对象

			Fruit fruit = new Fruit();

			Fruit2 fruit2 = new Fruit2();

			// 进行对象序列化  Fruit对象要实现序列化接口

			System.out.println("writeObject...");

			objectOutputStream.writeObject(fruit);

			objectOutputStream.writeObject(fruit2);

			objectOutputStream.flush();

			objectOutputStream.close();

			

			// 创建FileInputStream对象

			FileInputStream fileInputStream = new FileInputStream("fruit.out");

			// 创建ObjectInputStream对象

			ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);

			// 反序列化为对象

			System.out.println("readFruit...");

			fruit = (Fruit)objectInputStream.readObject();

			System.out.println("readFruit2...");

			fruit2 = (Fruit2)objectInputStream.readObject();

			objectInputStream.close();

			

		} catch (FileNotFoundException e) {

			e.printStackTrace();

		} catch (IOException e) {

			e.printStackTrace();

		} catch (ClassNotFoundException e) {

			e.printStackTrace();

		}

	}

}

执行结果:

Fruit constructor...

Fruit2 constuctor...

writeObject...

Fruit writeExternal...

Fruit writeExternal...

readFruit...

Fruit constructor...

Fruit readExternal...

readFruit2...

java.io.InvalidClassException: com.qunar.io.Fruit2; no valid constructor

at java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(Unknown Source)

at java.io.ObjectStreamClass.checkDeserialize(Unknown Source)

at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)

at java.io.ObjectInputStream.readObject0(Unknown Source)

at java.io.ObjectInputStream.readObject(Unknown Source)

at com.qunar.io.FruitSerialCode.main(FruitSerialCode.java:36)

Fruit和Fruit2除了细微的区别之外,差点儿全然一致。

上例中没有反序列化后Fruit2对象。而且导致了一个异常。主要是Fruit的构造函数是public的,而Fruit2的构造函数却不是,这样就会在反序列时抛出异常。

反序列化fruit后,会调用Fruit的默认构造函数。这与反序列一个Serializable对象不同。

对于一个Serializable对象。对象全然以它存储的二进制位为基础,而不用调用构造函数。而对于一个Externalizable对象,全部普通的构造函数都会被调用(包含在字段定义时的初始化)。然后调用readExternal()。

注意:

全部默认的构造函数都会被调用。才干使Externalizable对象产生正确的行为。

以下样例示范怎样正确的序列化和反序列一个Externalizable对象:


package com.qunar.io;

 

import java.io.Externalizable;

import java.io.IOException;

import java.io.ObjectInput;

import java.io.ObjectOutput;

 

public class Fruit implements Externalizable{

	private String name;

	private int num;

	

	// 必须有默认构造函数  反序列时使用

	public Fruit(){

		System.out.println("Fruit default constructor...");

	}

	

	/**

	 * @param name

	 * @param num

	 */

	public Fruit(String name, int num) {

		System.out.println("Fruit constructor...");

		this.name = name;

		this.num = num;

	}

 

	@Override

	public void writeExternal(ObjectOutput out) throws IOException {

		System.out.println("Fruit writeExternal...");

		// 必须做例如以下操作

		out.writeObject(name);

		out.writeInt(num);

	}

	@Override

	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

		System.out.println("Fruit readExternal...");

		// 必须做例如以下操作

		name = (String)in.readObject();

		num = in.readInt();

	}

 

	@Override

	public String toString() {

		return "name:" + name + "  num:" + num;

	}

}

package com.qunar.io;

 

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 FruitSerialCode {

 

	public static void main(String[] args) {

		try {

			// 创建FileOutputStream对象

			FileOutputStream fileOutputStream = new FileOutputStream("fruit.out");

			// 创建ObjectOutputStream对象

			ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);

			// 序列化对象

			Fruit fruit = new Fruit("苹果",20);

			// 进行对象序列化  Fruit对象要实现序列化接口

			System.out.println("writeObject...");

			objectOutputStream.writeObject(fruit);

			objectOutputStream.flush();

			objectOutputStream.close();

			

			// 创建FileInputStream对象

			FileInputStream fileInputStream = new FileInputStream("fruit.out");

			// 创建ObjectInputStream对象

			ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);

			// 反序列化为对象

			System.out.println("readFruit...");

			fruit = (Fruit)objectInputStream.readObject();

			System.out.println("Fruit->[" + fruit + "]");

			objectInputStream.close();

			

		} catch (FileNotFoundException e) {

			e.printStackTrace();

		} catch (IOException e) {

			e.printStackTrace();

		} catch (ClassNotFoundException e) {

			e.printStackTrace();

		}

	}

}

执行结果:

Fruit constructor...

writeObject...

Fruit writeExternal...

readFruit...

Fruit default constructor...

Fruit readExternal...

Fruit->[name:苹果  num:20]

能够看出。name和num仅仅在第二个构造函数中初始化,而不是在默认的构造函数中初始化。所以说,假如不在readExternal初始化name和num。name就会为null,age就会为0,假设凝视掉代码中"必须做例如以下操作"之后的代码,反序列化之后的对象的name为null,num为0。

Fruit constructor...

writeObject...

Fruit writeExternal...

readFruit...

Fruit default constructor...

Fruit readExternal...

Fruit->[name:null  num:0]

假设我们从一个Externalizable对象继承,通常须要调用基类版本号的writeExternal()和readExternal()来为基类组件提供恰当的序列化和反序列化功能。因此。为了正常执行,我们不仅须要在writeExternal()方法(没有不论什么默认行为来为Externalizable对象写入不论什么成员对象)中将来自对象的重要信息写入,还必须在readExternal()方法中恢复数据。

4. transientkeyword

但我们对序列化进行控制时,可能某个特定属性不想让Java序列化机制自己主动保存与恢复。假设属性表示的是我们不希望将其序列化的敏感信息(如password),就会遇到这样的问题。即使对象中的这些信息是private属性,一经序列化处理,人们就能够通过读取文件或者拦截网络传输的方式訪问到它。

(1)防止对象敏感信息被序列化。能够将类实现为Externalizable,像前面一样。这样就没有不论什么东西能够自己主动序列化。而且能够在writeExternal()内部仅仅对所需部分进行显示的序列化。

(2)假设我们操作的是Serializable对象,那么全部序列化操作都会自己主动进行。

为了进行控制,使用transientkeyword关闭序列化操作,它的意思"不用麻烦你序列化或者反序列化数据,我自己会处理的"。

如果我们用Login类保存某个特定的登录会话信息。登录的合法性得到检验之后。我们想把数据保存下来,但不包含password。


package com.qunar.io;

 

import java.io.Serializable;

import java.util.Date;

 

public class Login implements Serializable{

	/**

	 * 

	 */

	private static final long serialVersionUID = 1L;

	private Date date = new Date();

	private String userName;

	// 防止被序列化transient

	private transient String password;

	

	/**

	 * @param date

	 * @param userName

	 * @param password

	 */

	public Login(String userName, String password) {

		super();

		this.userName = userName;

		this.password = password;

	}

 

	@Override

	public String toString() {

		return "Date:" + date + "  UserName:" + userName + " Password:" + password;

	}

}

package com.qunar.io;

 

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 LoginSerialCode {

 

	public static void main(String[] args) {

		try {

			// 创建FileOutputStream对象

			FileOutputStream fileOutputStream = new FileOutputStream("login.out");

			// 创建ObjectOutputStream对象

			ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);

			// 序列化对象

			Login login = new Login("xiaosi", "123");

			// 进行对象序列化  Fruit对象要实现序列化接口

			System.out.println("writeObject...");

			objectOutputStream.writeObject(login);

			objectOutputStream.flush();

			objectOutputStream.close();

			

			// 创建FileInputStream对象

			FileInputStream fileInputStream = new FileInputStream("login.out");

			// 创建ObjectInputStream对象

			ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);

			// 反序列化为对象

			System.out.println("readFruit...");

			login = (Login)objectInputStream.readObject();

			System.out.println("LoginInfo->[" + login + "]");

			objectInputStream.close();

			

		} catch (FileNotFoundException e) {

			e.printStackTrace();

		} catch (IOException e) {

			e.printStackTrace();

		} catch (ClassNotFoundException e) {

			e.printStackTrace();

		}

	}

}

执行结果:

writeObject...

readFruit...

LoginInfo->[Date:Thu Dec 31 00:16:13 CST 2015  UserName:xiaosi Password:null]

date和useName(不是transient),所以它们会自己主动被序列化,而password是transient的,所以不会被自己主动保存到磁盘;另外自己主动序列化机制也不会去恢复它。当对象恢复时,password就会变成null。

同一时候我们发现date字段被存储在磁盘而且从磁盘上恢复出来,而不是又一次生成。

(3)Externalizable的替代方法

假设你不使用Externalizable。我们另一种方法。我们能够实现Serializable接口,并加入writeObject()和readObject()方法。

这样一旦进行序列化和反序列化,就会自己主动的分别调用这两个方法,来取代默认的序列化机制。


package com.qunar.io;

 

import java.io.IOException;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.io.Serializable;

import java.util.Date;

 

public class Login implements Serializable{

	/**

	 * 

	 */

	private static final long serialVersionUID = 1L;

	private Date date = new Date();

	private String userName;

	// 防止被序列化transient

	private transient String password;

	

	/**

	 * @param date

	 * @param userName

	 * @param password

	 */

	public Login(String userName, String password) {

		super();

		this.userName = userName;

		this.password = password;

	}

	// 必须有

	private void writeObject(ObjectOutputStream stream) throws IOException{

		// 默认的序列化

		stream.defaultWriteObject();

		// 手动完毕序列化

		stream.writeObject(password);

	}

	// 必须有

	private void readObject(ObjectInputStream stream) throws ClassNotFoundException, IOException{

		// 默认的反序列化

		stream.defaultReadObject();

		// 手动完毕反序列化

		password = (String)stream.readObject();

	}

	

	@Override

	public String toString() {

		return "Date:" + date + "  UserName:" + userName + " Password:" + password;

	}

}

package com.qunar.io;

 

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 LoginSerialCode {

 

	public static void main(String[] args) {

		try {

			// 创建FileOutputStream对象

			FileOutputStream fileOutputStream = new FileOutputStream("login.out");

			// 创建ObjectOutputStream对象

			ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);

			// 序列化对象

			Login login = new Login("xiaosi", "123");

			// 进行对象序列化  Fruit对象要实现序列化接口

			System.out.println("writeObject...");

			objectOutputStream.writeObject(login);

			objectOutputStream.flush();

			objectOutputStream.close();

			

			// 创建FileInputStream对象

			FileInputStream fileInputStream = new FileInputStream("login.out");

			// 创建ObjectInputStream对象

			ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);

			// 反序列化为对象

			System.out.println("readFruit...");

			login = (Login)objectInputStream.readObject();

			System.out.println("LoginInfo->[" + login + "]");

			objectInputStream.close();

			

		} catch (FileNotFoundException e) {

			e.printStackTrace();

		} catch (IOException e) {

			e.printStackTrace();

		} catch (ClassNotFoundException e) {

			e.printStackTrace();

		}

	}

}

执行结果:

writeObject...

readFruit...

LoginInfo->[Date:Fri Jan 01 15:57:53 CST 2016  UserName:xiaosi Password:123]

相比上面实验。password恢复出来了。

在这个样例中。password字段是transient字段,用来证明非transient字段是由defaultWriteObject()方法保存,而transient字段是必须在程序中明白保存和恢复。

5. 使用"持久化"

一个诱人的使用序列化技术的想法:存储程序的一些状态。以便我们随后可以非常easy的将程序恢复到当前的状态。可是在我们可以这样做之前,必须回答几个问题。假设我们将两个对象(都具有指向第三个对象的引用)进行序列化。会发生什么状况?当我们从它们的序列化状态恢复这两个对象时,第三个对象会仅仅出现一次吗?


package com.qunar.io;

 

import java.io.Serializable;

 

public class House implements Serializable{

 

	/**

	 * 

	 */

	private static final long serialVersionUID = 1L;

	private String name;

	private String location;

	

	/**

	 * @param name

	 * @param location

	 */

	public House(String name, String location) {

		super();

		this.name = name;

		this.location = location;

	}

}

package com.qunar.io;

 

import java.io.Serializable;

 

public class Animal implements Serializable{

 

	/**

	 * 

	 */

	private static final long serialVersionUID = 1L;

	private String name;

	private House house;

	

	/**

	 * 构造函数

	 * @param name

	 * @param house

	 */

	public Animal(String name, House house) {

		super();

		this.name = name;

		this.house = house;

	}

 

	@Override

	public String toString() {

		return name + "   " + house;

	}

}

package com.qunar.io;

 

import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

import java.io.FileNotFoundException;

import java.io.IOException;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.util.ArrayList;

import java.util.List;

 

 

public class AnimalSerialCode {

 

	@SuppressWarnings("unchecked")

	public static void main(String[] args) {

		House house = new House("水立方","北京海淀区");

		List<Animal> animals = new ArrayList<Animal>();

		animals.add(new Animal("狗", house));

		animals.add(new Animal("鸡", house));

		animals.add(new Animal("羊", house));

		

		System.out.println("Animals->" + animals);

		try {

			ByteArrayOutputStream buf = new ByteArrayOutputStream();

			ObjectOutputStream objectOutputStream = new ObjectOutputStream(buf);

			// 序列化

			objectOutputStream.writeObject(animals);

			objectOutputStream.writeObject(animals);

			

			ByteArrayOutputStream buf2 = new ByteArrayOutputStream();

			ObjectOutputStream objectOutputStream2 = new ObjectOutputStream(buf2);

			// 序列化

			objectOutputStream2.writeObject(animals);

			

			ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(buf.toByteArray()));

			List<Animal> ani1 = (List<Animal>)objectInputStream.readObject();

			List<Animal> ani2 = (List<Animal>)objectInputStream.readObject();

			

			ObjectInputStream objectInputStream2 = new ObjectInputStream(new ByteArrayInputStream(buf2.toByteArray()));

			List<Animal> ani3 = (List<Animal>)objectInputStream2.readObject();

			

			System.out.println("Animals1->"+ani1);

			System.out.println("Animals2->"+ani2);

			System.out.println("Animals3->"+ani3);

		} catch (FileNotFoundException e) {

			e.printStackTrace();

		} catch (IOException e) {

			e.printStackTrace();

		} catch (ClassNotFoundException e) {

			// TODO Auto-generated catch block

			e.printStackTrace();

		}

	}

}

执行结果:

Animals->[狗   [email protected], 鸡   [email protected], 羊   [email protected]]

Animals1->[狗   [email protected], 鸡   [email protected], 羊   [email protected]]

Animals2->[狗   [email protected], 鸡   [email protected], 羊   [email protected]]

Animals3->[狗   [email protected], 鸡   [email protected], 羊   [email protected]]

我们能够通过字节数组来使用对象序列化。从而实现不论什么可Serializable对象的"深度复制"(意味着复制的是整个对象网,而不不过基本对象及其引用)。

在这个样例中,Animal对象包括House类型字段。我们创建Animals列表并将其两次序列化,分别送至不同的流。当期被反序列化还原被打印时。我们能够看到:每次执行时对象将会处在不同的内存地址。

当然我们期望这些反序列还原后的对象地址与原来的对象地址不同,可是Animals1和Animals2却出现了同样的地址。当恢复Animals3时。系统无法知道还有一个流内的对象是第一个流内对象额别名,因此会产生全然不同的对象网。

仅仅要将不论什么对象序列化到单一流中,就能够会付出与我们写出时一样的对象网,而且没有不论什么意外反复复制的对象。假设想保存系统状态,最安全的做法就是将其作为"原子"操作进行序列化。

时间: 2024-10-25 22:27:00

[Java开发之路](9)对象序列化与反序列化的相关文章

Java对象序列化和反序列化

Java对象序列化和反序列化 在Java中,我们如果要保存一个对象的瞬时状态值,以便在下次使用时能够得到这些值,或者持久化对象,或者使用RMI(远程方法调用),或在网络中传递对象时,此时我们就需要将对象序列化,实现序列化,我们只要实现Serializable接口,该接口是一个标记接口(Tag interface),即里面没有方法,其主要作用就是告诉JVM该类的对象可以进行序列化. 一般来说,很多类的对象都实现了Serializable接口,但是,有些对象是不能进行序列化的,比如与数据库相关的连接

【转】Java对象序列化和反序列化

[转自]孤傲苍狼的Java基础学习总结——Java对象的序列化和反序列化 一.序列化和反序列化的概念 把对象转换为字节序列的过程称为对象的序列化. 把字节序列恢复为对象的过程称为对象的反序列化. 对象的序列化主要有两种用途: 1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中: 2) 在网络上传送对象的字节序列. 在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存.比如最常见的是Web服务器中的Session对 象,当有 10万用户并发访问,就有可

java 对象序列化与反序列化

Java序列化与反序列化是什么? 为什么需要序列化与反序列化? 如何实现Java序列化与反序列化? 本文围绕这些问题进行了探讨. 1.Java序列化与反序列化  Java序列化是指把Java对象转换为字节序列的过程: Java反序列化是指把字节序列恢复为Java对象的过程. 2.为什么需要序列化与反序列化 我们知道,当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本.图片.音频.视频等, 而这些数据都会以二进制序列的形式在网络上传送.那么当两个Java进程进行通信时,能否实现进程间的

Java对象序列化与反序列化

Java对象序列化与反序列化 对象序列化的目标是将对象保存在磁盘中或者在网络中进行传输.实现的机制是允许将对象转为与平台无关的二进制流. java中对象的序列化机制是将允许对象转为字节序列.这些字节序列可以使Java对象脱离程序存在,从而可以保存在磁盘上,也可以在网络间传输. 对象的序列化是将一个Java对象写入IO流:与此对应的,反序列化则是从IO流中恢复一个Java对象. 实现序列化 如果要将一个java对象序列化,那么对象的类需要是可序列化的.要让类可序列化,那么这个类需要实现如下两个接口

Java IO详解(六)------序列化与反序列化(对象流)

File 类的介绍:http://www.cnblogs.com/ysocean/p/6851878.html Java IO 流的分类介绍:http://www.cnblogs.com/ysocean/p/6854098.html Java IO 字节输入输出流:http://www.cnblogs.com/ysocean/p/6854541.html Java IO 字符输入输出流:https://i.cnblogs.com/EditPosts.aspx?postid=6859242 Jav

Java Object 对象序列化和反序列化

Java Object 对象序列化和反序列化 @author ixenos 对象序列化是什么 1.对象序列化就是把一个对象的状态转化成一个字节流. 我们可以把这样的字节流存储为一个文件,作为对这个对象的复制(深拷贝):在一些分布式应用中,我们还可以把对象的字节流发送到网络上的其他计算机. 反序列化是把流结构的对象恢复为其原有形式 2.Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,即,这些对象的生命周期不会比JVM的生命周期更长.但

对象序列化和反序列化应该写为一个工具类

对象序列化和反序列化工具类 package com.umu.util; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectInputStream; import java.io.Object

对象序列化与反序列化

1)要想让一个对象成为序列化对象,那么这个类就要实现Serializable或Externalizable接口(Externalizable接口继承与Serializable接口), 这种序列化仅对对象的 非transient 的实例变量进行序列化,不会对静态成员变量序列化,也不会对方法序列化 2) 对象序列化:把java“对象”转换成“字节序列”的过程称为对象序列化 对象反序列化:把“字节序列”恢复成java“对象”的过程称为反序列化 在反序列化时不会调用类的任何构造方法 3) 对象序列化的功

Java对象序列化与反序列化(1)

序列化机制允许将实现序列化的Java对象转换为字节序列,这些字节序列可以被保存在磁盘上,或通过网络传输,以备以后重新恢复成原来的对象.序列化机制使得对象可以脱离程序的运行而独立存在. 对象的序列化(Serialize)指将一个Java对象写入IO流中,与此对应的是,对象的反序列化(Deserialize)则指从IO流中恢复该Java对象.如果需要让某个对象可以支持序列化机制,必须让它的类是可序列化的(serializable),为了让某个类是可序列化的,该类必须实现如下两个接口之一: (1)Se