我注意到在Java中,您可以使用具有您无法使用的类型成员的类。例如,假设您有以下.class
类的预编译Person
文件;但不适用于它使用的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
}
}
即使您的Java项目无法使用Person person = Person("Male", "Andrew");
类型,也可以使用Address
实例化此类。
现在的问题是,如果要序列化Person
对象,除非Address
类型可用(即可以由类加载器加载),否则不能这样做,即使{ {1}}字段是暂时的。
由于某些原因,Java需要知道类型Address
,即使它不需要序列化它......
有关如何序列化Address
的任何帮助,而无需重新定义没有地址的Person
类?并解释为什么Java需要知道地址的类型,尽管它不需要序列化它?
非常感谢。
答案 0 :(得分:4)
我的运营假设您有.class
Person
个文件,但.class
没有Address
个文件。 (也许Person
是来自不同项目的依赖项?)
您无法序列化Person
(即使Address
字段是暂时的)的原因是序列化API使用Java反射API。虽然JVM将加载一个类而不加载其所有依赖项,但反射API并不宽容。
第一次使用反射API检索特定类的字段信息时,它会检索并缓存类中所有字段的信息。为此,它必须解析类中每个字段的类型,因此将尝试为每个字段加载类文件。 (您可以在Java源代码中看到这一点:ObjectOutputStream
使用ObjectStreamClass
,调用Class.getDeclaredField
,调用私有方法privateGetDeclaredFields
,它解析并缓存所有字段定义为了上课。)
作为解决方法,如果您的代码从不实际使用Address
类型的任何对象,您只需创建一个空的Address
类正确的包,编译它,并将其添加到您的类路径:
public class Address { }
对于那些认为在运行时没有Person
类定义而无法使用Address
的人,以下三个类演示了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; }
}
<强> Address.java 强>
public class Address {
private String address;
public String getAddress() { return address; }
}
<强> Test.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
答案 1 :(得分:2)
问题是要加载一个对象,它必须首先加载该类。要加载类,它必须解析所有字段的类,如果没有类Address
,则无法加载类或示例字段以确定它是瞬态的。
您可以通过提供位于类路径末尾的虚拟Address
类来解决此问题。即仅在未定义时使用。
答案 2 :(得分:0)
对于要序列化的类,必须满足两个条件:
您需要知道Address类才能构建项目。这与对象的序列化无关。地址类必须在编译时可用...
答案 3 :(得分:0)
我希望它能帮到你
<强> Person.java 强>
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
}
private void writeObject(ObjectOutputStream os) {
try {
os.defaultWriteObject();
os.writeUTF(this.address.getAddress());
} catch (IOException e) {
e.printStackTrace();
}
}
private void readObject(ObjectInputStream is) {
try {
is.defaultReadObject();
this.address = new Address(is.readUTF());
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
<强> Address.java 强>
public class Address {
private String address;
public String getAddress() { return address; }
public void setAddress() { this address = address}
}
<强> Test.java 强>
public class Test {
public static void main(String...args) throws Exception {
Person person = new Person("John Doe");
person.setAddress("test address");
try {
FileOutputStream fis = new FileOutputStream("simple.txt");
ObjectOutputStream os = new ObjectOutputStream(fis);
os.writeObject(person);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
FileInputStream fis = new FileInputStream("simple.txt");
ObjectInputStream os = new ObjectInputStream(fis);
person = (Person) os.readObject();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}