对象串行化概念:
对象串行化是指对对象进行存储和恢复的操作
在Java中使用Serializable接口或Externalizable接口来完成对象的串行化
对象串行化功能:
提供一个简单并可扩展的对象流存储机制。支持Java对象持久性存储。
在串行化形式中保存对象类型和安全属性。
支持远程对象的汇集和分解
支持定制串行化格式。
允许对象预定义自己的外部存储格式
可串行化的类:
任何实现了Serializable接口的类都是可串行化的类
一个类要实现可串行化,必须实现Seriziable接口,同时这个类中所包含的所有其他类也必须实现Serializable接口。
定义可串行化的类:
定制可串行化类的原因
由于特殊的安全问题,不希望对象的某一部分被串行化
某些对象恢复后,它包含的某一些对象需要重新创建,此时没有必要对这些对象进行串行化
定制的可串行化类的类型
部分定制的可串行化类
完全定制的可串行化类
部分定制可串行化的类:
部分定制的可串行化类指只对本身所定义数据的输出格式进行定制的可串行化类
在串行化类中重写writeObject()和 readObject()可实现部分定制串行化
writeObject(ObjectOutputStream s) 方法控制要保存的信息
readObject(ObjectInputStream s) 方法用于实现对象被恢复时对象数据的更新
Employee.java
import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class Employee implements Serializable { int id; String name; int age; public Employee() { } public Employee(int id, String name, int age) { this.id = id; this.name = name; this.age = age; } /** * 通过重写本方法,实现部分定制串行化 * @param out * @throws IOException */ private void writeObject(ObjectOutputStream out) throws IOException { out.writeInt(id); // out.writeInt(age); out.writeUTF(name); } /** * 通过重写本方法,实现部分定制串行化 * @param in * @throws IOException */ private void readObject(ObjectInputStream in) throws IOException { id = in.readInt(); // age = in.readInt(); name = in.readUTF(); } @Override public String toString() { return id + " " + name + " " + age; } }
EmployeeSerializableTest.java
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class EmployeeSerializableTest { public static void main(String[] args) throws IOException, ClassNotFoundException { Employee employee = new Employee(1, "zy", 22); ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("employee")); output.writeObject(employee); output.close(); ObjectInputStream input = new ObjectInputStream(new FileInputStream("employee")); employee = (Employee) input.readObject(); System.out.println(employee); input.close(); } }
完全制定的可串行化类:
完全制定的可串行化类指所有的数据(包括自己定义的及其父类的数据)的输出格式都进行定制的可串行化类。
完全制定的可串行化类必须实现Externalizable 接口
Externalizable 接口:
Externalizable 接口扩展了Serializable接口
Externalizable接口添加了两个方法
writeExternal()
readExternal()
在序列化和重新装配的过程中,Java虚拟机会自动调用这两个方法,执行一些特殊操作
完全定制的可串行化类的要求:
必须实现java.io.Externalizable 接口。
必须实现writeExternal()方法以保存对象的数据或状态。并且该类必须负责把对象的各个超类的数据保存到流中。
必须实现readExternal()方法,该方法从对象流中读取通过writeExternal()方法写入的对象的数据,同时还必须恢复父类中的数据。
如果对象串行化中使用了外部定义的格式,则writeExternal()方法和readExternal()方法都必须完全依照该格式。
必须定义一个具有public 访问权限的不带参数的构造方法
Movie.java
import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; public class Movie implements Externalizable { private String name; private long releaseYear; private String language; public Movie() { } public Movie(String name, long releaseYear, String language) { this.name = name; this.releaseYear = releaseYear; this.language = language; } public String toString() { return name + " , " + releaseYear + " , " + language; } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { name = in.readUTF(); releaseYear = in.readLong(); language = in.readUTF(); } @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeUTF(name); out.writeLong(releaseYear); out.writeUTF(language); } }
MovieExternalizableTest.java
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class MovieExternalizableTest { public static void main(String[] args) throws Exception { Movie harry = new Movie("Harry Potter and the Order of Phoenix", 2007, "English"); ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("movies")); output.writeObject(harry); output.close(); ObjectInputStream input = new ObjectInputStream(new FileInputStream("movies")); harry = (Movie) input.readObject(); System.out.println(harry); input.close(); } }
transient关键字:
被定义为transient类型的变量在串行化的过程中将不被保存和恢复。
使用transient可以加强串行化过程中的安全性。
使用Serializable进行串行化的优缺点:
内建支持
易于实现
占用空间过大
由于额外的开销导致速度变比较慢
完全定制的可串行化类的优缺点:
开销较少(开发者可以决定串行化的具体内容)
可能的速度提升
Java虚拟机不对其作任何支持,所有操作需要手工完成,实现复杂
容易引起安全问题
在一般情况下,不赞成使用完全定制的可串行化类
串行化对象:
将可串行化的对象进行串行化是通过两个类来实现
ObjectInputStream:从串行化的对象流中恢复对象,并且通过递归的方法遍历该对象对其它对象的引用,最终恢复对象所有的引用关系
ObjectOutputStream:将可串行化的对象写入字节流
ObjectOutputStream类:
ObjectOutputStream实现了DataOutputStream的接口
ObjectOutputStream中除了基本write(byte[]b)等方法之外,还定义了许多编写基本数据类型的writeObject()方法
writeObject()方法在指定的对象不可串行化时,将抛出NotSerializableException类型的例外。
ObjectOutputStream中的write方法:
write(byte[] buf); 写一个字节数组; write(byte[] buf, int off, int len);写一个字节数组的一部分; write(int val); 写一个字节; writeBoolean(Boolean val);写一个布尔值 writeByte (int val);写一个8位字节 writeChar(int val);写一个16位字节 writeChars(String str);写一个16位字符串 writeDouble(double val);写一个64位double型数值 writeFloat(float val);写一个32位float型数值 writeInt(int val);写一个32位int型数值 writeLong(long val);写一个64位long型数值 writeShort(int val);写一个16位short型数值 writeUTF(String str);采用UTF格式写一个字符串
ObjectInputStream类:
ObjectInputStream实现了java.io.DataInput接口。
ObjectInputStream中除了定义了基本read()等方法之外,还定义了许多读基本数据类型的readObject ()方法
WriteTest.java
import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.Serializable; public class WriteTest { public static void main(String[] args) throws IOException { ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("c://UserOutput.txt")); Test test = new Test(); out.writeObject(test); } } @SuppressWarnings("serial") class Test implements Serializable { }
ThawAlice.java
import java.io.FileInputStream; import java.io.ObjectInputStream; public class ThawAlien { public static void main(String[] args) throws Exception { ObjectInputStream in = new ObjectInputStream(new FileInputStream("c://UserInput.txt")); Object mystery = in.readObject(); System.out.println(mystery.getClass().toString()); } }
ObjectInputStream中的read方法:
read();读当前对象; read(byte[] buf, int off, int len);读一个字节数组的一部分; readBoolean();读一个布尔值; readByte();读一个8位字节; readChar();读一个16位字节; readDouble();读一个64位double型数值 readFloat();读一个32位float型数值 readInt();读一个32位int型数值 readLong();读一个64位long型数值 readShort();读一个16位short型数值 readUTF() ;采用UTF格式读一个字符串
对象串行化的注意点:
在对象进行串行化时,只保存对象的非静态成员变量的值。
变量的任何修饰符都不保存。
任何成员的方法和静态变量也不保存
在实现Serializable接口的类中,不对transient变量保存 ,通过transient可以使某些数据不被串行化
可串行化类的数据访问权限(public , protected或private)对于数据的串行化没有影响
数据是以字节的形式写入流而字符串型数据将表示为UTF格式
串行化中对敏感信息的保护:
必须保证对象的敏感数据不能从流中恢复,或者敏感数据在恢复后要由类进行某些验证
把类中包含敏感数据的成员变量定义为 transient 类型
串行化的必要性:
串行化机制能自动补偿操作系统间的差异
串行化机制为“远程方法调用”(RMI)提供了支持
对象的序列化也是Java Beans 的一个重要特性
使用串行化可以实现对象状态的“永久保存”