Java的Externalizable接口

Published: 05 Mar 2014 Category: Java

在理解Externalizable接口之前,你需要先理解什么是序列化。有关序列化可以读下 这篇文章多了解一些。 Java提供了序列化的机制来把Java对象里持久化成一个有序的字节序列,这里面包括对象的数据及其类型,还有对象本身的类型信息。

Externalizable:

就如名字所说的,这个接口是用来将对象序列化具体化的。如果你想定制自己的序列化机制 的话,它就派上用场了。它通过你自己编码的方式来完成对象的编解码。Externalizable接口继承了Serializable接口。如果你实现了这个接口你得重写下面的方法。

@Override
    public void readExternal(ObjectInput arg0) throws IOException,
 
            ClassNotFoundException {
 
    }

    @Override
    public void writeExternal(ObjectOutput arg0) throws IOException {
 
    }

现在我们看下序列化是怎么进行的: 发送方: JVM检查类是否实现了externalizable接口。如果实现了则使用writeExternal()方法来进行序列化。如果它没实现externalizable而实现了Serializable接口,就用ObjectOutputStream来进行对象的序列化。 接收方: 当对象被反序列化并且它实现了externalizable接口的话,就会用默认构造方法新建一个实例,然后调用它的readExternal方法。如果没有实现externalizable而实现了Serializable,就用ObjectOutputStream来进行对象的反序列化。 我们还是从Java的序列化中用到过的那个例子讲起。 在src->org.arpit.javapostsforlearning中新建一个Employee.java

package org.arpit.javapostsforlearning;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
 
public class Employee implements Externalizable{
 
 int employeeId;
 String employeeName;
 String department;
 String nationality;
 
 public Employee()
 {
 
 }
 public int getEmployeeId() {
  return employeeId;
 }
 public void setEmployeeId(int employeeId) {
  this.employeeId = employeeId;
 }
 public String getEmployeeName() {
  return employeeName;
 }
 public void setEmployeeName(String employeeName) {
  this.employeeName = employeeName;
 }
 public String getDepartment() {
  return department;
 }
 public void setDepartment(String department) {
  this.department = department;
 }
 
 public String getNationality() {
  return nationality;
 }
 public void setNationality(String nationality) {
  this.nationality = nationality;
 }
 @Override
 public void readExternal(ObjectInput in) throws IOException,
 ClassNotFoundException {
  employeeId=in.readInt();
  employeeName=(String) in.readObject();
 
 }
 @Override
 public void writeExternal(ObjectOutput out) throws IOException {
 
  out.writeInt(employeeId);
  out.writeObject(employeeName);
 }
}

如果实现了externalizable就必须有一个无参的构造方法。 在org.arpit.javapostsforlearning中新建一个ExternalizableMain.java。

package org.arpit.javapostsforlearning;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
 
public class ExternalizableMain {
 /**
  * @author Arpit Mandliya
  */
 public static void main(String[] args) {
 
  Employee emp = new Employee();
  emp.setEmployeeId(101);
  emp.setEmployeeName("Arpit");
  emp.setDepartment("CS");
 
  //Serialize
  try
  {
   FileOutputStream fileOut = new FileOutputStream("employee.ser");
   ObjectOutputStream outStream = new ObjectOutputStream(fileOut);
   outStream.writeObject(emp);
   outStream.close();
   fileOut.close();
  }catch(IOException i)
  {
   i.printStackTrace();
  }
 
  //Deserialize
  emp = null;
  try
  {
   FileInputStream fileIn =new FileInputStream("employee.ser");
   ObjectInputStream in = new ObjectInputStream(fileIn);
   emp = (Employee) in.readObject();
   in.close();
   fileIn.close();
  }catch(IOException i)
  {
   i.printStackTrace();
   return;
  }catch(ClassNotFoundException c)
  {
   System.out.println("Employee class not found");
   c.printStackTrace();
   return;
  }
  System.out.println("Deserialized Employee...");
  System.out.println("Emp id: " + emp.getEmployeeId());
  System.out.println("Name: " + emp.getEmployeeName());
 
 }
}

运行:当你运行ExternalizableMain.java,你会得到这样的输出结果:

Deserialized Employee...
2Emp id: 101
3Name: Arpit

既然有了serializable,为什么还需要externalizable! 当你用serialize进行序列化时,除了所有字段,属于这个对象MAP的和成员变量能接触到的所有对象都会被序列化。比如: 如果你Employee有个父类是person,那么它会序列化所有的父类直到Object类。 同样的如果Employee有个address类型的成员变量,那么它会把整个address的整个对象集合也序列化进去。 如果你想序列化的只有employeeId和employeeName的话,你会希望增加这么多额外开销吗? 当你用serializable的话JVM会使用反射来进行序列化,这样会很慢。 当使用serializable时,类的描述信息也会存储到流里,包括父类的描述信息,和类关联的成员变量的信息。这也是一个性能问题。

外部序列化里的继承

我们现在来看下继承是怎么影响到外部序列化的。这里会有几种 情况比如 父类有没有实现过externalizable接口。如果没有你应该如何 处理,使得它能正常工作。我们来看个例子。 我们创建一个Employee的父类叫做Person。

场景1:如果父类没有实现externalizable: 如果父类没有实现externalizable,那么你得在实现externalizable的子类里序列化父类的字段。

package org.arpit.javapostsforlearning;
public class Person {
 
 String name="default";
 String nationality;
 
 public Person()
 {
  System.out.println("Person:Constructor");
 }
 
 public Person(String name, String nationality) {
  super();
  this.name = name;
  this.nationality = nationality;
 }
 
 public String getName() {
  return name;
 }
 
 public void setName(String name) {
  this.name = name;
 }
 
 public String getNationality() {
  return nationality;
 }

 public void setNationality(String nationality) {
  this.nationality = nationality;
 }
 
}

在org.arpit.javapostsforlearning中创建Employee。

package org.arpit.javapostsforlearning;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
 
public class Employee extends Person implements Externalizable{
 
 int employeeId;
 String department;
 
 public Employee()
 {
 
 }
 public Employee(int employeeId,String name,String department,String nationality)
 {
  super(name,nationality);
  this.employeeId=employeeId;
  this.department=department;
  System.out.println("Employee:Constructor");
 }
 
 public int getEmployeeId() {
  return employeeId;
 }
 public void setEmployeeId(int employeeId) {
  this.employeeId = employeeId;
 }
 
 public String getDepartment() {
  return department;
 }
 public void setDepartment(String department) {
  this.department = department;
 }
 
@Override
public void writeExternal(ObjectOutput out) throws IOException {
 
 /*since superclass does not implement externalizable, you need to serialize super class field in this class itself*/
 //superclass fields
 out.writeObject(name);
 out.writeObject(nationality);
 
 // its own fields
 out.writeInt(employeeId);
 out.writeObject(department);
}
 
@Override
public void readExternal(ObjectInput in) throws IOException,
  ClassNotFoundException {
 /*since superclass does not implement externalizable, you need to deserialize super class field in this class itself*/
 //superclass fields
 name=(String) in.readObject();
 nationality=(String) in.readObject();
 
 // its own fields
 employeeId=in.readInt();
 department=(String) in.readObject();
 
}
}

在org.arpit.javapostsforlearning新建ExternalizableMain。

package org.arpit.javapostsforlearning;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
 
public class ExternalizableMain {
 
 /**
  * @author Arpit Mandliya
  */
 public static void main(String[] args) {
 
  //Serialize
  Employee emp = new Employee(101,"Arpit","CS","Indian");
  System.out.println("Before serializing");
  System.out.println("Emp id: " + emp.getEmployeeId());
  System.out.println("Name: " + emp.getName());
  System.out.println("Department: " + emp.getDepartment());
  System.out.println("Nationality: " + emp.getNationality());
  System.out.println("************");
  System.out.println("Serializing");
  try
  {
   FileOutputStream fileOut = new FileOutputStream("employee.ser");
   ObjectOutputStream outStream = new ObjectOutputStream(fileOut);
   outStream.writeObject(emp);
   outStream.close();
   fileOut.close();
  }catch(IOException i)
  {
   i.printStackTrace();
  }
 
  //Deserialize
  System.out.println("************");
  System.out.println("Deserializing");
  emp = null;
  try
  {
   FileInputStream fileIn =new FileInputStream("employee.ser");
   ObjectInputStream in = new ObjectInputStream(fileIn);
   emp = (Employee) in.readObject();
   in.close();
   fileIn.close();
  }catch(IOException i)
  {
   i.printStackTrace();
   return;
  }catch(ClassNotFoundException c)
  {
   System.out.println("Employee class not found");
   c.printStackTrace();
   return;
  }
  System.out.println("After serializing");
  System.out.println("Emp id: " + emp.getEmployeeId());
  System.out.println("Name: " + emp.getName());
  System.out.println("Department: " + emp.getDepartment());
  System.out.println("Nationality: " + emp.getNationality());
 }
}

运行后结果如下:

Employee:Constructor
Before serializing
Emp id: 101
Name: Arpit
Department: CS
Nationality: Indian
************
Serializing
************
Deserializing
Person:Constructor
After serializing
Emp id: 101
Name: Arpit
Department: CS
Nationality: Indian
场景二:如果父类实现了Externalizable

如果父类实现了Externalizable,它也有相应的readExternal和writeExternal方法,它会用这些方法序列化自己的字段。 Person.java

package org.arpit.javapostsforlearning;
 
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
 
public class Person implements Externalizable{
 
 String name="default";
 String nationality;
 
 public Person()
 {
  System.out.println("Person:Constructor");
 }
 
 public Person(String name, String nationality) {
  super();
  this.name = name;
  this.nationality = nationality;
 }
 
 public String getName() {
  return name;
 }
 
 public void setName(String name) {
  this.name = name;
 }
 
 public String getNationality() {
  return nationality;
 }
 
 public void setNationality(String nationality) {
  this.nationality = nationality;
 }
 
 @Override
 public void writeExternal(ObjectOutput out) throws IOException {
 
  out.writeObject(name);
  out.writeObject(nationality);
 }
 
 @Override
 public void readExternal(ObjectInput in) throws IOException,
 ClassNotFoundException {
  name=(String) in.readObject();
  nationality=(String) in.readObject();
 
 }
 
}

Employee.java:

package org.arpit.javapostsforlearning;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
 
public class Employee extends Person implements Externalizable{
 
 int employeeId;
 String department;
 
 public Employee()
 {
 
 }
 public Employee(int employeeId,String name,String department,String nationality)
 {
  super(name,nationality);
  this.employeeId=employeeId;
  this.department=department;
  System.out.println("Employee:Constructor");
 }
 
 public int getEmployeeId() {
  return employeeId;
 }
 public void setEmployeeId(int employeeId) {
  this.employeeId = employeeId;
 }
 
 public String getDepartment() {
  return department;
 }
 public void setDepartment(String department) {
  this.department = department;
 }
 
 @Override
 public void writeExternal(ObjectOutput out) throws IOException {
 
  super.writeExternal(out);
  out.writeInt(employeeId);
  out.writeObject(department);
 }
 
 @Override
 public void readExternal(ObjectInput in) throws IOException,
 ClassNotFoundException {
 
  super.readExternal(in);
  employeeId=in.readInt();
  department=(String) in.readObject();
 
 }
}

ExternalizableMain.java:

package org.arpit.javapostsforlearning;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
 
public class ExternalizableMain {
 
 /**
  * @author Arpit Mandliya
  */
 public static void main(String[] args) {
 
  //Serialize
  Employee emp = new Employee(101,"Arpit","CS","Indian");
  System.out.println("Before serializing");
  System.out.println("Emp id: " + emp.getEmployeeId());
  System.out.println("Name: " + emp.getName());
  System.out.println("Department: " + emp.getDepartment());
  System.out.println("Nationality: " + emp.getNationality());
  System.out.println("************");
  System.out.println("Serializing");
  try
  {
   FileOutputStream fileOut = new FileOutputStream("employee.ser");
   ObjectOutputStream outStream = new ObjectOutputStream(fileOut);
   outStream.writeObject(emp);
   outStream.close();
   fileOut.close();
  }catch(IOException i)
  {
   i.printStackTrace();
  }
 
  //Deserialize
  System.out.println("************");
  System.out.println("Deserializing");
  emp = null;
  try
  {
   FileInputStream fileIn =new FileInputStream("employee.ser");
   ObjectInputStream in = new ObjectInputStream(fileIn);
   emp = (Employee) in.readObject();
   in.close();
   fileIn.close();
  }catch(IOException i)
  {
   i.printStackTrace();
   return;
  }catch(ClassNotFoundException c)
  {
   System.out.println("Employee class not found");
   c.printStackTrace();
   return;
  }
  System.out.println("After serializing");
  System.out.println("Emp id: " + emp.getEmployeeId());
  System.out.println("Name: " + emp.getName());
  System.out.println("Department: " + emp.getDepartment());
  System.out.println("Nationality: " + emp.getNationality());
 }
}

运行的结果:

Employee:Constructor
Before serializing
Emp id: 101
Name: Arpit
Department: CS
Nationality: Indian
************
Serializing
************
Deserializing
Person:Constructor
After serializing
Emp id: 101
Name: Arpit
Department: CS
Nationality: Indian

在这个例子中,由于Person类用自己的writeExternal和readExternal方法存储和恢复自己的字段,不用再子类中序列化父类的字段。不过如果你仔细观察下Employee 的writeExternal和readExternal方法,你会发现还是先调了下super.xxxx方法,这样说明实现externalizable的对象还得和父类协作才能序列化对象的状态。

Externalizable的缺点:

如果你修改了类的定义,你得对应的修改writeExternal和readExternal方法。 就如示例中如示,子类对象必须 和父类进行协作才能正确存储和恢复自己的状态。

原创文章转载请注明出处:Java的Externalizable接口

英文原文链接