gpt4 book ai didi

java - Java序列化-实现细节泄漏

转载 作者:行者123 更新时间:2023-11-30 03:54:32 25 4
gpt4 key购买 nike

在演讲中,约书亚·布洛赫(Joshua Bloch)谈到了“实施细节泄漏”


  “在Java中,一个非常臭名昭著的例子是,
  可序列化。”完成此操作后,您的整个实现
  只是作为API的一部分泄漏出去了,因为串行形式
  包括构成对象的整个字段,甚至是
  私有字段,因此突然之间,私有字段是
  公共API,这确实非常糟糕。以及解决方法
  顺便说一句是精心设计您的序列表。不要只是说
  实现可序列化。”


如果你们能帮助我理解他的意思是


  解决这个问题的方法是设计您的序列表
  小心。不要只是说实现可序列化。

最佳答案

通过封装,我们假装没有透露对象的内部表示,并且我们仅通过组件的公共接口与组件进行交互。当我们想要更改组件中数据的内部表示而不会破坏其用户的任何代码时,通常会在以后利用的理想属性。

相反,序列化意味着通过将对象的状态转换成其他可以稍后存储和恢复的格式来公开对象的内部状态。这意味着,一旦序列化,就无法更改对象的内部结构,而不会冒着此复活过程成功的风险。

序列化的问题不仅会出现在开放系统的情况下,还会出现在某种程度上依赖于它的分布式系统中。例如,如果我们停止应用程序服务器,则它可能会选择在当前会话中序列化对象,以便稍后在服务器重新启动时恢复它们,但是如果我们使用可序列化对象的新版本重新部署应用程序,它们是否仍会服务器尝试重新启动它们时是否兼容?在分布式系统中,通常使用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的第一个版本。不过,对于第二个版本,我们想引入两个更改:首先,我们发现最好将一个人的出生日期而不是年龄存储为整数,然后将其定义为第二个变量类Person可能在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的用户序列化为二进制形式或其他格式,现在他们希望发展为我们的第二版代码。

然后,如果我们想像在第二个示例中所做的那样进行一些更改,我们将立即破坏其中的一些;所有具有基于该类原始版本进行序列化的对象的对象,这些对象存储的对象都包含一个名为int类型的age字段,包含一个人的年龄,以及名为isMale的boolean类型的字段,其中包含有关性别的信息,这些对象可能会在这些对象的反序列化,因为新的类定义使用了新的字段和新的数据类型。

显然,这里的问题是序列化已经公开了有关对象的敏感信息,现在我们不能简单地更改任何内容,甚至不能更改我们认为已封装的内容,因为通过序列化,所有内容都已公开公开。

有多种方法可以处理可序列化类的演变,但是这里的重点是,在封装时,我们希望将可序列化类尽可能地包含在其中,而对于确实需要序列化的那些类,则我们可能需要考虑任何可能发生的情况,其中我们可能会尝试使用其类的演化版本来复活一个对象。

因此,Joshua Block似乎暗示着,如果您想促进封装,也许您想要的是序列化其他内容,可能不是对象的所有内部状态,或者为序列化目的设计完全不同的东西,另一个类它不会暴露对象的太多内部状态,也不会干扰序列化过程(writeObject,readObject)来决定要序列化的对象。

更糟糕的是,当对象被序列化时,它还会将其图中的其他对象(如果它们可序列化)也被序列化,这会使它们暴露于非常麻烦的境地。这就是Josua说“对象的理想序列化形式仅包含该对象表示的逻辑数据”的原因。

关于java - Java序列化-实现细节泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23581072/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com