建議13:避免為final變量復(fù)雜賦值
為final變量賦值可以通過方法賦值,即直接在聲明時通過方法返回值賦值。
public class Person implements Serializable {
private static final long serialVerisionUID = 91282334L;
//通過方法返回值為final變量賦值
public final String name = initName();
//初始化方法名
public String initName(){
return "混世魔王";
// return "德天使";
}
}
先序列化上面的代碼,然后把initName的返回值改為注釋的代碼。然后在反序列化,name值是什么?
是“混世魔王”,雖然上一條建議說final變量會被重新賦值,其中的“值”指的是簡單對象,簡單對象包括:8個基本數(shù)據(jù)類型,以及數(shù)組,字符串(字符串情況很復(fù)雜,不通過new關(guān)鍵字生成String對象的情況下,final變量的賦值與基本類型相同),但是不能通過方法賦值。
其中的原理是這樣的,保存到磁盤上(或網(wǎng)絡(luò)傳輸)的對象文件包括兩部分:
(1)類描述信息
包括包路徑、繼承關(guān)系、訪問權(quán)限、變量描述、變量訪問權(quán)限、方法簽名、返回值,以及變量的關(guān)聯(lián)類信息。要注意的一點是,它并不是class文件的翻版,它不記錄方法、構(gòu)造函數(shù)、static變量等的具體實現(xiàn)。之所以類描述會被保存,很簡單,是因為能去也能回嘛,這保證反序列化的健壯運行。
(2)非瞬態(tài)(transient關(guān)鍵字)和非靜態(tài)(static關(guān)鍵字)的實例變量值
注意,這里的值如果是一個基本類型,就是一個簡單值保存下來;如果是復(fù)雜對象,連該對象和關(guān)聯(lián)類信息一起保存,并且持續(xù)遞歸下去(關(guān)聯(lián)類也必須實現(xiàn)Serializable接口,否則會出現(xiàn)序列化異常),也就是說遞歸到最后,其實還是基本數(shù)據(jù)類型的保存。
正是因為這兩點原因,一個持久化后的對象文件會比一個class類文件大很多。
總結(jié)一下,反序列化時final變量在以下情況下不會被重新賦值:
- 通過構(gòu)造函數(shù)為final變量賦值。
- 通過方法返回值為final變量賦值。
- final修飾的屬性不是基本類型。