Java基础笔记 - 对象的序列化和反序列化及其实现和使用

1、对象持久化:

将一个对象保存到永久存储的设备上的机制。

2、对象序列化(serialization):

将对象转换为字节流保存起来是把一个对象的状态写入一个字节流的过程。在需要时还原此对象的机制。

在一个对象图表中,如果试图序列化一个顶层的对象,所有其他引用的对象都被循环的定位和序列化。同样,在反序列化过程中,所有的这些对象以及他们的引用都被正确恢复。

2.1、序列化说明:

当一个对象被序列化时,只保存对象的非静态成员变量。

如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存。

如果一个可序列化的对象包含一个对象引用,而该引用的对象是不可序列化的,那么在序列化时会抛出NotSerializableException。可以使用标记transient该对象引用,设置该对象引用不被序列化,从而通过顶层对象的序列化。(声明为transient或static的变量不被序列化工具存储)

2.2、对象序列化的实现:

必须实现Serializable接口或者Externalizable接口。

public interface Serializable

类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。

该接口是一个表示性接口,没有定义任何方法。当一个类实现了该接口时,就表示该类的对象是可以序列化的。

2.3、ObjectOutput接口:

public interface ObjectOutput extends DataOutput

实现序列化。

ObjectOutput 扩展 DataOutput 接口以包含对象的写入操作。DataOutput 包括基本类型的输出方法;ObjectOutput 扩展了该接口,以包含对象、数组和 String 的输出方法。

2.3.1、基本方法:

void writeObject(Object obj)
throws IOException

将对象写入底层存储或流。实现此接口的类定义如何写入对象。

2.3.2、ObjectOutput接口的实现类ObjectOutputStream:

public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants

ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。

2.3.3、ObjectOutputStream是一个过滤流,装饰了OutputStream,以下是其构造方法:

protected ObjectOutputStream()
为完全重新实现 ObjectOutputStream 的子类提供一种方法,让它不必分配仅由 ObjectOutputStream 的实现使用的私有数据。
ObjectOutputStream(OutputStream out)
创建写入指定 OutputStream 的 ObjectOutputStream。

即是通过ObjectOutputStream的方法把对象写出到OutputStream字节流中。

2.4、ObjectInput接口:

public interface ObjectInput extends DataInput

实现反序列化。

ObjectInput 扩展 DataInput 接口以包含对象的读操作。DataInput 包括基本类型的输入方法;ObjectInput 扩展了该接口,以包含对象、数组和 String 的输出方法。

2.4.1、ObjectInput接口的实现类ObjectInputStream:

public class ObjectInputStream extends InputStream implements ObjectInput, ObjectStreamConstants

ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。

ObjectInputStream也是一个过滤流。

2.4.2、基本方法:

public final Object readObject()
throws IOException,
ClassNotFoundException

从 ObjectInputStream 读取对象。对象的类、类的签名和类及所有其超类型的非瞬态和非静态字段的值都将被读取。可以使用 writeObject 和 readObject 方法为类重写默认的反序列化。由此对象引用的对象是可传递读取的,这样 readObject 即可重新构造这些对象的完全等价的图形。

2.5、创建序列化对象并实现序列化和反序列化的例子:

创建序列化对象:

/*
\
创建序列化对象
*/
class User implements Serializable{

private static final long serialVersionUID = 1L;
//transient关键字,不被序列化
transient String username;
String password;
public User(String username, String password){
    this.username = username;
    this.password = password;
}

}

通过ObjectOutputStream和ObjectInputStream的方法实现序列化和反序列化:

public static void main(String[] args) throws IOException, ClassNotFoundException {

//以下是实现对象序列化
//创建序列化对象
User user = new User("arthinking", "arthinking");
//创建文件字节输出流
FileOutputStream fos = new FileOutputStream("D:/itzhai/arthinking.txt");
//通过文件字节输出流构造对象输出流
ObjectOutputStream oos = new ObjectOutputStream(fos);
//写出对象
oos.writeObject(user);
oos.close();

//以下是实现对象反序列化
FileInputStream fis = new FileInputStream("D:/itzhai/arthinking.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
//不会调用任何构造方法,而是根据反序列化出来的对象状态信息构造对象
User user2 = (User) ois.readObject();
//username不被序列化,这里输出为null
System.out.println(user2.username);
System.out.println(user2.password);

}

2.6、在序列化和反序列化过程中需要特殊处理的类必须使用下列准确签名来实现特殊方法:

private void writeObject(java.io.ObjectOutputStream out)
throws IOException
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException;
private void readObjectNoData()
throws ObjectStreamException;

例如上面例子中的User类如果实现了这两个方法,那么在使用ObjectOutputStream的writeObject()方法输出对象时,就不会自动序列化对象,而是执行这里的writeObject方法,在这两个方法中可以使用ObjectOutputStream和ObjectInputStream的一些方法对对象的属性进行序列化,如:

oos.writeUTF(username)

arthinking wechat
欢迎关注itzhai公众号