3

我注意到在 Java 中,您可以使用一个类,该类具有您不可用的类型成员。例如,假设您有.class以下Person类的预编译文件;但不是 for 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 需要知道地址类型尽管它不需要序列化它的任何解释?

非常感谢。

4

4 回答 4

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 的含义:

人.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
于 2012-06-21T16:48:53.737 回答
2

问题是要加载一个对象,它必须首先加载类。要加载类,它必须解析所有字段的类,如果没有类Address,则无法加载类或示例字段以确定它是瞬态的。

您可以通过提供一个Address位于类路径末尾的虚拟类来解决此问题。ie 仅在未定义时使用。

于 2012-06-21T15:55:51.497 回答
0

对于要序列化的类,必须满足两个条件:

  • 该类必须实现 java.io.Serializable 接口。
  • 类中的所有字段都必须是可序列化的。如果字段不可序列化,则必须将其标记为瞬态。

您需要知道 Address 类才能构建项目。这与对象的序列化无关。地址类必须在编译时可用...

于 2012-06-21T15:58:25.223 回答
0

我希望它会帮助你

人.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();
            }
        }

    }

地址.java

    public class Address {
        private String address;
        public String getAddress()  { return address; }
        public void setAddress() { this address = address}
    }

测试.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();
        }
    }
}
于 2014-06-17T02:32:09.933 回答