官方文檔理解
要使類(lèi)的成員變量可以序列化和反序列化涯捻,必須實(shí)現(xiàn)Serializable接口瞬痘。任何可序列化類(lèi)的子類(lèi)都是可序列化的崇堰。Serializable接口沒(méi)有提供任何方法和字段,只是標(biāo)記可以序列化婉商。
為了允許不可序列化類(lèi)的子類(lèi)可序列化龄减,子類(lèi)要承擔(dān)父類(lèi)的public项钮,protected和包內(nèi)可訪問(wèn)(default)修飾的字段。該父類(lèi)必須有一個(gè)子類(lèi)可訪問(wèn)的無(wú)參構(gòu)造器希停,去初始化它的屬性烁巫。
在反序列化過(guò)程中,不可序列化類(lèi)的字段通過(guò)public或protected修飾的無(wú)參構(gòu)造器初始化宠能。這個(gè)構(gòu)造器必須是子類(lèi)可訪問(wèn)的亚隙。而可序列化子類(lèi)的字段從流中恢復(fù)。
這里沒(méi)有提到使用friendly修飾的構(gòu)造器违崇,應(yīng)該是不確定子類(lèi)和父類(lèi)屬于同一包中阿弃。如果是一個(gè)包,應(yīng)該也可以羞延。因?yàn)閒riendly修飾的構(gòu)造器也可以被同一個(gè)包下的子類(lèi)訪問(wèn)渣淳。
如果對(duì)一個(gè)不可序列化對(duì)象進(jìn)行序列化操作時(shí),會(huì)拋出NotSerializableException標(biāo)記該類(lèi)不可序列化伴箩。
如果一個(gè)類(lèi)在序列化和反序列化中需要制定一些特殊操作水由,必須實(shí)現(xiàn)一下方法簽名的特殊方法:
private void writeObject(java.io.ObjectOutputStream out)
throws IOException
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException;
private void readObjectNoData()
throws ObjectStreamException;
writeObject()
方法職責(zé)是將特定類(lèi)的對(duì)象屬性輸出,這樣,相應(yīng)的readObject()
可以恢復(fù)砂客。可以調(diào)用out.defaultWriteObject()
使用默認(rèn)保存對(duì)象屬性的機(jī)制呵恢。out.defaultWriteObject()
自身不必考慮使用的變量是屬于父類(lèi)還是子類(lèi)鞠值。通過(guò)使用writeObject
或通過(guò)使用DataOutput支持的基本數(shù)據(jù)類(lèi)型的方法將各個(gè)字段寫(xiě)入ObjectOutputStream來(lái)保存狀態(tài)。
readObject()
主要責(zé)任是從流中讀取并恢復(fù)類(lèi)的字段渗钉。它可以通過(guò)調(diào)用in.defaultReadObject()
采用默認(rèn)機(jī)制恢復(fù)非靜態(tài)和non-transient字段彤恶。in.defaultReadObject()
使用流中的信息來(lái)將流中保存的對(duì)象的字段分配給當(dāng)前對(duì)象中相應(yīng)命名的字段。當(dāng)類(lèi)添加了新字段鳄橘,它依舊可以使用声离。in.defaultReadObject()
自身不必考慮使用的變量是屬于父類(lèi)還是子類(lèi)。通過(guò)使用writeObject
或通過(guò)使用DataOutput支持的基本數(shù)據(jù)類(lèi)型的方法將各個(gè)字段寫(xiě)入ObjectOutputStream來(lái)保存狀態(tài)瘫怜。
readObjectNoData()
:Serializable對(duì)象反序列化時(shí)术徊,由于序列化與反序列化提供的class版本不同,序列化的class的super class不同于序列化時(shí)的class的super class鲸湃;或者收到有敵意的流赠涮;或接收不完整;都會(huì)對(duì)初始化對(duì)象字段值時(shí)造成影響暗挑。針對(duì)這些情況可以在在該方法中實(shí)現(xiàn)這些字段的初始化笋除。如果沒(méi)有定義readObjectNoData()
,這些字段會(huì)初始化成JVM默認(rèn)值炸裆。
序列化類(lèi)在將對(duì)象寫(xiě)入流中時(shí)垃它,可以指定一個(gè)替代對(duì)象寫(xiě)入。但是必須實(shí)現(xiàn)精確的方法簽名:
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
如果writeReplace()
存在烹看,序列化時(shí)會(huì)被調(diào)用憎夷。它的權(quán)限修飾符可以是任何一個(gè),子類(lèi)遵循Java權(quán)限訪問(wèn)規(guī)則倔韭。
當(dāng)一個(gè)類(lèi)的對(duì)像從流中讀取時(shí)航棱,指派另一個(gè)類(lèi)作為返回值。必須實(shí)現(xiàn)精確的方法簽名:
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
該方法有著與writeReplace()
類(lèi)似的調(diào)用和訪問(wèn)規(guī)則靠胜。
Java允許為序列化的類(lèi)提供一個(gè)serialVersionUID的常量標(biāo)識(shí)該類(lèi)的版本掉瞳。只要serialVersionUID的值不變,Java就會(huì)把它們當(dāng)作相同的序列化版本浪漠。例如陕习,一個(gè)類(lèi)升級(jí)后,它的serialVersionUID類(lèi)變量值保持不變址愿,序列化機(jī)制也會(huì)把它們當(dāng)成同一個(gè)類(lèi)版本该镣。
ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
如果反序列時(shí),發(fā)送方的類(lèi)(指序列化時(shí)使用的類(lèi)文件)與接受方的(指反序列化時(shí)使用的類(lèi)文件)類(lèi)的各自serialVersionUID不同响谓,那么會(huì)拋出InvalidClassException異常损合。
JVM就會(huì)根據(jù)類(lèi)的各個(gè)方面計(jì)算出一個(gè)serialVersionUID的值省艳。不同的編譯器下會(huì)產(chǎn)生不同的serialVersionUID值。serialVersionUID值不同則會(huì)導(dǎo)致反序列化程序編譯失敗嫁审。解決辦法是顯示指定一個(gè)serialVersionUID跋炕。這樣,即使在某個(gè)對(duì)象被序列化后律适,它所對(duì)應(yīng)的類(lèi)被修改了辐烂,該對(duì)象也依然可以被正確的反序列化。
實(shí)踐
可序列化子類(lèi)默認(rèn)實(shí)現(xiàn)序列化
這應(yīng)該是毋庸置疑的捂贿。因?yàn)楦割?lèi)實(shí)現(xiàn)了Serializable接口纠修,那么子類(lèi)必然可序列化。
不可序列化父類(lèi)的子類(lèi)可否序列化
從文檔中得知厂僧,只要父類(lèi)有一個(gè)子類(lèi)可訪問(wèn)的無(wú)參構(gòu)造器能夠初始化父類(lèi)自身的字段扣草,就可行。那么沒(méi)有訪問(wèn)權(quán)限修飾符號(hào)修飾的無(wú)參構(gòu)造器可行嗎吁系?
public class FriendlyConstructorFather {
private int number;
FriendlyConstructorFather() {
this.number = 12;
}
public int getNumber() {
return this.number;
}
public void setNumber(int num) {
this.number = num;
}
}
public class FriendlyConstructorSon extends FriendlyConstructorFather
implements Serializable{
private int sonNum;
public FriendlyConstructorSon() {
}
public int getSonNum() {
return sonNum;
}
public void setSonNum(int number) {
sonNum = number;
}
}
public class TestFriendlyConstructor {
public static void main(String[] agrs) {
FriendlyConstructorSon son = new FriendlyConstructorSon();
son.setSonNum(2);
FileOutputStream fileOut = null;
ObjectOutputStream objectOut = null;
File file = new File("../file/TestFriendlyConstructor.txt");
try {
try {
fileOut = new FileOutputStream(file);
objectOut = new ObjectOutputStream(fileOut);
objectOut.writeObject(son);
}finally {
objectOut.close();
}
}catch(IOException e) {
e.printStackTrace();
}
FileInputStream fileIn = null;
ObjectInputStream objectIn = null;
try {
try {
fileIn = new FileInputStream(file);
objectIn = new ObjectInputStream(fileIn);
FriendlyConstructorSon resultSon =
(FriendlyConstructorSon) objectIn.readObject();
System.out.println(
"the father's number is " + resultSon.getNumber());
System.out.println(
"the son's num is " + resultSon.getSonNum());
}finally{
objectIn.close();
}
}catch(IOException e) {
e.printStackTrace();
}catch(ClassNotFoundException e) {
e.printStackTrace();
}
}
}
執(zhí)行后效果:
the father's number is 12
the son's num is 2
發(fā)現(xiàn)是可行的德召,但是要求子類(lèi)必須和父類(lèi)在同一個(gè)包中。
readObjectNoDate方法使用情況
原始Person.java
public class Person implements Serializable{
private int age;
public Person() {
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
}
并且通過(guò)序列化ObjectOutputStream輸出到test.txt文件中進(jìn)行保存汽纤。然后升級(jí)Person類(lèi):
public class Animal implements Serializable {
private String name;
public Animal() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private void readObjectNoData() throws ObjectStreamException{
this.name = "zhangsan";
}
}
public class Person extends Animal implements Serializable {
private int age;
public Person() {}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
編譯新的Person類(lèi)后上岗,使用新的Person.class文件從test.txt反序列化加載Person對(duì)象。并且使用getName()
獲取字段name
值蕴坪,執(zhí)行輸出如下:
the age is 25
the name is zhangsan
可以看到從信息不完整的序列化流中得到了完整的Person類(lèi)肴掷,這要?dú)w功于readObjectNoData()
。它初始化了name
字段背传。如果在這種Person發(fā)生升級(jí)的情況下呆瞻,沒(méi)有定義readObjectNoData()
那么name
字段會(huì)初始化它們的默認(rèn)值。readObjectNoData()
一般用于序列化對(duì)象和反序列化對(duì)象父類(lèi)不同的情況径玖,還有就是為了防止信息不完整痴脾,可以使用它來(lái)進(jìn)一步保證初始化。如果了類(lèi)中有自定義的readObject()
梳星,出現(xiàn)上述情況時(shí)赞赖,會(huì)用readObjectNoData()
替代它。
這里可以注意下冤灾,雖然升級(jí)了Person前域,且沒(méi)有顯示指定SerializableUID。序列化機(jī)制依舊認(rèn)為升級(jí)前后的Person是同一個(gè)版本韵吨。這是因?yàn)椋?/p>
- 只是修改了類(lèi)的方法匿垄,不會(huì)影響反序列化。
- 只是修改了類(lèi)的static Field或transient Field,不會(huì)影響反序列化椿疗。
- 修改了類(lèi)的非static和非transient Field漏峰,會(huì)影響序列化。
如果此時(shí)升級(jí)Person時(shí)变丧,繼承的Animal中有與原始Person相同的字段芽狗。那么readObjectNoDate()
的初始化無(wú)效果,會(huì)使用它們的默認(rèn)值初始化痒蓬。而且不顯示指定SerializableUID會(huì)拋出InvalidClassException異常。唯一的解決辦法就是顯示指定SerializableUID滴劲,即可執(zhí)行攻晒。
自定義序列化
case one
在一些特殊情況下,類(lèi)中某些實(shí)例變量是敏感信息不希望被序列化班挖,或者這些實(shí)例變量的類(lèi)型不可序列化為避免發(fā)生NotSerializableException異常鲁捏。可以通過(guò)關(guān)鍵詞transient修飾這些實(shí)例變量萧芙,指定類(lèi)在序列化時(shí)無(wú)需理會(huì)它們给梅。這樣一來(lái),反序列化后得到的對(duì)象中這些字段會(huì)被初始化為默認(rèn)值双揪。
序列化注意事項(xiàng):
- 對(duì)象的類(lèi)名动羽、Field(包括基本類(lèi)型、數(shù)組及對(duì)其他對(duì)象的引用)都會(huì)被序列化渔期,對(duì)象的static Field运吓,transient Field及方法不會(huì)被序列化;
- 實(shí)現(xiàn)Serializable接口的類(lèi)疯趟,如不想某個(gè)Field被序列化拘哨,可以使用transient關(guān)鍵字進(jìn)行修飾;
- 保證序列化對(duì)象的引用類(lèi)型Filed的類(lèi)也是可序列化的信峻,如不可序列化倦青,可以使用transient關(guān)鍵字進(jìn)行修飾,否則會(huì)序列化失旐镂琛产镐;
- 反序列化時(shí)必須要有序列化對(duì)象的類(lèi)的class文件,而且方法不會(huì)被序列化矾策;
- 當(dāng)通過(guò)文件網(wǎng)絡(luò)讀取序列化對(duì)象的時(shí)候磷账,必需按寫(xiě)入的順序來(lái)讀取。
使用transient關(guān)鍵字修飾實(shí)例變量避免序列化非常便捷贾虽,但該變量將被完全隔離在序列化機(jī)制之外逃糟,這樣導(dǎo)致在反序列化恢復(fù)的對(duì)象無(wú)法取得該實(shí)例變量值。Java還提供了一種自定義序列化機(jī)制,可以讓程序控制如何序列化各實(shí)例變量绰咽,甚至完全不序列化某些實(shí)例變量(與使用transient關(guān)鍵字的效果相同)菇肃。
public class CustomSerializable implements Serializable {
private String account;
private transient String password;
private int passwordCount;
public CustomSerializable(String name, String password)
throws Exception{
passwordCount = password.length();
for(int i = 0; i < passwordCount; i++) {
char c = password.charAt(i);
if(c < '0' && '9' < c) {
throw new Exception("the password is not correct!");
}
}
this.account = name;
this.password = password;
}
private String changePassword() {
byte[] bArray = new byte[passwordCount];
for(int i = 0; i < passwordCount; i++) {
bArray[i] = '*';
}
return new String(bArray);
}
private void writeObject(ObjectOutputStream out) throws IOException {
System.out.println("custom writeObject method execute!");
out.defaultWriteObject();
out.writeObject(changePassword());
}
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
System.out.println("custom readObject method execute!");
in.defaultReadObject();
this.password = (String) in.readObject();
}
@Override
public String toString() {
return "Account: " + account + "\nPassword: " + password +
"\nPasswrod Count: " + passwordCount;
}
}
在代碼中,使用了defaultWrite/ReadObject()
去執(zhí)行默認(rèn)的序列化機(jī)制取募。
在序列化中(自定義的writeObject())琐谤,由于password是一個(gè)敏感信息,所以使用transient修飾玩敏,將其排除在默認(rèn)序列化機(jī)制外斗忌。然后針對(duì)敏感信息自定義一套序列化操作,保護(hù)信息安全旺聚。
在反序列化中(自定義的readObject())织阳,首先使用默認(rèn)反序列化機(jī)制去初始化可序列化字段,然后針對(duì)自定義序列化中的password字段砰粹,使用相應(yīng)的readObject()
讀取唧躲,并且賦值給反序列化對(duì)象中的同名字段。
執(zhí)行效果碱璃,無(wú)自定義序列化和反序列化弄痹,transient修飾的password字段讀寫(xiě):
自定義序列化和反序列化后,transient修飾的password字段讀寫(xiě):
在代碼中還看到writeObject()
,readObject()
,readObjectNoDate()
都是private修飾的嵌器,但是序列化過(guò)程中一樣可以被外部的ObjectOut/InputStream調(diào)用肛真。
ObjectOutputStream在執(zhí)行自己的writeObject方法前會(huì)先通過(guò)反射在要被序列化的對(duì)象的類(lèi)中查找有無(wú)自定義的writeObject方法,如有的話(huà)嘴秸,則會(huì)優(yōu)先調(diào)用自定義的writeObject方法毁欣。因?yàn)椴檎曳瓷浞椒〞r(shí)使用的是getPrivateMethod,所以自定以的writeObject方法的作用域要被設(shè)置為private岳掐。通過(guò)自定義writeObject和readObject方法可以完全控制對(duì)象的序列化與反序列化凭疮。(詳情可見(jiàn)ObjectOutputStream中的writeSerialData方法,以及ObjectInputStream中的readSerialData方法串述。)
case two
Java序列化機(jī)制提供一種更徹底的序列化方式执解,writeReplace()
和readResolve()
。前者是在序列化過(guò)程中替換成其他對(duì)象纲酗,后者是在反序列化中替換掉readObject()
返回的實(shí)例衰腌。
注意兩者一般不同時(shí)使用,因?yàn)橥瑫r(shí)存在時(shí)觅赊,只會(huì)去執(zhí)行前者右蕊,而且不會(huì)調(diào)用自定義的writeObject()
和readobject()
。而后者執(zhí)行之前回去執(zhí)行writeObject()
和readobject()
吮螺,然后替換掉readObject()
返回的實(shí)例饶囚,并且拋起它帕翻。
在CustomSerializable.java中添加下列代碼:
private Object writeReplace() throws ObjectStreamException {
System.out.println("custom writeReplace method execute!");
return "No Permission to access the account'password!";
}
執(zhí)行序列化讀取:
在CustomSerializable.java中添加下列代碼:
private Object readResolve() throws ObjectStreamException {
System.out.println("custom readResolve method execute!");
return "The account's password is a private!";
}
執(zhí)行序列化讀嚷芊纭:
注意嘀掸,writeReplace()
和readResolve()
可以被任何權(quán)限修飾符修飾,且子類(lèi)訪問(wèn)這兩個(gè)方法遵循Java的權(quán)限訪問(wèn)規(guī)則规惰。這樣做的目的子類(lèi)可以使用父類(lèi)中已有的序列化操作睬塌,也可以覆寫(xiě)它們。
一般readResolve()
用于單例模式
一般類(lèi)實(shí)現(xiàn)單例模式的用途是保證該類(lèi)的實(shí)例只有一個(gè)歇万。但是對(duì)象序列化后揩晴,通過(guò)反序列化得到的對(duì)象是重構(gòu)的,也就是在堆內(nèi)存中重新創(chuàng)建的贪磺。所以無(wú)法保障唯一實(shí)例文狱。
public class SingletonSer implements Serializable {
private static SingletonSer instance;
private String name;
private int age;
private SingletonSer(String name, int age) {
this.name = name;
this.age = age;
}
public static SingletonSer getInstance() {
if(instance == null) {
instance = new SingletonSer("FoolishDev", 25);
}
return instance;
}
private Object readResolve() throws ObjectStreamException {
return getInstance();
}
}
執(zhí)行序列化讀取,在代碼中對(duì)序列化前后實(shí)例引用進(jìn)行比較(==)缘挽,得到
they are same? true
總結(jié)
Serializable反序列化時(shí),不會(huì)去調(diào)用類(lèi)的構(gòu)造器呻粹,除非類(lèi)的父類(lèi)不可序列化壕曼,會(huì)去調(diào)用子類(lèi)可訪問(wèn)的無(wú)參構(gòu)造器來(lái)初始化父類(lèi)字段。如果父類(lèi)和子類(lèi)都可序列化等浊,且各自自定義了序列化和反序列化方法腮郊,在整個(gè)序列化過(guò)程中父類(lèi)和子類(lèi)自定義的操作互不影響,各自執(zhí)行筹燕。
Externalizable
官方文檔理解
Externalizable實(shí)例類(lèi)可以實(shí)現(xiàn)序列化轧飞,而且承擔(dān)了自身內(nèi)容存儲(chǔ)和恢復(fù)的責(zé)任。Externalizable子類(lèi)需要實(shí)現(xiàn)兩個(gè)方法撒踪,writeExternal()
和readExternal()
过咬,通過(guò)這兩個(gè)方法可以完全控制它的對(duì)象和它父類(lèi)的流的內(nèi)容和格式。這兩個(gè)方法必須明確的和父類(lèi)協(xié)調(diào)制妄,處理它的狀態(tài)掸绞,同時(shí)替代了自定義的writeObject()
和readObject()
。
類(lèi)的序列化可以使用Serializable和Externalizable接口耕捞。對(duì)象的持久化可以使用這兩個(gè)接口衔掸。任何一個(gè)對(duì)象要存儲(chǔ),首先回去判斷有無(wú)實(shí)現(xiàn)Externalizable接口俺抽,如果有那么執(zhí)行writeExternal()
敞映,如果沒(méi)有但是實(shí)現(xiàn)了Serializable接口,那么使用ObjectOutputStream磷斧。
一旦類(lèi)實(shí)現(xiàn)了Externalizable就會(huì)替代了Serializable機(jī)制振愿。
當(dāng)一個(gè)Externalizable對(duì)象重建時(shí)捷犹,使用public no-arg 構(gòu)造器進(jìn)行創(chuàng)建實(shí)例,然后調(diào)用readExternal()
埃疫。Serializable對(duì)象創(chuàng)建是從流中讀取信息伏恐。Externalizable類(lèi)一樣可以使用writeReplace()
和readResolve()
替換對(duì)象。
實(shí)踐
Externalizable父類(lèi)構(gòu)造器必須是公共的栓霜?
public class Supertype implements Externalizable{
private String className;
private Date date;
Supertype() {
System.out.println("super no arg constructor executed!");
className = "Super";
date = new Date();
}
public void setClassName(String name) {
this.className = name;
}
@Override
public String toString() {
return "Class name is " + className + ". The date is " + date.toString();
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(className);
out.writeObject(date);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
className = (String) in.readObject();
date = (Date) in.readObject();
}
}
public class Subtype extends Supertype{
private String nickName;
public Subtype() {
System.out.println("sub no arg constructor executed!");
nickName = "Sub";
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
out.writeObject(nickName);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
nickName = (String) in.readObject();
}
public String getNickName() {
return nickName;
}
public void setNickName(String name) {
this.nickName = name;
}
@Override
public String toString() {
return super.toString() + ". The nick name is " + nickName;
}
}
執(zhí)行序列化操作后翠桦,輸出:
super no arg constructor executed!
sub no arg constructor executed!
super no arg constructor executed!
sub no arg constructor executed!
Class name is Supertype. The date is Sun Feb 26 16:10:45 CST 2017. The nick name is Subtype
發(fā)現(xiàn)父類(lèi)的構(gòu)造器可以被子類(lèi)訪問(wèn)即可,并沒(méi)有強(qiáng)制規(guī)定為public權(quán)限胳蛮。如果不可被子類(lèi)訪問(wèn)销凑,創(chuàng)建子類(lèi)對(duì)象時(shí)會(huì)拋出異常java.lang.IllegalAccessError。如果子類(lèi)的構(gòu)造器不是public修飾仅炊,反序列時(shí)會(huì)拋出異常java.io.InvalidClassException斗幼。
而且還需要注意,即使在反序列化時(shí)調(diào)用了Externalizable類(lèi)的無(wú)參構(gòu)造器去初始化了字段抚垄,最后依舊會(huì)使用序列化流中的信息賦值給同名字段蜕窿。
最后代碼中子類(lèi)并沒(méi)有覆寫(xiě)父類(lèi)中的writeExternal()
和readExternal()
,而是繼承了呆馁。這樣保證了父類(lèi)字段信息不丟失桐经。如果子類(lèi)覆寫(xiě)了這兩個(gè)方法,那么父類(lèi)字段會(huì)使用構(gòu)造器去初始化(實(shí)際上就是調(diào)用了子類(lèi)的公共無(wú)參構(gòu)造器浙滤,然后執(zhí)行內(nèi)部引用的父類(lèi)構(gòu)造器去初始化)阴挣。
這樣聯(lián)想到Serializable文檔中提到
writeObject()
和readObject()
無(wú)需考慮字段屬于父類(lèi)還是子類(lèi)。因?yàn)樗鼈儥?quán)限是private纺腊,所以是各自執(zhí)行字段的初始化畔咧,互不影響。
不可序列化父類(lèi)的子類(lèi)實(shí)現(xiàn)可序列化
如何去初始化父類(lèi)中的私有字段揖膜,以及子類(lèi)可訪問(wèn)的字段誓沸?
public class NoExternalSuper {
private String className;
private Date date;
protected NoExternalSuper() {
System.out.println("no-externalizable super no arg constructor executed!");
className = "Super";
date = new Date();
}
public void setClassName(String name) {
this.className = name;
}
@Override
public String toString() {
return "Class name is " + className + ",date is " + date.toString();
}
}
public class NoExternalSub
extends NoExternalSuper implements Externalizable{
private String nickName;
public NoExternalSub() {
System.out.println("no-externalizable sub no arg constructor executed!");
nickName = "Sub";
}
@Override
public void writeExternal(ObjectOutput out)
throws IOException {
out.writeObject(nickName);
}
@Override
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException {
nickName = (String) in.readObject();
}
@Override
public String toString() {
return super.toString() + ".The nick name is " + nickName;
}
}
執(zhí)行序列化操作后輸出結(jié)果:
no-externalizable super no arg constructor executed!
no-externalizable sub no arg constructor executed!
no-externalizable super no arg constructor executed!
no-externalizable sub no arg constructor executed!
Class name is Super,date is Sun Feb 26 16:36:34 CST 2017.The nick name is Sub
可以看出,父類(lèi)使用自身構(gòu)造器初始化自身字段(就是子類(lèi)公共無(wú)參構(gòu)造器引用的父類(lèi)構(gòu)造器)次氨。而子類(lèi)可訪問(wèn)的字段當(dāng)然可以在子類(lèi)中做出相應(yīng)序列化和反序列化操作蔽介。
總結(jié)
一個(gè)實(shí)現(xiàn)Externalizable接口的序列化類(lèi),必須有一個(gè)public-no-arg constructor煮寡,且會(huì)完全替代Serializable機(jī)制虹蓄,讓開(kāi)發(fā)者完全控制和自定義序列化和反序列化。所以需要注意協(xié)調(diào)處理父類(lèi)的屬性狀態(tài)幸撕。它雖然使用起來(lái)復(fù)雜薇组,但是性能比Serializable好。它實(shí)現(xiàn)持久化的方法是通過(guò)ObjectOutput和ObjectInput坐儿。