2012-06-21 2 views
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 
    } 
} 

Вы можете создать экземпляр этого класса с Person person = Person("Male", "Andrew"); даже если тип Address не доступен для вашего проекта Java.

Теперь проблема, если вы хотите сериализовать Person объект, вы не можете сделать это, если Address типа не доступен (т.е. может быть загружен загрузчиком классов), даже несмотря на то, Address поля преходяще.

почему Java нужно знать тип Address, даже если это не нужно сериализовать ...

Любая помощь о том, как я мог сериализации Person в любом случае без необходимости переопределить Person класс без адреса? И любое объяснение того, почему Java должен знать тип адреса, хотя его не нужно сериализовать?

Большое спасибо.

+1

Я понятия не имею, что вы пытаетесь сказать ........ ли вы имеете в виду адрес будучи пустой? – Thihara

+0

Нет, адрес является неопределенным типом, это означает: отсутствие файла класса Address. Прочитайте ответ matts ниже, если вы хотите получить ответ на этот вопрос. Он ответил правильно. –

+1

Я отредактировал ваш вопрос в соответствии с предположением, полученным ответом, который вы приняли. Если это действительно то, что вы делали, это делает ваш вопрос более понятным и пригодным для будущих посетителей. – jpaugh

ответ

4

я действую в предположении, что у вас есть .class файла Person, но не .class файла для Address. (Возможно, Person является зависимость от другого проекта?)

Причина вы не можете сериализовать Person (даже несмотря на то, Address поле непостоянно) является то, что сериализации API использует Reflection API Java. Хотя JVM будет загружать класс без загрузки всех его зависимостей, API-интерфейс отражения не так прост.

Впервые API отражения используется для извлечения информации о поле для определенного класса, он извлекает и кэширует информацию для всех полей в классе. Для этого он должен разрешать типы для каждого поля в классе и, таким образом, будет пытаться загрузить файлы классов для каждого поля.(Вы можете увидеть это в исходном коде Java: ObjectOutputStream использует ObjectStreamClass, который вызывает Class.getDeclaredField, который вызывает закрытый метод privateGetDeclaredFields, который разрешает и кэширует все определения полей для данного класса.)

В качестве временного решения, если ваш код никогда фактически не использует какие-либо объекты типа Address, вы можете просто создать пустой класс Address в правильной упаковке, скомпилировать его, и добавить его в свой класс путь:

public class Address { } 

Для тех, кто думает, что это не представляется возможным использовать Person во время выполнения без определения 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; } 
} 

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 
+0

Хорошо, спасибо маты, это полностью отвечает на мой вопрос. Этот ответ должен быть тем, который был проголосован сверху, потому что вы конкретно ответили на каждый аспект моего вопроса, но я не могу его проголосовать, потому что у меня нет 15 репутации извинения ... –

+0

О, я вижу, что как владелец вопроса, я может проголосовать за него как принятый ответ, который я сделал. Еще раз спасибо. –

2

Проблема в том, что для загрузки объекта он должен сначала загрузить класс. Чтобы загрузить класс, он должен разрешить класс всех полей, и если у вас нет класса Address, вы не можете загрузить класс или пример в поле, чтобы определить, что он является временным.

Вы можете решить эту проблему, предоставив класс dummy Address, который находится в конце вашего пути к классу. т.е. используется, если не определено.

+1

Спасибо, что ответите Петру, я действительно просто определю фиктивный адрес класса в том же наименовании пакета, что и адрес, определенный в проекте Person. Я думаю, что тогда я смогу сериализовать ... –

0

Для класса сериализовать, должны быть выполнены два условия:

  • Класс должен реализовывать интерфейс java.io.Serializable.
  • Все поля в классе должны быть сериализованы. Если поле не сериализуемо, оно должно быть помечено как переходное.

Для создания проекта необходимо знать класс адресов. Это не связано с сериализацией объекта. Адрес класс должен быть доступен во время компиляции ...

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