gpt4 book ai didi

Java transient 变量类型必须知道序列化?

转载 作者:塔克拉玛干 更新时间:2023-11-01 22:52:00 25 4
gpt4 key购买 nike

我注意到在 Java 中,您可以使用一个类,该类具有您不可用的类型成员。例如,假设您有一个用于以下 Person 类的预编译 .class 文件;但不适用于 Address,它使用:

public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String sex;
private String name;
private transient Address address;

public Person(String sex, String name) {
this.sex = sex; this.name = name;
}
public void setAddress(Address address) {
this.address = address
}
}

您可以使用 Person person = Person("Male", "Andrew"); 实例化此类,即使类型 Address 对您的 Java 项目不可用。

现在的问题是,如果你想序列化一个 Person 对象,除非 Address 类型可用(即可以由类加载器加载),否则你不能这样做),即使 Address 字段是暂时的

出于某种原因,Java 需要知道 Address 类型,即使它不需要序列化它...

关于如何序列化 Person 而不必重新定义没有地址的 Person 类的任何帮助?以及为什么 Java 需要知道地址的类型,尽管它不需要序列化它的任何解释?

非常感谢。

最佳答案

我假设您有一个 Person.class 文件,但没有 .class 文件 地址。 (也许 Person 是来自不同项目的依赖项?)

无法序列化 Person 的原因(即使 Address 字段是 transient 的)是因为序列化 API 使用 Java 反射 API。尽管 JVM 将加载一个类而不加载其所有依赖项,但反射 API 并不那么宽容。

第一次使用反射 API 检索特定类的字段信息时,它会检索并缓存类中所有 字段的信息。为此,它必须解析类中每个字段的类型,因此将尝试为每个字段加载类文件。 (您可以在 Java 源代码中看到这一点:ObjectOutputStream 使用 ObjectStreamClass,它调用 Class.getDeclaredField,它调用私有(private)方法 privateGetDeclaredFields,它解析并缓存类的所有字段定义.)

作为解决方法,如果您的代码从不实际上使用任何Address 类型的对象,您可以简单地创建一个空的Address 类在正确的包中,编译它,并将它添加到你的类路径中:

public class Address { }

对于那些认为如果没有 Address 类定义就无法在运行时使用 Person 的人来说,以下三个类演示了 OP 所讨论的内容:

Person.java

import java.io.Serializable;
public class Person implements Serializable {
private String name;
private transient Address address;
public Person(String name) { this.name = name; }
public Address getAddress() { return address; }
public String getName() { return name; }
}

地址.java

public class Address {
private String address;
public String getAddress() { return address; }
}

测试.java

import java.io.FileOutputStream;
import java.io.ObjectOuputStream;
public class Test {
public static void main(String...args) throws Exception {
Person person = new Person("John Doe");
System.out.println("Person successfully instantiated with name " + person.getName());

// now attempt to serialize
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.out"));
out.writeObject(person); // NoClassDefFoundError thrown here if Address.class doesn't exist
out.close();
}
}

现在编译Test.java,删除Address.class,运行Test:

$ javac Test.java
$ rm Address.class
$ java Test
Person successfully instantiated with name John Doe
Exception in thread "main" java.lang.NoClassDefFoundError: LAddress;
at java.lang.Class.getDeclaredFields0(Native Method)
at java.lang.Class.privateGetDeclaredFields(Class.java:2308)
at java.lang.Class.getDeclaredField(Class.java:1897)
at java.io.ObjectStreamClass.getDeclaredSUID(ObjectStreamClass.java:1624)
at java.io.ObjectStreamClass.access$700(ObjectStreamClass.java:69)
at java.io.ObjectStreamClass$2.run(ObjectStreamClass.java:442)
at java.security.AccessController.doPrivileged(Native Method)
at java.io.ObjectStreamClass.<init>(ObjectStreamClass.java:430)
at java.io.ObjectStreamClass.lookup(ObjectStreamClass.java:327)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1130)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:346)
at Test.main(Test.java:10)
Caused by: java.lang.ClassNotFoundException: Address
at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
at java.lang.ClassLoader.loadClass(ClassLoader.java:321)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
at java.lang.ClassLoader.loadClass(ClassLoader.java:266)
... 12 more

关于Java transient 变量类型必须知道序列化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11141886/

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