序列化:序列化是將對(duì)象轉(zhuǎn)成字節(jié)的過(guò)程
反序列化:反序列化就是將字節(jié)還原為對(duì)象的過(guò)程
需要序列化的原因:
1)持久化:當(dāng)我們程序創(chuàng)建一個(gè)對(duì)象的時(shí)候,這個(gè)對(duì)象的生命周期在程序運(yùn)行結(jié)束浓若,或者線程執(zhí)行完畢之后,就可能被JVM直接回收蛇数,被銷毀掉。而當(dāng)我們希望說(shuō)某個(gè)對(duì)象在內(nèi)存中的某個(gè)狀態(tài)被保留下來(lái)诵原,這個(gè)時(shí)候就需要把當(dāng)前這個(gè)對(duì)象序列化成二進(jìn)制字節(jié)挽放,然后保存在硬盤(pán)里,當(dāng)需要用的時(shí)候辑畦,再反序列化還原這個(gè)對(duì)象即可。例如:當(dāng)一個(gè)程序并發(fā)量非常大纯出,需要?jiǎng)?chuàng)建幾十萬(wàn)個(gè)對(duì)象,而對(duì)象保存在內(nèi)存中,一般我們服務(wù)器內(nèi)存不會(huì)那么大箩言,或者說(shuō)在一個(gè)內(nèi)存不足的情況下焕襟,就會(huì)出現(xiàn)內(nèi)存溢出,而如果把這些對(duì)象暫存在硬盤(pán)里面,則就可以緩解內(nèi)存的壓力拄衰。
2)傳遞信息:當(dāng)處于不同網(wǎng)絡(luò)中的進(jìn)程或者服務(wù)在進(jìn)行消息傳遞的時(shí)候饵骨,進(jìn)程間通信是不認(rèn)識(shí)像Java這種對(duì)象的,需要把對(duì)象序列化成字節(jié)居触,這樣計(jì)算機(jī)才能識(shí)別,然后A進(jìn)程將對(duì)象序列化成字節(jié)饼煞,B進(jìn)程收到后,再將這些字節(jié)還原回對(duì)象息堂,則就需要反序列化块促。一般這種場(chǎng)景在rpc調(diào)用非常常見(jiàn)荣堰。
例子
序列化需要實(shí)現(xiàn)Serializable接口振坚,當(dāng)調(diào)用ObjectOutPutStream的writeObject()方法就可以將一個(gè)Object序列化成字節(jié)斋扰,而相對(duì)應(yīng)的ObjectInPutStream的readObject()方法則可以將二進(jìn)制字節(jié)反序列化為一個(gè)對(duì)象
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
transient private String sex;
public Student(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}
public class Main {
public static void main(String [] args) throws Exception {
Student student = new Student("yushengfan", 20, "男");
FileOutputStream fileOutputStream = new FileOutputStream("student.txt");
ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream);
// 此處將student對(duì)象序列化,并且將結(jié)果保存在student.txt里面
outputStream.writeObject(student);
outputStream.close();
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("student.txt"));
// 此處從student.txt讀取字節(jié)信息传货,并且反序列化為對(duì)象
Student student1 = (Student) inputStream.readObject();
System.out.println(student1);
}
}
輸出:
Student{name='yushengfan', age=20, sex='null'}
Process finished with exit code 0
可以看到,我們新建的時(shí)候逮壁,sex是初始化為"男",但是反序列化回來(lái)的時(shí)候粮宛,結(jié)果卻是null,原因是由于上面的sex字段是用transient修飾,該修飾是代表忧饭,序列化的時(shí)候秉氧,屏蔽對(duì)該變量序列化。如果把transient修飾去掉汁咏,實(shí)現(xiàn)Serializable接口的時(shí)候,則會(huì)采取默認(rèn)策略攘滩,即該對(duì)象的所有成員都序列化。
如:
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
private String sex;
public Student(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}
運(yùn)行:
public class Main {
public static void main(String [] args) throws Exception {
Student student = new Student("yushengfan", 20, "男");
FileOutputStream fileOutputStream = new FileOutputStream("student.txt");
ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream);
// 此處將student對(duì)象序列化,并且將結(jié)果保存在student.txt里面
outputStream.writeObject(student);
outputStream.close();
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("student.txt"));
// 此處從student.txt讀取字節(jié)信息赖瞒,并且反序列化為對(duì)象
Student student1 = (Student) inputStream.readObject();
System.out.println(student1);
}
}
結(jié)果為:
Student{name='yushengfan', age=20, sex='男'}
Process finished with exit code 0
自定義序列化
上面的例子是實(shí)現(xiàn)Serialization接口蚤假,該接口就是采取默認(rèn)策略,將所有非transient修飾的成員變量全都序列化磷仰,而官方也為我們提供了自定義序列化的策略,就是實(shí)現(xiàn)Externalizable接口
public class Student implements Externalizable {
private String name;
private int age;
private String sex;
// 實(shí)現(xiàn)Externalizable 接口的時(shí)候灶平,一定需要默認(rèn)構(gòu)造函數(shù),否則會(huì)報(bào)錯(cuò)
public Student() {}
public Student(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
// 此處只對(duì)成員變量name和age進(jìn)行序列化
out.writeObject(name);
out.write(age);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
// 此處只對(duì)name和age屬性進(jìn)行反序列化
this.name = (String) in.readObject();
this.age = in.read();
}
}
運(yùn)行:
public class Main {
public static void main(String [] args) throws Exception {
Student student = new Student("yushengfan", 20, "男");
FileOutputStream fileOutputStream = new FileOutputStream("student.txt");
ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream);
outputStream.writeObject(student);
outputStream.close();
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("student.txt"));
Student student1 = (Student) inputStream.readObject();
System.out.println(student1);
}
}
結(jié)果:
Student{name='yushengfan', age=20, sex='null'}
Process finished with exit code 0
此處可以看到罐监,雖然沒(méi)有用transient修飾變量瞒爬,但是通過(guò)自定義序列化,同意也可以實(shí)現(xiàn)transient的功能吆你;