必须知道java瞬态变量类型才能进行序列化?

时间:2012-06-21 15:47:39

标签: java types serializable transient

我注意到在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需要知道地址的类型,尽管它不需要序列化它?

非常感谢。

4 个答案:

答案 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)

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

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

您需要知道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();
        }
    }
}