Почему readObject и writeObject закрыты и почему я должен записывать переходные переменные явно? - PullRequest
35 голосов
/ 19 сентября 2011

Я читаю главу о сериализации в Эффективная Java .

  1. Кто вызывает readObject () и writeObject ()?Почему эти методы объявлены закрытыми?

  2. Ниже приведен фрагмент кода из книги

    // StringList with a reasonable custom serialized form
    public final class StringList implements Serializable {
        private transient int size = 0;
        private transient Entry head = null;
    
        //Other code
    
        private void writeObject(ObjectOutputStream s)
            throws IOException {
            s.defaultWriteObject();
            s.writeInt(size);
            // Write out all elements in the proper order.
            for (Entry e = head; e != null; e = e.next)
               s.writeObject(e.data);
            }
        }
    }
    

    Существует ли какая-либо конкретная причина, по которой объявлена ​​переменная sizeкак переходный, а затем в методе writeObject это явно написано?Если бы он не был объявлен переходным, он все равно был бы написан, верно?

Ответы [ 6 ]

35 голосов
/ 19 сентября 2011

(1) Методы не объявлены ни в одном классе или интерфейсе. Класс, который реализует интерфейс Serializable и требует специальной специальной обработки в процессе сериализации и десериализации, должен реализовывать эти методы, и сериализатор / десериализатор попытается отразить эти методы.

Это один из довольно странных углов в Java, где API фактически определен в javaDoc ... Но если методы были определены в интерфейсе, то они имели быть public (мы не можем реализовать метод интерфейса, заблокировать его, добавив модификатор private).

Почему личное - javaDoc не дает подсказки. Возможно, они определены как частные, потому что никакой другой класс, кроме разработчика, не предназначен для их использования. Они частные по определению .

(2) В примере просто показано, как работает специальная обработка. В этом примере size является временным и не будет сериализован. Но теперь мы представляем специальный обработчик, и этот обработчик добавляет значение size в поток. Отличие от обычного подхода с нестационарными полями может заключаться в порядке элементов в результирующем потоке (если это имеет значение ...).

Пример мог бы иметь смысл, если бы переходное поле было определено в суперклассе, а подкласс хотел сериализовать значение.

17 голосов
/ 19 сентября 2011

Помимо не должны использоваться неправильными сторонами , вот еще одна причина конфиденциальности этих методов:

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

(Обратите внимание, что это относится только к суперклассам, которые сами реализуют Serializable.)

Таким образом, подклассы и суперклассы могут развиваться независимо и при этом оставаться совместимыми с сохраненными объектами более старых версий.

10 голосов
/ 21 ноября 2012

О том, что readObject () / writeObject () является приватным, вот в чем дело: если ваш класс Bar расширяет некоторый класс Foo; Foo также реализует readObject () / writeObject (), а Bar также реализует readObject () / writeObject ().

Теперь, когда объект Bar сериализован или десериализован, JVM необходимо вызвать readObject () / writeObject () для Foo и Bar автоматически (т.е. без необходимости явной классификации этих методов суперкласса). Однако, если эти методы не являются частными, они переопределяют методы, и JVM больше не может вызывать методы суперкласса для объекта подкласса.

Следовательно, они должны быть частными!

7 голосов
/ 19 сентября 2011

readObject и writeObject вызываются классами Object(Input/Output)Stream.

Эти методы (и должны быть) объявлены закрытыми (при реализации ваших собственных), доказывая / указывая, что ни один из методов не наследуется и не переопределяется или перегружается реализацией. Хитрость в том, что JVM автоматически проверяет, объявлен ли какой-либо метод во время соответствующего вызова метода. Обратите внимание, что JVM может вызывать закрытые методы вашего класса, когда хочет , но никакие другие объекты не могут . Таким образом, целостность класса сохраняется, и протокол сериализации может продолжать работать в обычном режиме.

А что касается переходного процесса int, он просто контролирует сериализацию всей сериализации объекта как таковой. Однако обратите внимание, что технически даже необязательно вызывать defaultWriteObject(), если все поля переходные. Но я думаю, что все еще рекомендуется вызывать его для целей гибкости, чтобы позже вы могли вводить в своем классе непереходные члены, сохраняя совместимость.

0 голосов
/ 20 сентября 2013

Предположим, у вас есть класс A, который имеет ссылку на сокет.Если вы хотите сериализовать объекты класса A, вы не можете напрямую, потому что Socket не Serializable.В этом случае вы пишете код, как показано ниже.

public class A implements  implements Serializable {

// mark Socket as transient so that A can be serialized

private transient Socket socket;

private void writeObject(ObjectOutputStream out)throws IOException {
    out.defaultWriteObject();

    // take out ip address and port write them to out stream
    InetAddress inetAddress = socket.getInetAddress();
    int port = socket.getPort();
    out.writeObject(inetAddress);
    out.writeObject(port);
}



private void readObject(ObjectInputStream in)
                  throws IOException, ClassNotFoundException{
    in.defaultReadObject();
    // read the ip address and port form the stream and create a frest socket.
    InetAddress inetAddress = (InetAddress) in.readObject();
    int port = in.readInt();
    socket = new Socket(inetAddress, port);
}
}

Игнорируйте любые проблемы, связанные с сетью, так как цель - показать использование методов writeObject / readObject.

0 голосов
/ 21 сентября 2011

Что касается переходной переменной, лучший способ понять, как будто мы объявляем временную переменную, а затем сериализуем их в методе writeobject, - это проверять / анализировать / отлаживать методы readobject / writeobject классов LinkedList / HashMap / etc.

Обычно это делается, когда вы хотите сериализовать / десериализовать переменные класса в предопределенном порядке и не полагаться на поведение / порядок по умолчанию.

...