电竞比分网-中国电竞赛事及体育赛事平台

分享

怎樣對帶有不可序列化屬性的Java對象進行序列化

 再生資源保存站 2018-05-03

出于很多原因我們想使用自定義的序列化方法取代Java默認的機制。一個最常見的原因是提高性能,而另一個原因是有時候我們無法使用默認的序列化方法。在這篇文章中,我們具體來討論怎樣通過定制的序列化方法,對一個較大的、帶有不可序列化屬性的對象進行序列化

下面這段代碼定義了一個簡單的類。它可以把一個給定的對象序列化到一個指定的文件,或者從相同的文件中把對象反序列化出來。在這片文章中,我將使用這個類進行演示。

SerializationDemonstrator.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package dustin.examples.serialization; 
   
import static java.lang.System.out;       
import java.io.FileInputStream; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.ObjectInputStream; 
import java.io.ObjectOutputStream; 
   
/**
 * Simple serialization/deserialization demonstrator.
 
 * @author Dustin
 */ 
public class SerializationDemonstrator 
   /**
    * Serialize the provided object to the file of the provided name.
    * @param objectToSerialize Object that is to be serialized to file; it is
    *     best that this object have an individually overridden toString()
    *     implementation as that is used by this method for writing our status.
    * @param fileName Name of file to which object is to be serialized.
    * @throws IllegalArgumentException Thrown if either provided parameter is null.
    */ 
   public static <T> void serialize(final T objectToSerialize, final String fileName) 
   
      if (fileName == null
      
         throw new IllegalArgumentException( 
            "Name of file to which to serialize object to cannot be null."); 
      
      if (objectToSerialize == null
      
         throw new IllegalArgumentException("Object to be serialized cannot be null."); 
      
      try (FileOutputStream fos = new FileOutputStream(fileName); 
           ObjectOutputStream oos = new ObjectOutputStream(fos)) 
      
         oos.writeObject(objectToSerialize); 
         out.println("Serialization of Object " + objectToSerialize + " completed."); 
      
      catch (IOException ioException) 
      
         ioException.printStackTrace(); 
      
   
   
   /**
    * Provides an object deserialized from the file indicated by the provided
    * file name.
    
    * @param <T> Type of object to be deserialized.
    * @param fileToDeserialize Name of file from which object is to be deserialized.
    * @param classBeingDeserialized Class definition of object to be deserialized
    *    from the file of the provided name/path; it is recommended that this
    *    class define its own toString() implementation as that will be used in
    *    this method's status output.
    * @return Object deserialized from provided filename as an instance of the
    *    provided class; may be null if something goes wrong with deserialization.
    * @throws IllegalArgumentException Thrown if either provided parameter is null.
    */ 
   public static <T> T deserialize(final String fileToDeserialize, final Class<T> classBeingDeserialized) 
   
      if (fileToDeserialize == null
      
         throw new IllegalArgumentException("Cannot deserialize from a null filename."); 
      
      if (classBeingDeserialized == null
      
         throw new IllegalArgumentException("Type of class to be deserialized cannot be null."); 
      
      T objectOut = null
      try (FileInputStream fis = new FileInputStream(fileToDeserialize); 
           ObjectInputStream ois = new ObjectInputStream(fis)) 
      
         objectOut = (T) ois.readObject(); 
         out.println("Deserialization of Object " + objectOut + " is completed."); 
      
      catch (IOException | ClassNotFoundException exception) 
      
         exception.printStackTrace(); 
      
      return objectOut; 
   

下面這段代碼給出了一個使用SerializationDemonstrator類序列化和反序列化標準的Java字符串的例子。字符串是支持序列化的。代碼之后的截圖顯示了在Netbeans中運行該類的serialize和deserialize方法后的輸出。

Running SerializationDemonstrator Methods on String

1
2
SerializationDemonstrator.serialize("Inspired by Actual Events", "string.dat"); 
final String stringOut = SerializationDemonstrator.deserialize("string.dat", String.class);

下面這兩段代碼定義了Person和CityState兩個類。CityState是Person的一個屬性。可以看到盡管Person實現(xiàn)了Serializable接口,CityState卻沒有。

Person.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package dustin.examples.serialization; 
   
import java.io.Serializable; 
   
/**
 * Person class.
 
 * @author Dustin
 */ 
public class Person implements Serializable 
   private String lastName; 
   private String firstName; 
   private CityState cityAndState; 
   
   public Person( 
      final String newLastName, final String newFirstName, 
      final CityState newCityAndState) 
   
      this.lastName = newLastName; 
      this.firstName = newFirstName; 
      this.cityAndState = newCityAndState; 
   
   
   public String getFirstName() 
   
      return this.firstName; 
   
   
   public String getLastName() 
   
      return this.lastName; 
   
   
   @Override 
   public String toString() 
   
      return this.firstName + " " + this.lastName + " of " + this.cityAndState; 
   

CityState.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package dustin.examples.serialization; 
   
/**
 * Simple class storing city and state names that is NOT Serializable.
 
 * @author Dustin
 */ 
public class CityState 
   private final String cityName; 
   private final String stateName; 
   
   public CityState(final String newCityName, final String newStateName) 
   
      this.cityName = newCityName; 
      this.stateName = newStateName; 
   
   
   public String getCityName() 
   
      return this.cityName; 
   
   
   public String getStateName() 
   
      return this.stateName; 
   
   
   @Override 
   public String toString() 
   
      return this.cityName + ", " + this.stateName; 
   

下面這段代碼演示了使用SerializationDemonstrator序列化Person類。由于包含了一個不可序列化的屬性CityState,在之后截圖里,我們可以看到Netbean拋出了異常。

Running SerializationDemonstrator Methods on Serializable Person with Non-Serializable CityState

1
2
3
4
final Person personIn = new Person("Flintstone", "Fred", new CityState("Bedrock", "Cobblestone")); 
SerializationDemonstrator.serialize(personIn, "person.dat"); 
final Person personOut = SerializationDemonstrator.deserialize("person.dat", Person.class); 

在這個例子里,由于CityState類是我們自己寫的,我們可以使它支持序列化。但是如果這個類屬于一個第三方的框架或者庫,我們就很難去修改這個類。但是我們可以修改Person類,通過使用自定義的序列化和反序列化方法,使它和CityState類一起正常工作。下面這段代碼定義了一個從Person類改過來的SerializablePerson類。

SerializablePerson.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package dustin.examples.serialization; 
   
import java.io.IOException; 
import java.io.InvalidObjectException; 
import java.io.ObjectInputStream; 
import java.io.ObjectOutputStream; 
import java.io.ObjectStreamException; 
import java.io.Serializable; 
   
/**
 * Person class.
 
 * @author Dustin
 */ 
public class SerializablePerson implements Serializable 
   private String lastName; 
   private String firstName; 
   private CityState cityAndState; 
   
   public SerializablePerson( 
      final String newLastName, final String newFirstName, 
      final CityState newCityAndState) 
   
      this.lastName = newLastName; 
      this.firstName = newFirstName; 
      this.cityAndState = newCityAndState; 
   
   
   public String getFirstName() 
   
      return this.firstName; 
   
   
   public String getLastName() 
   
      return this.lastName; 
   
   
   @Override 
   public String toString() 
   
      return this.firstName + " " + this.lastName + " of " + this.cityAndState; 
   
   
   /**
    * Serialize this instance.
    
    * @param out Target to which this instance is written.
    * @throws IOException Thrown if exception occurs during serialization.
    */ 
   private void writeObject(final ObjectOutputStream out) throws IOException 
   
      out.writeUTF(this.lastName); 
      out.writeUTF(this.firstName); 
      out.writeUTF(this.cityAndState.getCityName()); 
      out.writeUTF(this.cityAndState.getStateName()); 
   
    
   /**
    * Deserialize this instance from input stream.
    
    * @param in Input Stream from which this instance is to be deserialized.
    * @throws IOException Thrown if error occurs in deserialization.
    * @throws ClassNotFoundException Thrown if expected class is not found.
    */ 
   private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException 
   
      this.lastName = in.readUTF(); 
      this.firstName = in.readUTF(); 
      this.cityAndState = new CityState(in.readUTF(), in.readUTF()); 
   
   
   private void readObjectNoData() throws ObjectStreamException 
   
      throw new InvalidObjectException("Stream data required"); 
   

在上面這段代碼中,SerializablePerson有自定義的writeobject和readObject方法。它們以適當?shù)姆绞教幚鞢ityState的序列化和反序列化。下面這段代碼使用SerializationDemonstrator運行了這個類,我們可以看到這次的運行是成功的。

Running SerializationDemonstrator on SerializablePerson

1
2
3
4
final SerializablePerson personIn = new SerializablePerson("Flintstone", "Fred", new CityState("Bedrock", "Cobblestone")); 
SerializationDemonstrator.serialize(personIn, "person1.dat"); 
final SerializablePerson personOut = SerializationDemonstrator.deserialize("person1.dat", SerializablePerson.class); 

上面描述的這個方法可以允許我們在一個可序列化的類中使用不可序列化的屬性,而且不需要transient?,F(xiàn)在看上去已經(jīng)挺不錯了,但是如果前面這個CityState要在多個需要序列化的類中使用,更好的方式是用一個支持序列化的Decorator去修飾CityState。然后在那些需要做序列化的類中使用這個Decorator。下面這段代碼定義了SerializableCityState。它是CityState的一個支持序列化的Decorator版本。

SerializableCityState.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package dustin.examples.serialization; 
   
import java.io.IOException; 
import java.io.InvalidObjectException; 
import java.io.ObjectInputStream; 
import java.io.ObjectOutputStream; 
import java.io.ObjectStreamException; 
import java.io.Serializable; 
   
/**
 * Simple class storing city and state names that IS Serializable. This class
 * decorates the non-Serializable CityState class and adds Serializability.
 
 * @author Dustin
 */ 
public class SerializableCityState implements Serializable 
   private CityState cityState; 
   
   public SerializableCityState(final String newCityName, final String newStateName) 
   
      this.cityState = new CityState(newCityName, newStateName); 
   
   
   public String getCityName() 
   
      return this.cityState.getCityName(); 
   
   
   public String getStateName() 
   
      return this.cityState.getStateName(); 
   
   
   @Override 
   public String toString() 
   
      return this.cityState.toString(); 
   
   
   /**
    * Serialize this instance.
    
    * @param out Target to which this instance is written.
    * @throws IOException Thrown if exception occurs during serialization.
    */ 
   private void writeObject(final ObjectOutputStream out) throws IOException 
   
      out.writeUTF(this.cityState.getCityName()); 
      out.writeUTF(this.cityState.getStateName()); 
   
    
   /**
    * Deserialize this instance from input stream.
    
    * @param in Input Stream from which this instance is to be deserialized.
    * @throws IOException Thrown if error occurs in deserialization.
    * @throws ClassNotFoundException Thrown if expected class is not found.
    */ 
   private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException 
   
      this.cityState = new CityState(in.readUTF(), in.readUTF()); 
   
   
   private void readObjectNoData() throws ObjectStreamException 
   
      throw new InvalidObjectException("Stream data required"); 
   

這個可序列化的Decorator可以在Person類中直接使用。由于所有的屬性都支持序列化,Person類可以使用默認的序列化方法。下面這段代碼定義了一個從Person類改過來的Person2類。

Person2.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package dustin.examples.serialization; 
   
import java.io.Serializable; 
   
/**
 * Person class.
 
 * @author Dustin
 */ 
public class Person2 implements Serializable 
   private final String lastName; 
   private final String firstName; 
   private final SerializableCityState cityAndState; 
   
   public Person2( 
      final String newLastName, final String newFirstName, 
      final SerializableCityState newCityAndState) 
   
      this.lastName = newLastName; 
      this.firstName = newFirstName; 
      this.cityAndState = newCityAndState; 
   
   
   public String getFirstName() 
   
      return this.firstName; 
   
   
   public String getLastName() 
   
      return this.lastName; 
   
   
   @Override 
   public String toString() 
   
      return this.firstName + " " + this.lastName + " of " + this.cityAndState; 
   

下面這段代碼運行了這個類。之后是NetBeans輸出的截圖。

Running SerializationDemonstrator Against Person2/SerializableCityState

1
2
3
4
final Person2 personIn = new Person2("Flintstone", "Fred", new SerializableCityState("Bedrock", "Cobblestone")); 
SerializationDemonstrator.serialize(personIn, "person2.dat"); 
final Person2 personOut = SerializationDemonstrator.deserialize("person2.dat", Person2.class); 

通過使用定制的序列化方法,可以在不使用transient的情況下,對一個帶有不可序列化屬性的類進行序列化。當你要在一個需要序列化的類中使用不可序列化的類型,并且這些類型不能被修改時,這是一個有用的技術(shù)。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多