【问题标题】:Serializing private variables in java在java中序列化私有变量
【发布时间】:2010-11-22 12:17:05
【问题描述】:

我有一个关于序列化的问题。如果我的类有私有变量并且没有 getter 和 setter,那么序列化 API 将如何读取这些变量的值。

【问题讨论】:

  • 我正在等待更好的答案。过一段时间会接受的。谢谢。

标签: java serialization


【解决方案1】:

首先,访问权限是编译时特性。访问在运行时不受控制。

这可能会让您感到困惑,但请尝试执行以下操作: 创建 2 个版本的 A 类:

1

public class A {
    public foo() {
        System.out.println("hello");
    }
}

2

public class A {
    private foo() {
        System.out.println("hello");
    }
}

现在编写调用 new A().foo() 的类,并使用类 A 的第一个版本对其进行编译。然后将第二个版本放入类路径并运行应用程序。它会起作用的!

所以,不要担心访问权限:它们总是可以被绕过的。

例如,如果您使用反射调用私有方法foo(),则必须获取该方法,然后调用setAccessible(true)

Method m = A.class.getMethod("foo",
null); m.setAccessible(true);
m.invoke(new A(), null);

如果我们可以从我们的代码中访问私有方法,请确保 JDK 类可以做到这一点,即使它们是用 java 编写的。顺便说一句,据我所知,标准 java 序列化是作为本机代码实现的。

【讨论】:

  • 嗯?如果我没有完全弄错,第一部分就是错误的。如果您访问私有方法/字段,您会在运行时获得IllegalAccessError。这是 JVMS 的要求(参见第 5.4.3 节)。运行您的测试可以确认这一点。
【解决方案2】:

序列化 API 不担心私有变量。其目的是将您的对象转换为文件中的二进制表示形式或其他可以稍后重建的存储。

Here是Java的序列化算法解释。

【讨论】:

  • 但它确实需要读取值,对吧?而且 API 是用 Java 编写的,所以它必须有一种读取私有变量的方法。
  • @SidCool:在运行时执行反射和序列化。封装更像是一种设计。
  • 那么你认为如何在调试器中看到私有变量的值。
  • 声明一个私有变量实际上并不能以任何方式保护它。如果您可以访问存储它的内存位置,或者从序列化文件中读取,它仍然可以被读取。如果您不希望这种情况发生(例如,使用密码)用 transient 关键字标记变量(这样它就不会被序列化)或只在字段中以加密形式保存它(java.security.* 有类你可以用这个——谷歌无疑可以提供一个教程)。
  • 是的,你可以做到。同样,您可以在调试器中执行此操作。
【解决方案3】:

默认的序列化机制不关心成员变量的访问范围。换句话说,公共、受保护、包私有和私有变量都以相同的方式处理。实现细节可能会有所不同,但我记得 Sun JRE 通过在本地 (JNI) 代码中实现大部分序列化来实现这一点,其中不强制执行访问权限。

【讨论】:

  • 大部分序列化代码是Java,但是字段访问确实使用JNI。
【解决方案4】:

不用担心,通过使用反射,任何人都可以访问您的私有字段。

Here 是如何执行此操作的示例。

【讨论】:

  • 这不会影响封装吗?
  • 封装有什么问题?我不确定我是否理解这个问题。
猜你喜欢
  • 2012-06-03
  • 2016-08-19
  • 2012-07-26
  • 1970-01-01
  • 2015-01-13
  • 2011-11-13
  • 1970-01-01
  • 2012-05-09
  • 2013-09-23
相关资源
最近更新 更多