序列化與反序列化
簡(jiǎn)介
1.序列化是將對(duì)象狀態(tài)轉(zhuǎn)換可保存或傳輸格式的過(guò)程
2.反序列化是將流轉(zhuǎn)化為對(duì)象的過(guò)程
應(yīng)用場(chǎng)景
1.將對(duì)象序列化永久的保存在磁盤(pán)上,例如Tomcat中將session就是序列化到磁盤(pán)中,這樣做可以減少內(nèi)存消耗
2.用于網(wǎng)絡(luò)中傳輸?shù)淖止?jié)序列,例如RPC框架中將對(duì)象序列化之后傳輸
實(shí)例
實(shí)現(xiàn)序列化與反序列化的方式主要有兩種,第一種實(shí)現(xiàn)Serialable接口,第二種實(shí)現(xiàn)實(shí)現(xiàn)Externalizable接口,如下會(huì)詳細(xì)介紹這兩種方式。
1.實(shí)現(xiàn)Serialable接口
如下代碼是對(duì)Person類(lèi)進(jìn)行序列化的實(shí)例,如下為Person實(shí)體類(lèi)實(shí)現(xiàn)了Serializable接口
public class Person implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
private Integer age;
private String sex;
...//此處省略的部分代碼
}
從上面代碼中可以看出,首先要實(shí)現(xiàn)類(lèi)的對(duì)象可以被序列化,首先的實(shí)現(xiàn)Serializable接口,還需要添加屬性serialVersionUID
1.1 屬性serialVersionUID的作用
在代碼中常用設(shè)置該屬性的值如下所示
//這是默認(rèn)方式
private static final long serialVersionUID = 1L;
//生成的
private static final long serialVersionUID = -2907193193159218377L;
在對(duì)象序列化時(shí)該屬性用于控制版本, 意思就是在序列化時(shí)該值就對(duì)應(yīng)著一個(gè)版本,反序列化時(shí)也會(huì)根據(jù)這個(gè)值進(jìn)行,若不顯示指定這個(gè)值,在類(lèi)編譯時(shí)期,java編譯器會(huì)自動(dòng)的給它加上一個(gè)UID,但是會(huì)出現(xiàn)的問(wèn)題是,只要對(duì)類(lèi)文件做了修改哪怕是新增了一個(gè)空格,都會(huì)導(dǎo)致UID變化,反序列化時(shí)就會(huì)出現(xiàn)問(wèn)題,因此在代碼中盡量明確指明這個(gè)值。
1.2 對(duì)象序列化
如下代碼通過(guò)ObjectOutputStream將對(duì)象Object序列化保存到文件中(fileName對(duì)應(yīng)的文件)
/** 序列化對(duì)象 **/
public static void serialObject(Object obj,String filename){
FileOutputStream out = null;
ObjectOutputStream objOut = null;
try {
out = new FileOutputStream(filename);
objOut = new ObjectOutputStream(out);
objOut.writeObject(obj);
} catch (Exception e) {
// TODO: handle exception
} finally{
try {
if (null != out) {
out.close();
}
if (null != objOut) {
objOut.close();
}
} catch (Exception e2) {
// TODO: handle exception
}
}
}
1.3 讀取文件中序列化的對(duì)象
如下代碼中是將文件中讀取數(shù)據(jù),然后反序列化
/**反序列化對(duì)象**/
public static Object unSerialObject(String filename){
Object obj = null;
File file = new File(filename);
FileInputStream in = null;
ObjectInputStream objIn = null;
try {
in = new FileInputStream(file);
objIn = new ObjectInputStream(in);
return objIn.readObject();
} catch (Exception e) {
e.printStackTrace();
} finally{
try {
if (null != in) {
in.close();
}
if (null != objIn) {
objIn.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
return obj;
}
1.4 測(cè)試及結(jié)果
測(cè)試代碼如下:
public static void main(String[] args) {
//構(gòu)造對(duì)象
Person person = new Person();
person.setAge(11);
person.setName("張三");
person.setSex("男");
String fileName = "D://a.txt";
//序列化
serialObject(person, fileName);
//反序列化
Person newPerson = (Person)unSerialObject(fileName);
System.out.println(newPerson);
}
結(jié)果如下:
1.在對(duì)應(yīng)目錄生產(chǎn)了a.txt文件
2.如下圖所示
[ name:張三,age:11, sex:男]
2.實(shí)現(xiàn)Externalizable接口
如下代碼為實(shí)現(xiàn)Externalizable接口的進(jìn)行序列化操作的過(guò)程
public class NewPerson implements Externalizable{
private static final long seriaVersionUID = 1L;
private String name;
private Integer age;
private String sex;
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(name);
out.writeObject(age);
out.writeObject(sex);
}
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
//注意的是讀取的順序和寫(xiě)入的順序一樣
name = (String) in.readObject();
age = (Integer) in.readObject();
sex = (String) in.readObject();
}
...//此處省略部分代碼
}
從代碼中可以看出,實(shí)現(xiàn)Externalizable接口后需要實(shí)現(xiàn)兩個(gè)方法,而這兩個(gè)方法正是與方法一的區(qū)別之處,可以控制序列化的對(duì)象,當(dāng)然第一種方法中也可以部分字段序列化,使用 transient關(guān)鍵字,或者使用static去修飾屬性
3.序列化與反序列化需要注意的點(diǎn)
3.1 對(duì)于對(duì)象中包含其他對(duì)象的,所有對(duì)象實(shí)現(xiàn)序列化接口
如下實(shí)例,Person中引用了Teacher類(lèi)
public class Person implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
private Integer age;
private String sex;
private Teacher teacher;
...//此處省略了部分代碼
}
如下測(cè)試代碼,若person對(duì)象中未引用Teacher對(duì)象,對(duì)于序列化而言沒(méi)有任何引用,能夠正常輸出,但是若引用了Teacher對(duì)象,則會(huì)報(bào)異常,分析源碼可知,ObjectOutputStream進(jìn)行序列化得過(guò)程,實(shí)際上時(shí)循環(huán)處理的,這對(duì)于這種嵌套的對(duì)象,需要全部序列化。
public static void main(String[] args) {
//構(gòu)造對(duì)象
Person person = new Person();
person.setAge(11);
person.setName("張三");
person.setSex("男");
//不加以下代碼不會(huì)報(bào)錯(cuò)
//Teacher te = new Teacher();
//te.setName("王五");
//person.setTeacher(te);
}