簡介
序列化:将對象寫入到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