Подтвердить что ты не робот

Почему readObject и writeObject являются частными, и почему я должен писать переменные переменные явно?

Я читаю главу "Сериализация в эффективной Java".

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

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

    // 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 явно написана? Если бы он не был объявлен переходным, все равно он был бы написан, верно?

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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

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

Ответ 5

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

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

Ответ 6

Предположим, что у вас есть класс A, который имеет ссылку на Socket. Если вы хотите сериализовать объекты класса 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.