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教程。