ZetCode

Java ObjectStreamClass 类

最后修改时间:2025 年 4 月 16 日

java.io.ObjectStreamClass 类提供关于类的序列化元数据。它包含可序列化类的名称和 serialVersionUID。此类由 Java 的序列化机制在内部使用。

ObjectStreamClass 描述序列化或反序列化的类。它充当序列化描述符,保存类名、字段和版本信息。该类主要由 ObjectInputStream 和 ObjectOutputStream 使用。

ObjectStreamClass 类概述

ObjectStreamClass 提供关于可序列化类的信息。主要方法包括获取类名、serialVersionUID 和字段描述符。应用程序不能直接实例化该类。

public class ObjectStreamClass implements Serializable {
    public static ObjectStreamClass lookup(Class cl);
    public static ObjectStreamClass lookupAny(Class cl);
    public String getName();
    public long getSerialVersionUID();
    public Class forClass();
    public ObjectStreamField[] getFields();
    public ObjectStreamField getField(String name);
    public String toString();
}

上面的代码显示了 ObjectStreamClass 提供的关键方法。这些方法允许检查序列化元数据。lookup 方法用于获取特定类的 ObjectStreamClass 实例。

获取类的 ObjectStreamClass

lookup 方法是获取 ObjectStreamClass 实例的主要方式。它返回可序列化类的描述符,对于不可序列化类返回 null。lookupAny 方法的工作方式类似,但不检查可序列化性。

Main.java
import java.io.ObjectStreamClass;
import java.io.Serializable;

class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;
}

public class Main {
    public static void main(String[] args) {
        // Get ObjectStreamClass for serializable class
        ObjectStreamClass osc = ObjectStreamClass.lookup(Person.class);
        System.out.println("Class name: " + osc.getName());
        System.out.println("SerialVersionUID: " + osc.getSerialVersionUID());
        
        // Try with non-serializable class
        ObjectStreamClass osc2 = ObjectStreamClass.lookup(String.class);
        System.out.println("String class serializable? " + (osc2 != null));
    }
}

此示例演示如何获取可序列化类的 ObjectStreamClass。Person 类实现了 Serializable 并具有 serialVersionUID。 lookup 方法为 String 类返回 null,因为 String 类未实现 Serializable。

检查序列化元数据

ObjectStreamClass 提供检查序列化元数据的方法。 getFields 方法返回所有可序列化字段。 getField 方法按名称检索特定字段。

Main.java
import java.io.ObjectStreamClass;
import java.io.Serializable;

class Employee implements Serializable {
    private static final long serialVersionUID = 2L;
    private String id;
    private transient String password;
    public String department;
}

public class Main {
    public static void main(String[] args) {
        ObjectStreamClass osc = ObjectStreamClass.lookup(Employee.class);
        
        System.out.println("Class: " + osc.getName());
        System.out.println("SUID: " + osc.getSerialVersionUID());
        
        // Get all serializable fields
        ObjectStreamField[] fields = osc.getFields();
        System.out.println("\nSerializable fields:");
        for (ObjectStreamField field : fields) {
            System.out.println(field.getName() + " - " + field.getType());
        }
        
        // Get specific field
        ObjectStreamField deptField = osc.getField("department");
        System.out.println("\nDepartment field type: " + deptField.getType());
    }
}

此示例显示了如何检查序列化元数据。 Employee 类有三个字段,但只有两个是可序列化的(瞬态字段被排除)。 getFields 方法仅返回可序列化字段,包括公共字段。

比较 SerialVersionUID 值

serialVersionUID 对于序列化中的版本兼容性至关重要。 ObjectStreamClass 可以帮助验证运行时类是否与序列化版本匹配。不匹配的 UID 会在反序列化期间导致 InvalidClassException。

Main.java
import java.io.ObjectStreamClass;
import java.io.Serializable;

class Product implements Serializable {
    private static final long serialVersionUID = 12345L;
    private String name;
    private double price;
}

class ModifiedProduct implements Serializable {
    private static final long serialVersionUID = 67890L;
    private String name;
    private double price;
    private String category; // Added field
}

public class Main {
    public static void main(String[] args) {
        ObjectStreamClass original = ObjectStreamClass.lookup(Product.class);
        ObjectStreamClass modified = ObjectStreamClass.lookup(ModifiedProduct.class);
        
        System.out.println("Original SUID: " + original.getSerialVersionUID());
        System.out.println("Modified SUID: " + modified.getSerialVersionUID());
        
        if (original.getSerialVersionUID() == modified.getSerialVersionUID()) {
            System.out.println("Classes are compatible");
        } else {
            System.out.println("Classes are incompatible - UIDs differ");
        }
    }
}

此示例演示了检查 serialVersionUID 兼容性。由于结构更改,Product 和 ModifiedProduct 类具有不同的 UID。在反序列化期间,这种不匹配将导致 InvalidClassException。

使用数组

ObjectStreamClass 还可以描述数组类。类名遵循 JVM 数组类型命名约定。 无论其组件类型如何,数组始终是可序列化的。

Main.java
import java.io.ObjectStreamClass;

public class Main {
    public static void main(String[] args) {
        // Get ObjectStreamClass for various array types
        ObjectStreamClass intArray = ObjectStreamClass.lookup(int[].class);
        ObjectStreamClass stringArray = ObjectStreamClass.lookup(String[].class);
        ObjectStreamClass multiArray = ObjectStreamClass.lookup(int[][].class);
        
        System.out.println("int[] class: " + intArray.getName());
        System.out.println("String[] class: " + stringArray.getName());
        System.out.println("int[][] class: " + multiArray.getName());
        
        // Arrays are always serializable
        System.out.println("\nIs int[] serializable? " + (intArray != null));
        System.out.println("Is String[] serializable? " + (stringArray != null));
    }
}

此示例显示了 ObjectStreamClass 如何与数组一起使用。 所有数组类型都是可序列化的,即使它们的组件类型不是。 类名使用 JVM 表示法(例如,int [] 的“[I”,int [][] 的“[[I”,String [] 的“[Ljava.lang.String;”)。

对不可序列化类使用 lookupAny

lookupAny 方法为任何类(包括不可序列化的类)返回 ObjectStreamClass。 这对于检查类结构(无论是否可序列化)都非常有用。

Main.java
import java.io.ObjectStreamClass;
import java.util.Date;

class NonSerializable {
    private int value;
    public String info;
}

public class Main {
    public static void main(String[] args) {
        // Using lookup (returns null for non-serializable)
        ObjectStreamClass osc1 = ObjectStreamClass.lookup(NonSerializable.class);
        System.out.println("lookup result: " + osc1);
        
        // Using lookupAny (works for any class)
        ObjectStreamClass osc2 = ObjectStreamClass.lookupAny(NonSerializable.class);
        System.out.println("\nClass from lookupAny: " + osc2.getName());
        System.out.println("SUID: " + osc2.getSerialVersionUID());
        
        // Works with serializable classes too
        ObjectStreamClass osc3 = ObjectStreamClass.lookupAny(Date.class);
        System.out.println("\nDate class: " + osc3.getName());
    }
}

此示例演示了 lookup 和 lookupAny 之间的区别。 NonSerializable 类不可序列化,因此 lookup 返回 null。 lookupAny 为任何类返回 ObjectStreamClass,允许检查其字段和计算的 serialVersionUID。

检查字段描述符

ObjectStreamField 对象提供有关可序列化字段的详细信息。 它们包括字段名称、类型以及该字段是否为原始类型。 此元数据对于分析序列化数据结构很有用。

Main.java
import java.io.ObjectStreamClass;
import java.io.ObjectStreamField;
import java.io.Serializable;

class InventoryItem implements Serializable {
    private static final long serialVersionUID = 1L;
    private String sku;
    private int quantity;
    private double price;
    private transient String location;
}

public class Main {
    public static void main(String[] args) {
        ObjectStreamClass osc = ObjectStreamClass.lookup(InventoryItem.class);
        ObjectStreamField[] fields = osc.getFields();
        
        System.out.println("Serializable fields in InventoryItem:");
        for (ObjectStreamField field : fields) {
            System.out.println("\nField: " + field.getName());
            System.out.println("Type: " + field.getType());
            System.out.println("Type code: " + field.getTypeCode());
            System.out.println("Is primitive: " + field.isPrimitive());
            System.out.println("Is unshared: " + field.isUnshared());
        }
    }
}

此示例检查 InventoryItem 类的字段描述符。 瞬态的 location 字段被排除在序列化之外。 每个 ObjectStreamField 提供有关可序列化字段的类型信息和其他元数据。

来源

Java ObjectStreamClass 类文档

在本文中,我们介绍了 Java ObjectStreamClass 类的基本方法和功能。 了解这些概念对于使用 Java 的序列化机制和序列化对象的版本控制至关重要。

作者

我叫 Jan Bodnar,是一位经验丰富的专业程序员。 我于 2007 年开始撰写编程文章,至今已撰写了 1400 多篇文章和 8 本电子书。 凭借超过八年的教学经验,我致力于分享我的知识并帮助他人掌握编程概念。

列出所有Java教程