3

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ここでの問題は、オブジェクトをシリアライズしたい場合、フィールドが transientAddressであっても、型が利用可能 (つまり、クラスローダーによってロードできる) でない限り、これを行うことができないことです。Address

なんらかの理由Addressで、シリアライズする必要がないにもかかわらず、Java は型を知る必要があります...

アドレスなしでクラスPersonを再定義することなく、とにかくシリアル化する方法について何か助けはありますか? Personそして、シリアライズする必要はないのに、Javaがアドレスのタイプを知る必要がある理由についての説明はありますか?

どうもありがとう。

4

4 に答える 4

4

.classのファイルはあるが のファイルPersonがないという前提で操作しています。(おそらく、別のプロジェクトからの依存関係ですか?).classAddressPerson

Person(フィールドが一時的であっても)シリアル化できない理由Addressは、シリアル化 API が Java リフレクション API を使用するためです。JVM はすべての依存関係をロードせずにクラスをロードしますが、リフレクション API はそれほど寛容ではありません。

リフレクション API を初めて使用して特定のクラスのフィールド情報を取得すると、そのクラスのすべてのフィールドの情報が取得され、キャッシュされます。これを行うには、クラス内のすべてのフィールドの型を解決する必要があるため、すべてのフィールドのクラス ファイルをロードしようとします。(これは Java ソース コードで確認できます: ObjectOutputStreamusesObjectStreamClassを呼び出します。これは、クラスのすべてのフィールド定義を解決してキャッシュするClass.getDeclaredFieldプライベート メソッドを呼び出します。)privateGetDeclaredFields

回避策として、コードで type のオブジェクトを実際に使用しない場合Address、正しいパッケージに空のAddressクラスを作成し、コンパイルして、クラス パスに追加するだけです。

public class Address { }

クラス定義Personなしでは実行時に使用できないと考える人のために、次の 3 つのクラスは OP が何について話しているかを示しています。Address

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
于 2012-06-21T16:48:53.737 に答える
2

問題は、オブジェクトをロードするには、最初にクラスをロードする必要があることです。クラスをロードするには、すべてのフィールドのクラスを解決する必要があります。クラスAddressがない場合は、クラスをロードしたり、フィールドをサンプルして一時的であると判断したりすることはできません。

クラスパスの最後にあるダミーAddressクラスを提供することで、これを解決できます。つまり、定義されていない場合にのみ使用されます。

于 2012-06-21T15:55:51.497 に答える
0

クラスをシリアル化するには、次の2つの条件を満たす必要があります。

  • クラスはjava.io.Serializableインターフェースを実装する必要があります。
  • クラス内のすべてのフィールドはシリアル化可能である必要があります。フィールドがシリアル化できない場合は、一時としてマークする必要があります。

プロジェクトをビルドするには、Addressクラスを知っている必要があります。これは、オブジェクトのシリアル化とは関係ありません。アドレスクラスはコンパイル時に使用可能である必要があります...

于 2012-06-21T15:58:25.223 に答える