Techincally我知道为什么类需要实现serializable。原因是ObjectOutputStream的writeObject方法在写入对象状态之前在内部检查“serializable的实例”。
但我的问题是需要什么呢? writeObject方法可以简单地写对象状态是否对象(其状态需要写入)是否实现可序列化?
根据wiki,类实现此接口以指示其非瞬态数据成员可以写入ObjectOutputStream。但问题是为什么类需要实现可序列化以确定字段是否是瞬态的。即使是不实现可序列化的类也应该被序列化(标记为瞬态的字段除外)。
将一个对象标记为可序列化(带有一个接口)并没有神奇地使该对象可序列化,它一直是可序列化的,只是现在你表达了系统可以自己找到的东西,所以我看到序列化没有真正的正当理由。
为什么类需要实现序列化标记接口来序列化类?
答案 0 :(得分:5)
通过提供标记界面,人们可以选择是否创建课程Serializable
。有时,你可能不想要这个! (当然,将此设为默认值并不好:请参阅此问题的答案:Why Java needs Serializable interface?)
答案 1 :(得分:3)
通过封装,我们假装没有透露关于对象的内部表示,并且我们仅通过其公共接口与我们的组件交互;当我们想要更改组件中数据的内部表示而不破坏其用户的任何代码时,我们通常会利用的理想属性。
相反,序列化意味着通过将对象的状态转换为可以在以后存储和复活的其他格式来暴露对象的内部状态。这意味着,一旦序列化,就不能改变对象的内部结构而不会冒这个复活过程成功的风险。
序列化的问题不仅出现在开放系统的情况下,而且出现在以某种方式依赖它的分布式系统中。例如,如果我们停止我们的应用程序服务器,它可能会选择序列化当前会话中的对象以便在服务器重新启动时重新生成它们,但是如果我们使用新版本的可序列化对象重新部署我们的应用程序,它们是否仍然是服务器试图复活它们时兼容吗?在分布式系统中,使用code mobility,是常见的,即,类集合位于中央存储库中,可供客户端和服务器共享公共代码。在这种方法中,由于对象被序列化以在客户端和服务器之间共享,如果我们更新此公共存储库中的可序列化类,我们是否有可能破坏任何内容?
例如,考虑我们有一个类Person,如下所示:
public class Person {
private String firstName;
private String lastName;
private boolean isMale;
private int age;
public boolean isMale() {
return this.isMale;
}
public int getAge() {
return this.age;
}
//more getters and setters
}
假设我们使用Person的抽象发布了我们的第一个API版本。但是对于第二个版本,我们想介绍两个变化:首先,我们发现如果我们可以存储一个人的出生日期而不是整数的年龄会更好,然后我们定义当Java没有枚举但是现在我们想用它们代表一个人的性别时,可能发生了类。
显然,由于字段已正确封装,我们可以在不影响公共接口的情况下更改类的内部工作方式。有点像这样:
public class Person {
private String firstName;
private String lastName;
private Gender gender;
private Date dateOfBirth;
public boolean isMale() {
return this.gender == Gender.MALE;
}
public int getAge() {
Calendar today = Calendar.getInstance();
Calendar birth = Calendar.getInstance();
birth.setTime(this.dateOfBirth);
return today.get(Calendar.YEAR) - birth.get(Calendar.YEAR);
}
//the rest of getters and setters
}
通过如上所示进行这些更改,我们可以确保预先存在的客户端不会中断,因为即使我们更改了对象状态的内部表示,我们也保持公共接口不变。
但是,考虑到默认情况下Person类是可序列化的,如果我们的系统是一个开放系统,那么可能有数千行代码依赖于它们能够基于它复活序列化对象的事实。原始类,或者甚至是基于类的原始版本作为其父类序列化扩展类的客户端。其中一些对象可能已被我们的API用户序列化为二进制形式或其他格式,现在,他们希望演变为我们的第二版代码。
然后,如果我们想要像我们在第二个例子中那样做一些改变,我们会立即打破其中一些;所有具有序列化对象的人都是基于类的原始版本,这些对象包含一个包含一个名为age of int的字段的对象,包含一个人的年龄,以及一个名为isMale的字段,其类型为boolean,包含有关性别的信息,可能会失败这些对象的反序列化是因为新的类定义使用了新的字段和新的数据类型。
显然我们的问题是序列化暴露了有关我们对象的敏感信息,现在我们不能简单地改变任何东西,甚至不是我们认为封装的东西,因为通过序列化,一切都已公开曝光。
现在,考虑一种情况,默认情况下JDK API中的每个类都是可序列化的。 Java的设计者根本无法发展Java的API而不会有破坏许多应用程序的风险。他们将被迫假设那里的人可能有JDK中任何类的序列化版本。
有很多方法可以处理可序列化类的发展,但重要的一点是,当涉及到封装时,我们希望尽可能包含可序列化的类以及我们确实需要的那些类。序列化,然后我们可能需要思考任何可能的场景的含义,在这些场景中我们可能尝试使用其类的进化版本来复活对象。
尽管如此,序列化也具有安全隐患,因为重要的是,有关我们对象的敏感信息很容易暴露出来。
因此,使用可序列化标记的类,可以使API的设计者更容易处理它们。
答案 2 :(得分:0)
Serializable被称为标记接口,如Cloneable。当你使用一些标记接口(没有方法实现)时,你想告诉jvm或编译器在运行时或编译时添加或检查一些东西。
标记界面定义:
“当一个接口被java解释器提供为句柄以标记一个类,以便它可以在运行时为它提供特殊的行为并且它们没有任何方法声明时,它被称为标记接口。”
标记界面也是对代码进行分类的好方法。您可以创建标记接口以逻辑划分代码,如果您拥有自己的工具,则可以对这些类执行一些预处理操作。对于开发像Spring和Struts这样的API和框架特别有用。
标记界面还可以帮助代码覆盖或代码审查工具根据标记接口的指定行为查找错误。
请参阅wiki http://en.wikipedia.org/wiki/Marker_Interface_pattern
中的标记界面