天天看點

Java序列化基礎

簡介

序列化:将對象寫入到IO流中

反序列化:從IO流中恢複對象

意義:序列化機制允許将實作序列化的Java對象轉換位位元組序列,這些位元組序列可以儲存在磁盤上,或通過網絡傳輸,以達到以後恢複成原來的對象。序列化機制使得對象可以脫離程式的運作而獨立存在。

使用場景:所有可在網絡上傳輸的對象都必須是可序列化的,比如RMI(remote method invoke,即遠端方法調用),傳入的參數或傳回的對象都是可序列化的,否則會出錯;所有需要儲存到磁盤的java對象都必須是可序列化的。通常建議:程式建立的每個JavaBean類都實作Serializeable接口。

注意事項

1.實作方式有實作Serializable接口和實作Externalizable接口兩種模式

2.可序列化的類加上serialVersionUID 版本号,有利于JVM遷移

3.對象類名,執行個體變量包括基本類型,數組,引用都會被序列化(transient執行個體變量不被序列化);方法和類變量都不會被序列化

4.反序列化必須按照寫入順序

5.單例類序列化需要重寫readResolve方法

6.對象不會重複序列化,多次序列化時隻有第一次序列化為位元組流,其他儲存序列号

7.反序列化建立對象不會調用構造方法

序列化實作的方式

如果需要将某個對象儲存到磁盤上或者通過網絡傳輸,那麼這個類應該實作Serializable接口或者Externalizable接口之一

1.java預設為Serializable接口提供序列化和反序列化支援,一旦實作這個接口就表明該對象支援序列化。

/**
 * 
 */
package com.mlycan.smoon.home.serializable;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * @author Administrator
 *
 */
public class Person  implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = 6950744995811540444L;

	/**
	 * 
	 */
	public Person() {
	}
	private String name;
	private int age;
	  //我不提供無參構造器
	public Person(String name, int age) {
	      this.name = name;
	      this.age = age;
	}
	@Override
   public String toString() {
       return "Person{" +
              "name='" + name + '\'' +
              ", age=" + age +
              '}';
   }
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public static void main(String[] args) {
		 //1.try-with-resource建立檔案流
	      try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:/eclipse/workspace/work/person.txt"))){
	          
	          Person person = new Person("yuwei", 23);
	         //2.将對象序列化到檔案
	          oos.writeObject(person);
	          
	      } catch (Exception e) {
	          e.printStackTrace();
	      }
	      //3.反序列化
	      try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:/eclipse/workspace/work/person.txt"))) {
	             Person brady = (Person) ois.readObject();
	             System.out.println(brady);
	      } catch (Exception e) {
	             e.printStackTrace();
	      }
	  }

}
           

2.序列化對象的引用也必須是序列化,當引用的非序列化對象不指派時可以正常序列化,當引用對象指派時序列化會抛出java.io.NotSerializableException異常

/**
 * 
 */
package com.mlycan.smoon.home.serializable;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * @author Administrator
 *
 */
class Student {

	public Student() {
		// TODO Auto-generated constructor stub
	}
	private String name;
	private int age;
	  //我不提供無參構造器
	public Student(String name, int age) {
	      this.name = name;
	      this.age = age;
	}
	@Override
   public String toString() {
       return "Person{" +
              "name='" + name + '\'' +
              ", age=" + age +
              '}';
   }
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}
public class Person  implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = 6950744995811540444L;

	/**
	 * 
	 */
	public Person() {
	}
	private String name;
	private int age;
	
	private Student student;
	  //我不提供無參構造器
	public Person(String name, int age) {
	      this.name = name;
	      this.age = age;
	}
	@Override
   public String toString() {
       return "Person{" +
              "name='" + name + '\'' +
              ", age=" + age +
              '}';
   }
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	
	public Student getStudent() {
		return student;
	}
	public void setStudent(Student student) {
		this.student = student;
	}
	public static void main(String[] args) {
		 //1.try-with-resource建立檔案流
	      try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:/eclipse/workspace/work/person.txt"))){
	          
	          Person person = new Person("yuwei", 23);
	          Student student = new Student("yuwei", 23);
	          person.setStudent(student);
	         //2.将對象序列化到檔案
	          oos.writeObject(person);
	          
	      } catch (Exception e) {
	          e.printStackTrace();
	      }
	      //3.反序列化
	      try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:/eclipse/workspace/work/person.txt"))) {
	             Person brady = (Person) ois.readObject();
	             System.out.println(brady);
	      } catch (Exception e) {
	             e.printStackTrace();
	      }
	  }

}
           

異常:

java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: com.mlycan.smoon.home.serializable.Student
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1571)
	at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2278)
	at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2202)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2060)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1567)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:427)
	at com.mlycan.smoon.home.serializable.Person.main(Person.java:110)
           

3.自定義序列化

自定義序列化可以通過transient關鍵字設定需要執行個體化的屬性,也可以通過實作writeReplace和readResolve()方法實作,或者通過實作Externalizable接口強制自定義序列化。這裡主要介紹一下通過實作Externalizable接口,必須實作writeExternal、readExternal方法。

/**
 * 
 */
package com.mlycan.smoon.home.serializable;

import java.io.Externalizable;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;

/**
 * @author Administrator
 *
 */
public class ExPerson implements Externalizable {

	/**
	 * 
	 */
	public ExPerson() {
		// TODO Auto-generated constructor stub
	}
	private String name;
	private int age;
	
	  //我不提供無參構造器
	public ExPerson(String name, int age) {
	      this.name = name;
	      this.age = age;
	}
	@Override
   public String toString() {
       return "Person{" +
              "name='" + name + '\'' +
              ", age=" + age +
              '}';
   }
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	


	@Override
	public void writeExternal(ObjectOutput out) throws IOException {
		//将name反轉後寫入二進制流
        StringBuffer reverse = new StringBuffer(name).reverse();
        System.out.println(reverse.toString());
        out.writeObject(reverse);
        out.writeInt(age);
	}

	@Override
	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
		this.name = ((StringBuffer) in.readObject()).reverse().toString()+"-external";;
        System.out.println(name);
        this.age = in.readInt()+1;		
	}
	/**
	 * @param args
	 */
	public static void main(String[] args) throws IOException, ClassNotFoundException {
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ExPerson.txt"));
             ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ExPerson.txt"))) {
            oos.writeObject(new ExPerson("brady", 23));
            ExPerson ep = (ExPerson) ois.readObject();
            System.out.println(ep);
        }
    }
	
}
           

輸出結果:

ydarb

brady-external

Person{name='brady-external', age=24}

在重寫readExternal時,改變了序列化對象的屬性值,是以在這裡看到和原來序列化時不一樣的結果。

參考:

本文隻寫了我個人關注的部分,更具體的内容建議參考下面這篇部落格,非常完整。

https://www.cnblogs.com/9dragon/p/10901448.html