Java InvalidClassException 类
最后修改时间:2025 年 4 月 16 日
当序列化或反序列化期间,序列化运行时检测到类出现问题时,会抛出 java.io.InvalidClassException
异常。 这些问题包括不兼容的类版本或缺失的 serialVersionUID。
当序列化类定义在序列化和反序列化之间发生更改时,通常会发生此异常。 JVM 使用 serialVersionUID 来验证兼容性。 如果没有显式声明,它会自动生成。
InvalidClassException 类概述
InvalidClassException
扩展自 ObjectStreamException
并指示序列化问题。 它通过其消息提供有关失败的详细信息。 通常包含类名和解释。
public class InvalidClassException extends ObjectStreamException { public String classname; public InvalidClassException(String reason); public InvalidClassException(String cname, String reason); public String getMessage(); }
上面的代码展示了 InvalidClassException
的结构。 classname 字段保存有问题的类名。 构造函数允许指定原因和类名。 getMessage 方法将两者组合在消息中。
基本序列化示例
此示例演示了基本序列化,其中没有发生异常。 我们将序列化和反序列化一个简单的 Person 对象。 该类实现了 Serializable 并声明了 serialVersionUID。
import java.io.*; class Person implements Serializable { private static final long serialVersionUID = 1L; String name; int age; public Person(String name, int age) { this.name = name; this.age = age; } } public class Main { public static void main(String[] args) { Person person = new Person("John Doe", 30); // Serialize try (ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("person.ser"))) { oos.writeObject(person); System.out.println("Serialization complete"); } catch (IOException e) { e.printStackTrace(); } // Deserialize try (ObjectInputStream ois = new ObjectInputStream( new FileInputStream("person.ser"))) { Person deserialized = (Person) ois.readObject(); System.out.println("Deserialized: " + deserialized.name); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } }
此示例显示了成功的序列化和反序列化。 Person 类已声明了 serialVersionUID。 在不对该类进行更改的情况下,反序列化可以正常工作。 接下来,示例将展示导致 InvalidClassException 的场景。
缺少 serialVersionUID
当缺少 serialVersionUID 时,JVM 会根据类结构生成一个。 更改类结构会使生成的 UID 不兼容。 这会导致反序列化期间出现 InvalidClassException。
import java.io.*; // Version 1: Original class without serialVersionUID class Product implements Serializable { String name; double price; public Product(String name, double price) { this.name = name; this.price = price; } } public class Main { public static void main(String[] args) { // Serialize original version Product product = new Product("Laptop", 999.99); try (ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("product.ser"))) { oos.writeObject(product); } catch (IOException e) { e.printStackTrace(); } // Simulate class change by adding a new field class Product implements Serializable { String name; double price; int quantity; // Added field public Product(String name, double price) { this.name = name; this.price = price; } } // Attempt deserialization try (ObjectInputStream ois = new ObjectInputStream( new FileInputStream("product.ser"))) { Product deserialized = (Product) ois.readObject(); System.out.println("Deserialized: " + deserialized.name); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); // Will throw InvalidClassException } } }
此示例演示了由于缺少 serialVersionUID 导致的 InvalidClassException。 在序列化原始 Product 之后,我们通过添加一个字段修改了该类。 反序列化失败,因为生成的 UID 不匹配。 始终为可序列化类声明 serialVersionUID。
不兼容的 serialVersionUID
显式的 serialVersionUID 值必须在序列化和反序列化之间匹配。 手动更改此值会导致 InvalidClassException。 此示例显示了 UID 不匹配的场景。
import java.io.*; class Employee implements Serializable { private static final long serialVersionUID = 1L; // Original UID String name; String department; public Employee(String name, String department) { this.name = name; this.department = department; } } public class Main { public static void main(String[] args) { // Serialize with original UID Employee emp = new Employee("Alice", "Engineering"); try (ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("employee.ser"))) { oos.writeObject(emp); } catch (IOException e) { e.printStackTrace(); } // Simulate UID change class Employee implements Serializable { private static final long serialVersionUID = 2L; // Changed UID String name; String department; public Employee(String name, String department) { this.name = name; this.department = department; } } // Attempt deserialization try (ObjectInputStream ois = new ObjectInputStream( new FileInputStream("employee.ser"))) { Employee deserialized = (Employee) ois.readObject(); System.out.println("Deserialized: " + deserialized.name); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); // InvalidClassException: incompatible UIDs } } }
此示例显示了由更改 serialVersionUID 引起的 InvalidClassException。 序列化数据具有 UID=1,但该类现在期望 UID=2。 异常消息将指示 UID 不匹配。 除非您有意进行不兼容的更改,否则切勿更改 serialVersionUID。
类结构更改
即使 serialVersionUID 匹配,某些类结构更改也是不兼容的。 更改字段类型或删除字段会导致 InvalidClassException。 此示例演示了这种不兼容的更改。
import java.io.*; class Account implements Serializable { private static final long serialVersionUID = 1L; String accountNumber; double balance; // Original type: double public Account(String accountNumber, double balance) { this.accountNumber = accountNumber; this.balance = balance; } } public class Main { public static void main(String[] args) { // Serialize original version Account acc = new Account("123456", 1000.0); try (ObjectOutputStream oos = new ObjectOutputStream( new FileInputStream("account.ser"))) { oos.writeObject(acc); } catch (IOException e) { e.printStackTrace(); } // Simulate incompatible change: change balance type to String class Account implements Serializable { private static final long serialVersionUID = 1L; // Same UID String accountNumber; String balance; // Changed type public Account(String accountNumber, String balance) { this.accountNumber = accountNumber; this.balance = balance; } } // Attempt deserialization try (ObjectInputStream ois = new ObjectInputStream( new FileInputStream("account.ser"))) { Account deserialized = (Account) ois.readObject(); System.out.println("Deserialized: " + deserialized.accountNumber); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); // InvalidClassException: incompatible types } } }
此示例显示了由于不兼容的字段类型更改导致的 InvalidClassException。 即使 serialVersionUID 匹配,将 balance 从 double 更改为 String 也会破坏反序列化。 序列化系统无法在这些类型之间自动转换。 这种更改需要自定义的 readObject/writeObject 方法。
缺少类定义
当反序列化期间类定义不可用时,会发生 InvalidClassException。 当该类在序列化期间可用但在反序列化期间丢失时,就会发生这种情况。 该示例模拟了此场景。
import java.io.*; // Original class that will be serialized class Customer implements Serializable { private static final long serialVersionUID = 1L; String name; String email; public Customer(String name, String email) { this.name = name; this.email = email; } } public class Main { public static void main(String[] args) { // Serialize Customer object Customer cust = new Customer("Bob", "bob@example.com"); try (ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("customer.ser"))) { oos.writeObject(cust); System.out.println("Serialization complete"); } catch (IOException e) { e.printStackTrace(); } // Simulate missing class by not having Customer class definition // during deserialization // Attempt deserialization without Customer class try (ObjectInputStream ois = new ObjectInputStream( new FileInputStream("customer.ser"))) { Object deserialized = ois.readObject(); System.out.println("Deserialized: " + deserialized.getClass()); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); // InvalidClassException: class not found } } }
此示例演示了由于缺少类定义导致的 InvalidClassException。 Customer 类在序列化期间可用,但在反序列化期间不可用。 异常消息将指示缺少的类名。 确保在反序列化期间类定义在类路径中可用。
自定义 serialPersistentFields
如果使用 serialPersistentFields 来控制序列化,如果不小心处理,可能会导致 InvalidClassException。 此示例显示了不正确的用法,其中声明的字段与实际的类字段不匹配。
import java.io.*; import java.io.ObjectStreamField; class Settings implements Serializable { private static final long serialVersionUID = 1L; // Incorrect serialPersistentFields - missing 'darkMode' field private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("language", String.class) }; String language; boolean darkMode; public Settings(String language, boolean darkMode) { this.language = language; this.darkMode = darkMode; } } public class Main { public static void main(String[] args) { // Serialize Settings settings = new Settings("en", true); try (ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("settings.ser"))) { oos.writeObject(settings); } catch (IOException e) { e.printStackTrace(); } // Attempt deserialization try (ObjectInputStream ois = new ObjectInputStream( new FileInputStream("settings.ser"))) { Settings deserialized = (Settings) ois.readObject(); System.out.println("Language: " + deserialized.language); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); // InvalidClassException: field mismatch } } }
此示例显示了由不正确的 serialPersistentFields 引起的 InvalidClassException。 该数组仅声明了 'language' 字段,而该类还具有 'darkMode' 字段。 序列化系统检测到此不匹配。 使用 serialPersistentFields 时,请确保所有可序列化字段都已正确声明。
来源
Java InvalidClassException 类文档
在本文中,我们介绍了在 Java 序列化期间导致 InvalidClassException 的常见场景。 了解这些情况有助于防止和解决 Java 应用程序中的序列化问题。
作者
列出所有Java教程。