NotSerializableException

2024年5月22日15:06:52

NotSerializableException异常

Exception in thread "main" java.io.NotSerializableException: com.lmcode.PrototypeMode.deepCloneCase2.Citation at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348) at com.lmcode.PrototypeMode.deepCloneCase2.main.main(main.java:15)

报错 java.io.NotSerializableException 表示 Citation 类没有实现 Serializable 接口。Java 的序列化机制要求所有被序列化的对象类都必须实现 Serializable 接口。

要解决这个问题,需要让 Citation 类和它包含的任何其他类(如 Student 类)实现 Serializable 接口。以下是修改后的代码示例:

public class Citation implements Serializable {
    private static final long serialVersionUID = 1L; // 可选,但推荐
}
public class Student implements Serializable {
    private static final long serialVersionUID = 1L; // 可选,但推荐
}

private static final long serialVersionUID = 1L; 是一个类变量,用于表示类的序列化版本号。它在 Java 的序列化机制中扮演了一个重要角色。具体来说,serialVersionUID 的作用如下:

  1. 版本控制serialVersionUID 用于表明类的兼容性版本。序列化时,Java 会将 serialVersionUID 写入序列化的对象流中。反序列化时,Java 会检查对象流中的 serialVersionUID 是否与当前类的 serialVersionUID 匹配。如果匹配,则反序列化成功;否则会抛出 InvalidClassException 异常,表明类的版本不兼容。

  2. 显式声明 vs 隐式生成:如果一个类没有显式声明 serialVersionUID,Java 会根据类的各种信息(包括类名、实现的接口、成员变量等)自动生成一个 serialVersionUID。这种自动生成的值可能会随着类的细微变化而变化,因此显式声明 serialVersionUID 可以确保类的兼容性,即使类的某些细节发生变化,也不会影响序列化机制。

  3. 常见的做法:通常,我们会显式声明 serialVersionUID,并赋予一个固定值,例如 1L,以确保在类结构不变的情况下,序列化版本号保持不变。

以下是一个简单的示例:

import java.io.Serializable;

public class MyClass implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name;
    private int age;

    // 省略构造函数、getter 和 setter
}

在这个示例中,MyClass 类实现了 Serializable 接口,并显式声明了 serialVersionUID1L。这样,当你对 MyClass 对象进行序列化和反序列化时,只要 serialVersionUID 一致,即使类有小的变动(如添加新的字段),反序列化过程也不会失败。

总结:

serialVersionUID 是用于版本控制的标识符,它确保了序列化对象在不同版本的类之间的兼容性。显式声明 serialVersionUID 是一种推荐的做法,能避免因自动生成的 serialVersionUID 导致的反序列化失败。

在什么情况下需要实现序列化接口:

实现序列化接口 (Serializable) 通常在以下几种情况下是必要的:

  1. 持久化对象:需要将对象的状态保存到文件、数据库或其他持久化存储中,以便以后可以恢复。例如,将用户会话信息保存到文件中,当用户再次登录时可以恢复会话状态。

  2. 远程方法调用 (RMI):在分布式系统中,Java RMI(远程方法调用)允许对象在不同的JVM(Java虚拟机)之间传输。在这种情况下,参与传输的对象需要实现 Serializable 接口。

  3. 缓存:在分布式缓存系统(如 Redis、Ehcache)中,需要将对象序列化后存储到缓存中,以便其他应用或进程能够访问这些对象。

  4. 通过网络传输对象:当需要通过网络将对象从一个节点传输到另一个节点时,比如在Web服务中,通过HTTP传输对象数据。

  5. 深克隆对象:序列化和反序列化是一种创建对象深克隆的常用方法。通过将对象序列化成字节流,然后再反序列化成一个新的对象,可以实现对象的深克隆。

具体示例

持久化对象到文件

import java.io.*;

public class SerializableExample {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        MyObject obj = new MyObject("example", 42);
        
        // 序列化对象到文件
        FileOutputStream fos = new FileOutputStream("object.dat");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(obj);
        oos.close();
        
        // 从文件反序列化对象
        FileInputStream fis = new FileInputStream("object.dat");
        ObjectInputStream ois = new ObjectInputStream(fis);
        MyObject deserializedObj = (MyObject) ois.readObject();
        ois.close();
        
        System.out.println(deserializedObj);
    }
}

class MyObject implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int value;
    
    public MyObject(String name, int value) {
        this.name = name;
        this.value = value;
    }
    
    @Override
    public String toString() {
        return "MyObject{name='" + name + "', value=" + value + "}";
    }
}

什么时候不需要实现序列化接口

  • 临时对象:如果对象是短期存在的,并且不需要在内存之外存储或传输,那么不需要实现 Serializable 接口。
  • 安全考虑:在某些情况下,出于安全考虑,你可能不希望对象被序列化和反序列化。例如,包含敏感信息的对象可能需要防止被意外或恶意序列化。

总结

实现 Serializable 接口主要在需要持久化、传输、缓存或深克隆对象时使用。它允许对象以平台无关的方式进行序列化和反序列化,从而能够在不同的环境中恢复对象的状态。然而,对于不需要持久化或传输的临时对象,以及出于安全考虑的对象,不必实现 Serializable 接口。

posted @ 2024-05-22 15:21  燕子去了  阅读(15)  评论(0编辑  收藏  举报

Powered by .NET 8.0 on Kubernetes

我会翻山越岭,到每一个我想去的地方

...