ZetCode

Java NotSerializableException 类

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

当需要一个实例实现 Serializable 接口但却没有时,会抛出 java.io.NotSerializableException 异常。它是 Java 对象序列化机制的一部分。序列化将对象转换为字节流。

当尝试对不可序列化对象进行序列化时,通常会发生此异常。Java 序列化机制会检查 Serializable 接口。如果没有实现该接口,则会抛出此异常,以防止无效的序列化。

NotSerializableException 类概述

NotSerializableException 继承自 ObjectStreamException,表示序列化问题。异常消息会指明有问题的类。这是一个受检异常,需要在代码中显式处理。

public class NotSerializableException extends ObjectStreamException {
    public NotSerializableException(String classname);
    public NotSerializableException();
}

上面的代码展示了 NotSerializableException 类的结构。带参数的构造函数接受不可序列化类的名称。默认构造函数创建不包含特定类信息的异常。

基本序列化示例

此示例演示了在展示失败案例之前,如何进行正确的序列化。一个类必须实现 Serializable 接口才能被序列化。该示例展示了正确配置的类的成功序列化。

Main.java
import java.io.*;

class Person implements Serializable {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

public class Main {
    public static void main(String[] args) {
        Person person = new Person("John Doe", 30);
        
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("person.ser"))) {
            oos.writeObject(person);
            System.out.println("Person serialized successfully");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例展示了 Person 类的成功序列化。该类实现了 Serializable 接口,允许对象序列化。ObjectOutputStream 将对象写入文件,没有错误。

不可序列化类示例

此示例演示了尝试序列化未实现 Serializable 接口的类时,发生的 NotSerializableException 异常。异常清楚地指出了哪个类导致了问题。

Main.java
import java.io.*;

class Product {  // Doesn't implement Serializable
    private String name;
    private double price;
    
    public Product(String name, double price) {
        this.name = name;
        this.price = price;
    }
}

public class Main {
    public static void main(String[] args) {
        Product product = new Product("Laptop", 999.99);
        
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("product.ser"))) {
            oos.writeObject(product);  // This will throw NotSerializableException
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

运行此代码会抛出 NotSerializableException 异常,因为 Product 没有实现 Serializable 接口。异常堆栈跟踪将在其消息中包含类名 (Product)。这有助于在调试期间识别有问题的类。

不可序列化字段示例

即使一个类实现了 Serializable 接口,如果它包含不可序列化的字段,也可能会失败。此示例展示了这种情况,即一个可序列化的类包含一个不可序列化的字段。

Main.java
import java.io.*;

class Engine {}  // Not serializable

class Car implements Serializable {
    private String model;
    private Engine engine;  // Non-serializable field
    
    public Car(String model, Engine engine) {
        this.model = model;
        this.engine = engine;
    }
}

public class Main {
    public static void main(String[] args) {
        Car car = new Car("Sedan", new Engine());
        
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("car.ser"))) {
            oos.writeObject(car);  // Fails due to Engine field
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例抛出 NotSerializableException 异常,因为 Car 包含一个不可序列化的 Engine 字段。所有字段都必须是可序列化的或标记为 transient。异常消息将指定 Engine 为有问题的类。

瞬态字段解决方案

transient 关键字标记不应被序列化的字段。此示例通过将 engine 字段设为瞬态来修改前一个示例。序列化现在成功,跳过了 engine 字段。

Main.java
import java.io.*;

class Engine {}  // Still not serializable

class Car implements Serializable {
    private String model;
    private transient Engine engine;  // Marked as transient
    
    public Car(String model, Engine engine) {
        this.model = model;
        this.engine = engine;
    }
    
    public Engine getEngine() { return engine; }
}

public class Main {
    public static void main(String[] args) {
        Car car = new Car("Sedan", new Engine());
        
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("car.ser"))) {
            oos.writeObject(car);
            System.out.println("Car serialized successfully (engine skipped)");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例成功序列化了 Car 对象,因为 engine 字段被标记为 transient。反序列化后,engine 字段将为 null。当字段不需要持久化或可以重建时,此方法有效。

自定义序列化解决方案

为了获得更多控制权,类可以使用 writeObjectreadObject 方法实现自定义序列化。此示例展示了如何使用自定义序列化来处理不可序列化的字段。

Main.java
import java.io.*;

class Engine {
    private String type;
    
    public Engine(String type) { this.type = type; }
    public String getType() { return type; }
}

class Car implements Serializable {
    private String model;
    private transient Engine engine;
    
    public Car(String model, Engine engine) {
        this.model = model;
        this.engine = engine;
    }
    
    private void writeObject(ObjectOutputStream oos) throws IOException {
        oos.defaultWriteObject();
        oos.writeObject(engine.getType());  // Serialize engine data
    }
    
    private void readObject(ObjectInputStream ois) 
            throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        String engineType = (String) ois.readObject();
        this.engine = new Engine(engineType);  // Reconstruct engine
    }
    
    public Engine getEngine() { return engine; }
}

public class Main {
    public static void main(String[] args) {
        Car car = new Car("SUV", new Engine("V6"));
        
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("car.ser"))) {
            oos.writeObject(car);
            System.out.println("Car with custom serialization saved");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例演示了对具有不可序列化字段的类进行自定义序列化。writeObject 方法保存了 engine 的类型字符串。readObject 方法在反序列化期间重建了 engine。此方法提供了对序列化的完全控制。

继承和序列化

类层次结构中的序列化需要注意。如果超类不可序列化,则其字段将不会被序列化。此示例展示了扩展不可序列化类的行为。

Main.java
import java.io.*;

class Vehicle {  // Not serializable
    protected String type;
    
    public Vehicle(String type) { this.type = type; }
}

class Truck extends Vehicle implements Serializable {
    private int capacity;
    
    public Truck(String type, int capacity) {
        super(type);
        this.capacity = capacity;
    }
    
    @Override
    public String toString() {
        return "Truck{type='" + type + "', capacity=" + capacity + "}";
    }
}

public class Main {
    public static void main(String[] args) {
        Truck truck = new Truck("Heavy", 5000);
        
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("truck.ser"))) {
            oos.writeObject(truck);
            System.out.println("Truck serialized (but not Vehicle fields)");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例展示了使用继承的序列化。Truck 类是可序列化的,但其 Vehicle 超类不是。type 字段将不会被序列化,并且在反序列化后将为 null。要包含超类字段,超类也必须实现 Serializable 接口。

来源

Java NotSerializableException 类文档

本教程涵盖了 Java 序列化中的 NotSerializableException 异常。我们探讨了处理序列化问题的根本原因、解决方案和最佳实践。理解这些概念对于在 Java 中有效实现对象持久化至关重要。

作者

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

列出所有Java教程