Статьи

ObjectStreamClass: просмотр сериализации объекта Java

ObjectStreamClass может быть полезным классом для анализа характеристик сериализации сериализованного класса, загруженного в JVM. В этом посте рассматривается некоторая информация, которую этот класс предоставляет о загруженном сериализованном классе.

ObjectStreamClass предоставляет два статических метода для поиска класса: lookup (class) и lookupAny (Class) . Первый, lookup(Class) , будет возвращать экземпляр ObjectStreamClass тогда, когда предоставленный класс сериализуем и возвращает нуль, если предоставленный класс не сериализуем. Во-вторых, lookupAny(Class) возвращает экземпляр ObjectStreamClass для предоставленного класса независимо от того, является ли он сериализуемым или нет.

Как только экземпляр ObjectStreamClass предоставляется с помощью статических методов «поиска», этот экземпляр можно запросить для имени класса , для UID серийной версии и для сериализуемых полей .

Чтобы продемонстрировать использование ObjectStreamClass , я сначала перечислю листинги кода для двух простых классов, которые будут частью демонстрации. Один класс, Person , является Сериализуемым , но имеет временное поле. Другой класс, UnserializablePerson , почти идентичен, но он не Serializable.

Person.java

01
02
03
04
05
06
07
08
09
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
package dustin.examples.serialization;
 
import java.io.Serializable;
 
/**
 * Person class intended for demonstration of ObjectStreamClass.
 *
 * @author Dustin
 */
public class Person implements Serializable
{
   private final String lastName;
   private final String firstName;
   transient private final String fullName;
 
   public Person(final String newLastName, final String newFirstName)
   {
      this.lastName = newLastName;
      this.firstName = newFirstName;
      this.fullName = this.firstName + " " + this.lastName;
   }
 
   public String getFirstName()
   {
      return this.firstName;
   }
 
   public String getLastName()
   {
      return this.lastName;
   }
 
   public String getFullName()
   {
      return this.fullName;
   }
 
   @Override
   public String toString()
   {
      return this.fullName;
   }
}

UnserializablePerson.java

01
02
03
04
05
06
07
08
09
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
package dustin.examples.serialization;
 
/**
 * Person class intended for demonstration of ObjectStreamClass.
 *
 * @author Dustin
 */
public class UnserializablePerson
{
   private final String lastName;
   private final String firstName;
   private final String fullName;
 
   public UnserializablePerson(final String newLastName, final String newFirstName)
   {
      this.lastName = newLastName;
      this.firstName = newFirstName;
      this.fullName = this.firstName + " " + this.lastName;
   }
 
   public String getFirstName()
   {
      return this.firstName;
   }
 
   public String getLastName()
   {
      return this.lastName;
   }
 
   public String getFullName()
   {
      return this.fullName;
   }
 
   @Override
   public String toString()
   {
      return this.fullName;
   }
}

Имея два класса для запуска использования вместе с ObjectStreamClass , пришло время взглянуть на простое демонстрационное приложение, которое демонстрирует использование ObjectStreamClass .

ObjectStreamClassDemo.java

01
02
03
04
05
06
07
08
09
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
package dustin.examples.serialization;
 
import static java.lang.System.out;
 
import java.io.ObjectStreamClass;
import java.io.ObjectStreamField;
 
/**
 * Demonstrates use of ObjectStreamDemo.
 *
 * @author Dustin
 */
public class ObjectStreamClassDemo
{
   /**
    * Displays class name, serial version UID, and serializable fields as
    * indicated by the provided instance of ObjectStreamClass.
    *
    * @param serializedClass
    */
   public static void displaySerializedClassInformation(
      final ObjectStreamClass serializedClass)
   {
      final String serializedClassName = serializedClass.getName();
      out.println("Class Name: " + serializedClassName);
      final long serializedVersionUid = serializedClass.getSerialVersionUID();
      out.println("serialversionuid: " + serializedVersionUid);
      final ObjectStreamField[] fields = serializedClass.getFields();
      out.println("Serialized Fields:");
      for (final ObjectStreamField field : fields)
      {
         out.println("\t" + field.getTypeString() + " " + field.getName());
      }
   }
 
   /**
    * Main function that demonstrates use of ObjectStreamDemo.
    *
    * @param arguments Command line arguments; none expected.
    */
   public static void main(String[] arguments)
   {
      // Example 1: ObjectStreamClass.lookup(Class) on a Serializable class
      out.println("\n=== ObjectStreamClass.lookup(Serializable) ===");
      final ObjectStreamClass serializedClass = ObjectStreamClass.lookup(Person.class);
      displaySerializedClassInformation(serializedClass);
 
      // Example 2: ObjectStreamClass.lookup(Class) on a class that is not
      //            Serializable (which will result in a NullPointerException
      //            when trying to access null returned from 'lookup'
      out.println("\n=== ObjectStreamClass.lookup(Unserializable) ===");
      try
      {
         final ObjectStreamClass unserializedClass =
            ObjectStreamClass.lookup(UnserializablePerson.class);
         displaySerializedClassInformation(unserializedClass);
      }
      catch (NullPointerException npe)
      {
         out.println("NullPointerException: Unable to lookup unserializable class with ObjectStreamClass.lookup.");
      }
 
      // Example 3: ObjectStreamClass.lookupAny(Class) works without the
      //            NullPointerException, but only provides name of the class as
      //            Serial Version UID and serialized fields do not apply in the
      //            case of a class that is not serializable.
      out.println("\n=== ObjectStreamClass.lookupAny(Unserializable) ===");
      final ObjectStreamClass unserializedClass =
          ObjectStreamClass.lookupAny(UnserializablePerson.class);
      displaySerializedClassInformation(unserializedClass);
   }
}

Комментарии в исходном коде выше указывают на то, что демонстрируется. Результат выполнения этого класса показан на следующем снимке экрана.

outputObjectStreamClassDemo

Когда вывод, показанный выше, соотносится с кодом перед ним, мы можем сделать несколько наблюдений, связанных с ObjectStreamClass . К ним относится тот факт, что transient поле сериализуемого класса не возвращается как одно из сериализуемых полей. Мы также видим, что ObjectStreamClass.lookup(Class) возвращает значение null, если предоставленный ему класс не сериализуем. ObjectStreamClass.lookupAny(Class) возвращает экземпляр ObjectStreamClass для классов, которые не сериализуются, но в этом случае доступно только имя класса.

Приведенный выше код показывает UID последовательной версии для Person.java 1940442894442614965. Когда serialver запускается в командной строке , генерируется и отображается тот же UID последовательной версии.

serialverOnPersonPowerShell

Приятно, что возможность программно рассчитывать тот же UID последовательной версии, который будет рассчитан с serialver инструмента serialver , поставляемого с Oracle JDK, заключается в том, что в сгенерированный код можно явно добавить тот же UID последовательной версии, который в любом случае будет добавлен неявно. Любой дружественный JVM сценарий или инструмент (например, написанный на Groovy), которому необходимо знать неявный UID ObjectStreamClass версии класса, может использовать ObjectStreamClass для получения этого UID ObjectStreamClass версии.