问题描述
我正在构建一个 music.player 并将我的音乐库存储在 HashMap 中。用户应能够添加和删除歌曲。我想在程序重新启动时保存这个 HashMap。 但是我是否遇到过这个警告:
Exception in thread "main" java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: musicplayer.song
研究表明我必须在我的 Song 类中实现可序列化接口。我做了,但仍然有这个警告。 我的歌曲课:
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;
//...
MusicDaten - 课堂
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
,您可以找到:
在序列化和反序列化过程中需要特殊处理的类必须实现具有以下确切签名的特殊方法:
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 来调用。该方法不需要关心属于它的超类或子类的状态。通过使用 writeObject 方法或使用 DataOutput 支持的原始数据类型的方法将各个字段写入 ObjectOutputStream 来保存状态。
readObject 方法负责从流中读取并恢复类字段。它可以调用 in.defaultReadObject 来调用默认机制来恢复对象的非静态和非瞬态字段。
这意味着您可以创建方法 writeObject
和 readObject
,您可以在其中指定如何(反)序列化对象。
如果要保留支持序列化的属性的默认(反)序列化,可以将所有不支持序列化的字段标记为transient
,并在{{ 1}}/out.defaultWriteObject
方法。
标记属性 in.defaultReadObject
意味着序列化忽略它。然后您可以使用您的自定义逻辑。
请注意,序列化会带来一些问题,您可能不想使用它。
一方面,如果您对不受信任的数据进行反序列化,可能会导致严重的拒绝服务甚至远程代码执行漏洞。这也在 the docs of Serializable
中注明:
警告:不可信数据的反序列化本质上是危险的,应该避免。应根据 Secure Coding Guidelines for Java SE 的“序列化和反序列化”部分仔细验证不受信任的数据。 Serialization Filtering 描述了防御性使用串行过滤器的最佳做法。
序列化的另一个问题是,它会将您的应用程序绑定到固定格式,并且如果您在最初创建应用程序时没有仔细考虑,则在更新应用程序时很难与旧的序列化数据兼容。
有关这方面的更多信息,您可能需要考虑阅读 writeObject
一书。