Java中的序列化与示例
Java中的序列化是将对象的状态转换为字节流的过程,反序列化的反向过程是将该字节流(对象的序列化形式)转换回对象的副本。
将对象转换为字节流后,即可保存它。由于这种通过Java序列化提供的持久化对象的能力,我们创建的对象可能会在JVM的生存期之外存在。
序列化需要什么
如果Java对象的类或者其任何超类实现了java.io.Serializable接口或者其子接口java.io.Externalizable,则可以对其进行序列化。请注意,Serializable是标记接口,没有任何字段或者方法。
ObjectOutputStream和ObjectInputStream类
为了序列化对象,使用了ObjectOutputStream类的writeObject()方法。
final void writeObject(Object obj) throws IOException
为了反序列化对象,使用了ObjectInputStream类的readObject()方法。
final Object readObject() throws IOException, ClassNotFoundException
Java对象序列化示例
在示例中,我们将使用以下实现了Serializable接口的Employee类。此处的代码未包含Getter和Setters。
import java.io.Serializable; public class Employee implements Serializable{ private String name; private String dept; private int salary; private transient int ssn; Employee(String name, String dept, int salary, int ssn){ this.name = name; this.dept = dept; this.salary = salary; this.ssn = ssn; } }
如我们所见,类实现了Serializable接口,这是序列化的必备条件。尝试序列化未实现Serializable接口的类会导致java.io.NotSerializableException的对象。
默认情况下,所有非静态对象字段都已序列化。如果我们不希望序列化任何特定字段,则应将该字段标记为瞬态。在类SSN中,该字段被标记为瞬态。
序列化过程
public class SerializationDemo { public static void main(String[] args) { Employee emp = new Employee("Ryan", "IT", 7500, 11111); final String fileName = "F:\theitroad\emp.ser"; serialzeObject(emp, fileName); } // Method to serialize object public static void serialzeObject(Object obj, String fileName){ try(ObjectOutputStream outStream = new ObjectOutputStream(new FileOutputStream(fileName))){ outStream.writeObject(obj); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
执行此程序时,将创建一个名为emp.ser的文件,该文件存储Employee对象的序列化信息。
反序列化过程
以下程序反序列化上面示例中序列化的Employee对象。
public class SerializationDemo { public static void main(String[] args) { //Employee emp = new Employee("Ryan", "IT", 7500, 11111); final String fileName = "F:\theitroad\emp.ser"; //serialzeObject(emp, fileName); /// Do null check here Employee e = (Employee)deSerializeObject(fileName); System.out.println("Name- " + e.getName()); System.out.println("Dept- " + e.getDept()); System.out.println("Salary- " + e.getSalary()); System.out.println("SSN- " + e.getSsn()); } // Method to deserialize object public static Object deSerializeObject(String fileName){ Object obj = null; try(ObjectInputStream inStream = new ObjectInputStream(new FileInputStream(fileName))){ obj = inStream.readObject(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { System.out.println("No class found for serialization"); e.printStackTrace(); } return obj; } }
输出:
Name- Ryan Dept- IT Salary- 7500 SSN- 0
在反序列化程序中,将存储序列化对象的文件传递给ObjectInputStream。使用readObject()方法从该对象读取。
由于SSN被标记为瞬态,因此序列化对象时未序列化SSN字段。这就是为什么没有要读取的SSN字段数据,所以显示默认int值0的原因。
关于序列化的要点
要保留的对象必须实现Serializable接口或者从其父类继承该实现。如果子类实现了Serializable,则情况并非如此,但父类不会变为Serializable。
所有不可序列化的字段都应标记为瞬态。
如果实现序列化的类引用了另一个对象,则该对象也应该实现序列化以进行序列化。如果所有引用的对象也都实现了Serializable接口,那么整个对象图将被序列化。
在反序列化过程中,对象从序列化的字节中重构,因此在反序列化过程中不会调用任何构造函数。
实现writeObject()和readObject()方法
大多数时候,writeObject()和readObject()方法的默认实现都可用于序列化和反序列化对象,但是我们也可以实现这些方法以对过程进行更多控制。
一种这样的情况是,父类没有实现子类实现的Serializable接口。在序列化对象时,如果要序列化从父类继承的字段,则可以通过实现writeObject()和readObject()方法来实现。让我们通过一个例子来理解它,父类是ClassB扩展的ClassA。
public class A { int a; public int getA() { return a; } public void setA(int a) { this.a = a; } }
import java.io.Serializable; public class B extends A implements Serializable{ int b; String test; public int getB() { return b; } public void setB(int b) { this.b = b; } public String getTest() { return test; } public void setTest(String test) { this.test = test; } public static void main(String[] args) { B obj = new B(); obj.setA(1); obj.setB(2); obj.setTest("Test"); final String fileName = "F:\theitroad\test.ser"; SerializationDemo.serialzeObject(obj, fileName); B retObj = (B)SerializationDemo.deSerializeObject(fileName); System.out.println("A " + retObj.getA()); System.out.println("B " + retObj.getB()); System.out.println("Test " + retObj.getTest()); } }
输出:
A 0 B 2 Test Test
如我们所见,字段A的值为0。字段A未序列化,因为ClassA没有实现Serializable。我们可以实现writeObject()和readObject()方法,并提供逻辑以显式序列化和反序列化在这种情况下从父类继承的字段。
import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class B extends A implements Serializable{ int b; String test; public int getB() { return b; } public void setB(int b) { this.b = b; } public String getTest() { return test; } public void setTest(String test) { this.test = test; } public static void main(String[] args) { B obj = new B(); obj.setA(1); obj.setB(2); obj.setTest("Test"); final String fileName = "F:\theitroad\test.ser"; SerializationDemo.serialzeObject(obj, fileName); B retObj = (B)SerializationDemo.deSerializeObject(fileName); System.out.println("A " + retObj.getA()); System.out.println("B " + retObj.getB()); System.out.println("Test " + retObj.getTest()); } private void writeObject(ObjectOutputStream outStream) throws IOException{ // default functionality for classB outStream.defaultWriteObject(); // Explicitly writing ClassA fields outStream.writeInt(getA()); } private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException{ // default functionality for classB inputStream.defaultReadObject(); // Explicitly reading ClassA fields and setting them setA(inputStream.readInt()); } }
使用writeObject()和readObject()停止序列化
我们可能还会遇到一种情况,要确保类从不序列化。
假设我们创建了一个其超类可序列化的类,但是我们不希望该新类可序列化?由于父类可序列化,因此子类可自动序列化。
为了确保子类不可序列化,我们可以实现writeObject()和readObject()方法,并从方法中抛出NotSerializableException。
在示例中,ClassA是实现Serializable的父类。
public class A implements Serializable{ int a; public int getA() { return a; } public void setA(int a) { this.a = a; } }
ClassB是具有writeObject()和readObject()方法的子类,实现该类可引发NotSerializableException。
import java.io.IOException; import java.io.NotSerializableException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class B extends A{ int b; String test; public int getB() { return b; } public void setB(int b) { this.b = b; } public String getTest() { return test; } public void setTest(String test) { this.test = test; } public static void main(String[] args) { B obj = new B(); obj.setA(1); obj.setB(2); obj.setTest("Test"); final String fileName = "F:\theitroad\test.ser"; SerializationDemo.serialzeObject(obj, fileName); B retObj = (B)SerializationDemo.deSerializeObject(fileName); System.out.println("A " + retObj.getA()); System.out.println("B " + retObj.getB()); System.out.println("Test " + retObj.getTest()); } private void writeObject(ObjectOutputStream outStream) throws IOException{ throw new NotSerializableException("Class is not serializable"); } private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException{ throw new NotSerializableException("Class is not serializable"); } }
输出:
java.io.NotSerializableException: Class is not serializable at com.theitroad.proj.Programs.B.writeObject(B.java:40)