當用SpotBugs檢測程式碼時,出現關於Serializable的警告訊息如下。
Bug: Class simple.People defines non-transient non-serializable instance field play
This Serializable class defines a non-primitive instance field which is neither transient, Serializable, or java.lang.Object, and does not appear to implement the Externalizable interface or the readObject() and writeObject() methods. Objects of this class will not be deserialized correctly if a non-Serializable object is stored in this field.
Rank: Troubling (14), confidence: High
Pattern: SE_NO_SUITABLE_CONSTRUCTOR_FOR_EXTERNALIZATION
Type: Se, Category: BAD_PRACTICE (Bad practice)
意思是說這個Class為可序列化的類別(Serializable class)(也就是此類別有實作Serializable
或其繼承的父類別為可序列化類別),而其成員變數(field)中有不可序列化的實例。這樣的狀況以SpotBugs掃描程式碼時就會出現如上警示。
例如定義一個People
類別實作Serializable
介面如下,所以People
為可序列化的類別。
如果有一成員變數Play
為未序列化(Non-serializable)的類別。
People
import java.io.Serializable;
public class People implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
private Play play; // <-- SpotBugs
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Play getPlay() {
return play;
}
public void setPlay(Play play) {
this.play = play;
}
}
下面是未實作Serializable
介面的類別Play
,為非序列化類別(Non-serializable class)。
Play
public class Play {
...
}
這Bug出現的原因是,序列化類別(People
)中的成員變數都必須也是可序列化的類別,若有非序列化的成員變數(Play
),可利用以下方法解決:
- 該成員變數必須使其序列化;
- 或在非序列化成員變數前加上
transient
關鍵字; - 或序列化類別實作
private void readObject()
及private void writeObject()
方法; - 或序列化類別實作
Externalizable
介面。
若未使用以上任一方法,則序程式進行序列化及反序列化的過程會出錯。
該成員變數必須使其序列化
Play
public class Play implements Serializable {
...
}
在非序列化成員變數前加上transient
關鍵字
People
import java.io.Serializable;
public class People implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
private transient Play play; // <-- 加上 transient
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Play getPlay() {
return play;
}
public void setPlay(Play play) {
this.play = play;
}
}
序列化類別實作private void readObject()
及private void writeObject()
方法。實作此方法是當須要對序列化及反序列化做特殊處理實,則須要實作這兩個方法,請參考ObjectOutputStream
API文件說明如下:
Classes that require special handling during the serialization and deserialization process must implement special methods with these exact signatures:
private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException; private void writeObject(java.io.ObjectOutputStream stream) throws IOException private void readObjectNoData() throws ObjectStreamException;
下面範例是將此序列化類別變成不可序列化,否則會拋出NotSerializableException
。
import java.io.IOException;
import java.io.NotSerializableException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class People implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private Play play;
private void writeObject(ObjectOutputStream out) throws IOException {
throw new NotSerializableException("Not Allowed");
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
throw new NotSerializableException("Not Allowed!");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Play getPlay() {
return play;
}
public void setPlay(Play play) {
this.play = play;
}
}
序列化類別實作Externalizable
介面。
實作Externalizable
的類別必須提供不帶參數的建構式,否則會出現java.io.InvalidClassException
錯誤。
People
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
public class People implements Externalizable {
private String name;
private Play play;
// 不帶參數的建構式
public People() {
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(this.name);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.name = in.readObject().toString();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Play getPlay() {
return play;
}
public void setPlay(Play play) {
this.play = play;
}
}
參考:
沒有留言:
張貼留言