【问题标题】:java transient variable type must be known for serialization?java 瞬态变量类型必须知道序列化吗?
【发布时间】:2012-06-23 21:47:06
【问题描述】:

我注意到在 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
    }
}

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

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

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

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

非常感谢。

【问题讨论】:

  • 我不知道你想说什么............你的意思是地址为空吗?
  • 否,Address 是未定义的类型,这意味着:没有 Address 的类文件。如果您想知道这个问题的答案,请阅读下面的 matts 答案。他答对了。
  • 我已编辑您的问题以匹配您接受的答案所提出的假设。如果这确实是您想要的,它会让您的问题更容易被未来的访问者理解和使用。

标签: java types serializable transient


【解决方案1】:

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

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

第一次使用反射 API 检索特定类的字段信息时,它会检索并缓存该类中所有字段的信息。为此,它必须解析类中每个字段的类型,因此将尝试为每个字段加载类文件。 (可以在Java源代码中看到:ObjectOutputStream使用ObjectStreamClass,它调用Class.getDeclaredField,它调用私有方法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; }
}

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

【讨论】:

  • 好的,谢谢 matts 这完全回答了我的问题。这个答案应该是最重要的,因为你专门回答了我问题的各个方面,但我不能投票,因为我没有 15 声望,抱歉......
  • 哦,我看到作为问题的所有者,我可以将其投票为接受的答案,我做到了。再次感谢。
【解决方案2】:

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

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

【讨论】:

  • 感谢您回答彼得,我确实会在与 Person 项目中定义的 Address 相同的包名中定义一个虚拟类 Address。我想我将能够序列化...
【解决方案3】:

要序列化一个类,必须满足两个条件:

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

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

【讨论】:

    【解决方案4】:

    希望对你有帮助

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-03-05
      • 2011-11-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-19
      • 2015-01-13
      相关资源
      最近更新 更多