Java序列化是指把Java对象转换为字节序列的过程;而Java反序列化是指把字节序列恢复为Java对象的过程。从而达到网络传输、本地存储的效果。
   本文主要要看看JDK中使用Serializable和Externalizable接口来完成Java对象序列化,并给出部分属性序列化的几种方式,最终做出Serializable和Externalizable接口的几个方面的对比。
   注:本文不讨论为什么不用第三方工具包完成序列化等~
  
   序列化Serializable
      要实现Java对象的序列化,只要将类实现Serializable或Externalizable接口即可。
   采用类实现Serializable接口的序列化很简单,Java自动会将非transient修饰属性序列化到指定文件中去。
  
  举个例子:
  import java.io.Serializable; import java.util.List;  /**  * @Type Book.java  * @Desc   * @author wangmengjun  * @date 2017年12月1日 下午7:16:29  * @version   */ public class Book implements Serializable {      private static final long serialVersionUID = -6212470156629515269L;      /**书名*/     private String name;      /**ISBN*/     private String isbn;      /**作者*/     private List<String> authors;      /**      * @return the name      */     public String getName() {         return name;     }      /**      * @param name the name to set      */     public void setName(String name) {         this.name = name;     }      /**      * @return the isbn      */     public String getIsbn() {         return isbn;     }      /**      * @param isbn the isbn to set      */     public void setIsbn(String isbn) {         this.isbn = isbn;     }      /**      * @return the authors      */     public List<String> getAuthors() {         return authors;     }      /**      * @param authors the authors to set      */     public void setAuthors(List<String> authors) {         this.authors = authors;     }      /* (non-Javadoc)      * @see java.lang.Object#toString()      */     @Override     public String toString() {         return "Book [name=" + name + ", isbn=" + isbn + ", authors=" + authors + "]";     }  } 
  然后编写一个用于序列化和反序列的小工具类,
  import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream;   /**  * @Type SerializationUtil.java  * @Desc   * @author wangmengjun  * @date 2017年12月1日 下午7:23:04  * @version   */ public class SerializationUtil {       /**      * 从一个给定的文件完成反序列化      */     public static Object deserialize(String fileName) throws IOException,             ClassNotFoundException {         FileInputStream fis = new FileInputStream(fileName);         BufferedInputStream bis = new BufferedInputStream(fis);         ObjectInputStream ois = new ObjectInputStream(bis);         Object obj = ois.readObject();         ois.close();         return obj;     }       /**      * 将给定的对象序列化到指定的文件中去      */     public static void serialize(Object obj, String fileName)             throws IOException {           FileOutputStream fos = new FileOutputStream(fileName);         BufferedOutputStream bos = new BufferedOutputStream(fos);         ObjectOutputStream oos = new ObjectOutputStream(bos);         oos.writeObject(obj);         oos.close();     } }  
  写个测试类,测试一下:
  public class SerializableTest {      public static void main(String[] args) throws IOException, ClassNotFoundException {                  Book book = new  Book();         book.setIsbn("ABC123456789");         book.setName("Hello Java");         book.setAuthors(Arrays.asList("John","Eric"));         //book==>Book [name=Hello Java, isbn=ABC123456789, authors=[John, Eric]]          System.out.println("book==>" + book);                  /**          * 将book对象序列化到book.temp文件中去          */         String fileName = "book.temp";         SerializationUtil.serialize(book, fileName);                  /**          * 从book.temp文件中,反序列化一个Book对象          */         Book deserializedBook = (Book) SerializationUtil.deserialize(fileName);         //deserializedBook==>Book [name=Hello Java, isbn=ABC123456789, authors=[John, Eric]]         System.out.println("deserializedBook==>" + deserializedBook);     } }
  一个简单的示例,就完成了Book对象的序列化和反序列化。
  在上述示例中,Book对象中的所有的属性都被序列化。如果里面存在部分属性,我们不想要被序列化,该如何做呢?
   
   部分属性序列化
  如果只想将部分属性进行序列化,可以采用如下几种方法:
     - 使用transient关键字
- 添加writeObject和readObject方法
- 使用Externalizable实现
使用transient关键字
     对属性添加transient关键字,可以防止该属性序列化~
  
  如下示例中,我们不想isbn和authors属性被序列,添加上transient关键字实现一下~
  public class Book implements Serializable {  	private static final long serialVersionUID = -6212470156629515269L;  	/** 书名 */ 	private String name;  	/** ISBN */ 	private transient String isbn;  	/** 作者 */ 	private transient List<String> authors;   ... ...   }
  运行上述提到的SerializableTest.java程序,输出如下结果,我们可以看出isbn和authors的值都为null,表明这两个属性没有被序列化~
  book==>Book [name=Hello Java, isbn=ABC123456789, authors=[John, Eric]] deserializedBook==>Book [name=Hello Java, isbn=null, authors=null]
   添加writeObject和readObject方法
  另外,我们也可以采用编写私有方法writeObject和readObject,完成部分属性的序列化。修改Book类,增加writeObject 和 readObject方法,如:
  public class Book implements Serializable {  	private static final long serialVersionUID = -6212470156629515269L;  	/** 书名 */ 	private String name;  	/** ISBN */ 	private String isbn;  	/** 作者 */ 	private List<String> authors;  	private void writeObject(ObjectOutputStream oos) throws IOException { 		// oos.defaultWriteObject(); 		oos.writeObject(name); 		oos.writeObject(isbn); 	}  	private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { 		// ois.defaultReadObject(); 		name = (String) ois.readObject(); 		isbn = (String) ois.readObject(); 	}  ... ...  }
  在上述示例中,我们选择序列化的属性为name和isbn,书本作者authors没有序列化~
  同样,使用SerializableTest.java类测试一下,结果如下:
  book==>Book [name=Hello Java, isbn=ABC123456789, authors=[John, Eric]] deserializedBook==>Book [name=Hello Java, isbn=ABC123456789, authors=null]
     注意:
   这里的writeObject和readObject是private的且是void的~
  
     Java调用ObjectOutputStream类检查其是否有私有的,无返回值的writeObject方法,如果有,其会委托该方法进行对象序列化。
  
  检查是否有合适的方法如下:
  
  writeObjectMethod = getPrivateMethod(cl, "writeObject",                             new Class<?>[] { ObjectOutputStream.class },                             Void.TYPE);                         readObjectMethod = getPrivateMethod(cl, "readObject",                             new Class<?>[] { ObjectInputStream.class },                             Void.TYPE);                         readObjectNoDataMethod = getPrivateMethod(                             cl, "readObjectNoData", null, Void.TYPE);                         hasWriteObjectData = (writeObjectMethod != null);
   使用Externalizable实现
  还有一种方式,就是使用Externalizable完成部分属性的序列化。
     Externalizable继承自Serializable,使用Externalizable接口需要实现writeExternal以及readExternal方法~在writeExternal方法中,写入想要外部序列化的元素~
  
  public interface Externalizable extends java.io.Serializable {     /**      * The object implements the writeExternal method to save its contents      * by calling the methods of DataOutput for its primitive values or      * calling the writeObject method of ObjectOutput for objects, strings,      * and arrays.      *      * @serialData Overriding methods should use this tag to describe      *             the data layout of this Externalizable object.      *             List the sequence of element types and, if possible,      *             relate the element to a public/protected field and/or      *             method of this Externalizable class.      *      * @param out the stream to write the object to      * @exception IOException Includes any I/O exceptions that may occur      */     void writeExternal(ObjectOutput out) throws IOException;      /**      * The object implements the readExternal method to restore its      * contents by calling the methods of DataInput for primitive      * types and readObject for objects, strings and arrays.  The      * readExternal method must read the values in the same sequence      * and with the same types as were written by writeExternal.      *      * @param in the stream to read data from in order to restore the object      * @exception IOException if I/O errors occur      * @exception ClassNotFoundException If the class for an object being      *              restored cannot be found.      */     void readExternal(ObjectInput in) throws IOException, ClassNotFoundException; }
  修改Book类的内容,如下:
  import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.List;  /**  * @Type Book.java  * @Desc  * @author wangmengjun  * @date 2017年12月1日 下午7:16:29  * @version  */ public class Book implements Externalizable {  	/** 书名 */ 	private String name;  	/** ISBN */ 	private String isbn;  	/** 作者 */ 	private List<String> authors;  	@Override 	public void writeExternal(ObjectOutput out) throws IOException { 		out.writeObject(name); 		out.writeObject(isbn); 	}  	@Override 	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { 		name = (String) in.readObject(); 		isbn = (String) in.readObject(); 	}  	/** 	 * @return the name 	 */ 	public String getName() { 		return name; 	}  	/** 	 * @param name 	 *            the name to set 	 */ 	public void setName(String name) { 		this.name = name; 	}  	/** 	 * @return the isbn 	 */ 	public String getIsbn() { 		return isbn; 	}  	/** 	 * @param isbn 	 *            the isbn to set 	 */ 	public void setIsbn(String isbn) { 		this.isbn = isbn; 	}  	/** 	 * @return the authors 	 */ 	public List<String> getAuthors() { 		return authors; 	}  	/** 	 * @param authors 	 *            the authors to set 	 */ 	public void setAuthors(List<String> authors) { 		this.authors = authors; 	}  	/* 	 * (non-Javadoc) 	 *  	 * @see java.lang.Object#toString() 	 */ 	@Override 	public String toString() { 		return "Book [name=" + name + ", isbn=" + isbn + ", authors=" + authors + "]"; 	}  } 
  同样,使用SerializableTest.java类测试一下,同样获得如下结果:
  book==>Book [name=Hello Java, isbn=ABC123456789, authors=[John, Eric]] deserializedBook==>Book [name=Hello Java, isbn=ABC123456789, authors=null] 
   Externalizable vs Serializable
  Externalizable和Serializable的一些比较点,如下:
  【1】 Serializable 是标识接口
  public interface Serializable { }
  实现该接口,无需重写任何方法;
  public interface Externalizable extends java.io.Serializable {          void writeExternal(ObjectOutput out) throws IOException;          void readExternal(ObjectInput in) throws IOException, ClassNotFoundException; }
  Externalizable 接口继承于Serializable,实现该接口,需要重写readExternal和writeExternal方法~
  【2】Serializable提供了两种方式进行对象的序列化,
     - 采用默认序列化方式,将非transatient和非static的属性进行序列化
- 编写readObject和writeObject完成部分属性的序列化
Externalizable 接口的序列化,需要重写writeExternal和readExternal方法,并且在方法中编写相关的逻辑完成序列化和反序列化。
  【3】Externalizable接口的实现方式一定要有默认的无参构造函数~
  
  如果,没有无参构造函数,反序列化会报错~ 验证一下~ Book添加一个有参数的Book构造函数~
  public class Book implements Externalizable {  	/** 书名 */ 	private String name;  	/** ISBN */ 	private String isbn;  	/** 作者 */ 	private List<String> authors; 	 	public Book(String name) { 		this.name = name; 	}  ... ...  }
     这样,序列化、反序列化之后,报no valid constructor的异常~
  
  Exception in thread "main" java.io.InvalidClassException: Book; no valid constructor 	at java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(ObjectStreamClass.java:150) 	at java.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:768) 	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1772) 	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350) 	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370) 	at SerializationUtil.deserialize(SerializationUtil.java:27) 	at SerializableTest.main(SerializableTest.java:33)
  
     Serializable接口实现,其采用反射机制完成内容恢复,没有一定要有无参构造函数的限制~
  
  【4】采用Externalizable无需产生序列化ID(serialVersionUID)~而Serializable接口则需要~
  【5】相比较Serializable, Externalizable序列化、反序列更加快速,占用相比较小的内存
  在项目中,大部分的类还是推荐使用Serializable, 有些类可以使用Externalizable接口,如:
     - 完全控制序列的流程和逻辑
- 需要大量的序列化和反序列化操作,而你比较关注资源和性能~ 当然,这种情况下,我们一般还会考虑第三方序列化/反序列化工具,如protobuf等进行序列化和反序列化操作~