java.io.NotSerializableException 即使 class 实现了 Serializable

java.io.NotSerializableException even tho the class implements Serializable

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

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

研究表明我必须在我的歌曲中实现可序列化接口 class。我做了,但仍然有这个警告。 我的歌 class:

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;
    //...

The MusicDaten - Class

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");
    }

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

实施 Serializable 是不够的。

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

在您的例子中,Song 包含类型为 File 的属性,并且 File 不可序列化。使用 Clip,你有同样的问题。

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

查看the docs of Serializable,你可以发现:

Classes that require special handling during the serialization and deserialization process must implement special methods with these exact signatures:

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

The writeObject method is responsible for writing the state of the object for its particular class so that the corresponding readObject method can restore it. The default mechanism for saving the Object's fields can be invoked by calling out.defaultWriteObject. The method does not need to concern itself with the state belonging to its superclasses or subclasses. State is saved by writing the individual fields to the ObjectOutputStream using the writeObject method or by using the methods for primitive data types supported by DataOutput.

The readObject method is responsible for reading from the stream and restoring the classes fields. It may call in.defaultReadObject to invoke the default mechanism for restoring the object's non-static and non-transient fields.

这意味着您可以创建方法 writeObjectreadObject,您可以在其中指定如何(反)序列化对象。

如果想对支持序列化的属性保持默认(反)序列化,可以将所有不支持序列化的字段都标记为transient,然后调用out.defaultWriteObject/in.defaultReadObject writeObject/readObject 方法。

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


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

一方面,如果反序列化不受信任的数据,可能会导致严重的拒绝服务甚至远程代码执行漏洞。 the docs of Serializable:

中也注明了这一点

Warning: Deserialization of untrusted data is inherently dangerous and should be avoided. Untrusted data should be carefully validated according to the "Serialization and Deserialization" section of the Secure Coding Guidelines for Java SE. Serialization Filtering describes best practices for defensive use of serial filters.

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

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