首页 > 解决方案 > java.io.NotSerializableException 即使类实现了 Serializable

问题描述

我正在构建一个 music.player 并将我的音乐库存储在一个 HashMap 中。用户应能够添加和删除歌曲。我想在程序重新启动时保存这个 HashMap。但是我是否遇到过这个警告:

Exception in thread "main" java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: musicplayer.Song

研究表明我必须在我的 Song 类中实现 Serializable 接口。我做了,但仍然有这个警告。我的歌曲课:

package musicplayer;
//Song-Klasse, speichert alle Attribute und Methoden eines Songs. Funktioniert soweit
import java.io.File;
import java.io.IOException;
import java.io.Serializable;

public class Song implements Serializable {
    private static final long serialVersionUID = 4390482518182625971L;
    //Attribute
    File file;
    Clip clip;
    String string;
    //...

MusicDatan - 类

package musicplayer;


public class MusicDaten implements Serializable {
    
    private static Map<String,Song> all; //= new HashMap<String,Song>();
    private File file = new File("C://Users//ThinkPad T450s//git//testproject//musicplayer//SongInfo.ser");
    
// ...

    public MusicDaten() throws ClassNotFoundException, IOException {
        this.setSavedSongs();
    }
    
    
    public void setSavedSongs() throws IOException, ClassNotFoundException  { //initialisziert HashMap mit den gespeicherten Songs
        FileInputStream fileIn = new FileInputStream(file);
        ObjectInputStream in = new ObjectInputStream(fileIn);
        all = (HashMap<String,Song>) in.readObject();
        in.close();
        fileIn.close();
    }
    public void save() throws IOException {   //Speicher HashMap
        FileOutputStream fileOut = new FileOutputStream(file);
        ObjectOutputStream out = new ObjectOutputStream(fileOut);
        out.writeObject(all);
        out.close();
        fileOut.close();
        System.out.println("Songinfo saved");
    }

感谢您的帮助。(我已经编辑了这个问题,因为它不是很清楚)

标签: javaeclipseserializationdeserializationjava-13

解决方案


实施Serializable是不够的。

如果您尝试序列化一个对象,它的所有非瞬态属性也会被序列化。如果这些属性中的任何一个不是Serializable,它将不起作用。

在您的情况下,Song包含类型的属性File并且File不可序列化。Clip,你有同样的问题。

为了解决这个问题,您可以进行自定义序列化。

查看的文档Serializable,您可以找到以下内容:

在序列化和反序列化过程中需要特殊处理的类必须实现具有这些确切签名的特殊方法:

 private void writeObject(java.io.ObjectOutputStream out)
     throws IOException
 private void readObject(java.io.ObjectInputStream in)
     throws IOException, ClassNotFoundException;
 private void readObjectNoData()
       throws ObjectStreamException;

writeObject 方法负责为其特定类写入对象的状态,以便相应的 readObject 方法可以恢复它。可以通过调用 out.defaultWriteObject 来调用保存 Object 字段的默认机制。该方法不需要关注属于其超类或子类的状态。通过使用 writeObject 方法或使用 DataOutput 支持的原始数据类型的方法将各个字段写入 ObjectOutputStream 来保存状态。

readObject 方法负责从流中读取并恢复类字段。它可以调用 in.defaultReadObject 来调用用于恢复对象的非静态和非瞬态字段的默认机制。

这意味着您可以创建方法writeObject以及readObject指定如何(反)序列化对象的位置。

如果要保留支持序列化的属性的默认(反)序列化,可以标记所有不支持序列化的字段并在/方法中transient调用out.defaultWriteObject/ 。in.defaultReadObjectwriteObjectreadObject

标记一个属性transient意味着序列化会忽略它。然后,您可以使用您的自定义逻辑。


请注意,序列化会带来一些问题,您可能不想使用它。

一方面,如果反序列化不受信任的数据,可能会导致严重的拒绝服务甚至远程代码执行漏洞。这在以下文档中Serializable也有说明:

警告:不可信数据的反序列化本质上是危险的,应该避免。应根据Java SE 安全编码指南的“序列化和反序列化”部分仔细验证不受信任的数据。序列化过滤描述了防御性使用串行过滤器的最佳实践。

序列化的另一个问题是,它将您的应用程序绑定到固定格式,如果您在最初创建应用程序时没有仔细考虑,则在更新应用程序时很难与旧的序列化数据兼容。

有关这方面的更多信息,您可能需要考虑阅读本书Effective Java


推荐阅读