Пример двойной отправки в Java

Я читал статью в Википедии о DD и перешел к "Двойная диспетчеризация в Java и пример" ссылка в конце. Описание следующего примера Serializable кажется мне довольно запутанным:

A a = new A();
ObjectOutputStream oos = new ObjectOutputStream();
oos.writeObject( a);

Вот описание:

Чтобы сериализовать A, ObjectOutputStream сначала проверяет, существует ли метод writeObject( ObjectOutputStream oos). Если это так, то он вызывает этот метод с самим собой в качестве аргумента. Затем метод writeObject отправляет вызов обратно в ObjectOutputStream (что делает его двойной отправкой). Выполняя этот очень простой обратный вызов, ObjectOutputStream сказал: "Я делегирую вам ответственность за запись вашего состояния в этот поток". Сделав этот простой выбор, ObjectOutputStream отделился от нашего объекта A. Объект A в свою очередь говорит ок, вот какое-то состояние, которое я хочу записать в поток. Если значение является примитивом, то с ним можно справиться путем перегрузки метода записи. Если нет, то можно продолжать движение вперед и назад на следующем уровне графа объектов до тех пор, пока все полезное состояние не будет размещено в потоке.

Я предполагаю, что описание может сбивать с толку, потому что описывается то, что происходит за кулисами, а не представленный код, потому что в противном случае это не имеет особого смысла. Вот части, которые меня смущают:

  • «ObjectOutputStream сначала проверяет, существует ли метод writeObject( ObjectOutputStream oos)». Зачем ObjectOutputStream проверять наличие этого метода, если это его собственный метод?
  • «Если это так, то он вызывает этот метод с самим собой в качестве аргумента». Мне кажется, что он вызывает writeObject с экземпляром A в качестве аргумента. Возвращаясь к предыдущему пункту, зачем ему искать подпись writeObject с аргументом ObjectOutputStream, если он вызывается с экземпляром A?
  • «Затем метод writeObject отправляет вызов обратно в ObjectOutputStream (что делает его двойной отправкой)». Опять же, метод writeObject принадлежит классу ObjectOutputStream, поэтому я не вижу, как он «отправляется обратно в ObjectOutputStream», поскольку он кажется первоначальным пунктом назначения.

Я просто упустил что-то фундаментальное или это плохо написанный/описанный пример? Если это плохой пример, я хотел бы изменить статью в Википедии, чтобы указать на лучшую, поэтому не стесняйтесь вносить предложения.

Спасибо.


person WXB13    schedule 31.10.2012    source источник
comment
Спасибо за хорошие ответы. Я все же считаю, что эта статья была бы намного лучше, если бы в ней было хотя бы краткое описание/объяснение лежащих в ее основе механизмов.   -  person WXB13    schedule 31.10.2012
comment
Это технически точно, но коряво сформулировано. Вы правы, что один или два примера помогли бы.   -  person Ted Hopp    schedule 31.10.2012


Ответы (2)


Чтобы ответить на ваши вопросы по порядку:

  1. ObjectOutputStream.writeObject(Object) проверяет свой аргумент (используя отражение), чтобы определить, реализует ли объект writeObject(ObjectOutputStream). То есть он более или менее спрашивает объект: «Вы знаете, как записать свою структуру в ObjectOutputStream

  2. Если ответ "да", то ObjectOutputStream вызывает метод writeObject(ObjectOutputStream) объекта, передавая себя (ObjectOutputStream, а не объект) в качестве аргумента. По сути, это говорит: «Хорошо. Напишите мне свою структуру».

  3. Реализация writeObject(ObjectOutputStream) в объекте вызывает ObjectOutputStream для записи элементов самого себя. Повторный вызов oos.writeObject(this) не предполагается.

Например, предположим, что у меня есть такой объект:

class A {
    int x;
}

Если бы я хотел, чтобы он мог записывать себя в ObjectOutputStream, я мог бы расширить его следующим образом:

class A implements Serializable {
    int x;
    private void writeObject(ObjectOutputStream stream) throws IOException {
        stream.writeInt(x);
    }
}

Затем этот код:

A a = new A();
ObjectOutputStream oos = . . .;
oos.writeObject(a);

будет использовать метод writeObject в классе A для записи a, а не использовать отражение для записи всех (непереходных) полей A. (В этом случае, однако, не будет никакой разницы.)

person Ted Hopp    schedule 31.10.2012
comment
Что объясняет его. Спасибо. - person WXB13; 31.10.2012

Из документации по интерфейсу Serializable:

Классы, которые требуют специальной обработки в процессе сериализации и десериализации, должны реализовывать специальные методы со следующими точными сигнатурами:

private void writeObject(java.io.ObjectOutputStream out) бросает IOException private void readObject(java.io.ObjectInputStream in) бросает IOException, ClassNotFoundException; private void readObjectNoData() генерирует исключение ObjectStreamException;

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

person Bhesh Gurung    schedule 31.10.2012
comment
О да. У меня должен быть RTFM (что я обычно и делаю). Спасибо. - person WXB13; 31.10.2012