java - 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");
}
感谢您的帮助。(我已经编辑了这个问题,因为它不是很清楚)
解决方案
实施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.defaultReadObject
writeObject
readObject
标记一个属性transient
意味着序列化会忽略它。然后,您可以使用您的自定义逻辑。
请注意,序列化会带来一些问题,您可能不想使用它。
一方面,如果反序列化不受信任的数据,可能会导致严重的拒绝服务甚至远程代码执行漏洞。这在以下文档中Serializable
也有说明:
警告:不可信数据的反序列化本质上是危险的,应该避免。应根据Java SE 安全编码指南的“序列化和反序列化”部分仔细验证不受信任的数据。序列化过滤描述了防御性使用串行过滤器的最佳实践。
序列化的另一个问题是,它将您的应用程序绑定到固定格式,如果您在最初创建应用程序时没有仔细考虑,则在更新应用程序时很难与旧的序列化数据兼容。
有关这方面的更多信息,您可能需要考虑阅读本书Effective Java
。
推荐阅读
- javascript - 如何根据属性键对值进行唯一和求和
- python - 如何在python中自动命名pdf文件
- c++ - 使用 std::move 函数签名传递向量
- java - 在 RowMapper 中使用 BeanMapper?
- javascript - 将 jQuery 插件导入 React 组件?
- debugging - node_modules 中的断点
- eclipse - 将新的 Eclipse 项目与 GitHub 存储库同步
- php - 如何在 PHP 中处理 ajax 请求中的 GET 变量
- xcode - 为什么 Firebase 远程推送通知在 ios 中不起作用,但在 android 中起作用
- swift - 有没有办法在 RealityKit 中将视图呈现为实体?