Почему HashMap сам реализует методы writeObject и readObject?

Java Spring

Оригинал: Miss Sister Taste (идентификатор публичной учетной записи WeChat: xjjdog), добро пожаловать, пожалуйста, сохраните источник для перепечатки.

Феномен

Если вы внимательно читали исходный код HashMap, то наверняка заметили проблему: в HashMap есть два приватных метода.

private void writeObject(java.io.ObjectOutputStream s) throws IOException
private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException

Эти два метода имеют две общие черты:

  1. частные методы
  2. Хотя частные методы, я не могу найти места для их вызова внутри HashMap.

сомневаться

Для чего используются эти два метода?

Зачем делать это приватным?

отвечать

Какова роль методов writeObject и readObject в HashMap?

Ответ: Методы readObject и writeObject созданы для сериализации HashMap.

Прежде всего, HashMap реализует интерфейс Serializable, что означает, что класс может быть сериализован, и класс, предоставляемый JDK для операции сериализации объектов Java, — это ObjectOutputStream, а десериализованный класс — ObjectInputStream. Давайте взглянем на ObjectOutputStream, используемый для сериализации, который предоставляет различные методы для сериализации объектов разных типов, таких как writeBoolean, writeInt, writeLong и т. д. Для пользовательских типов предоставляется метод writeObject. Метод writeObject ObjectOutputStream вызовет следующий метод:

private void writeSerialData(Object obj, ObjectStreamClass desc) 
    throws IOException 
    {
    ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
    for (int i = 0; i < slots.length; i++) {
        ObjectStreamClass slotDesc = slots[i].desc;
        if (slotDesc.hasWriteObjectMethod()) {//如果重写了writeObject方法
            PutFieldImpl oldPut = curPut;
            curPut = null;
            SerialCallbackContext oldContext = curContext;
            try {
                curContext = new SerialCallbackContext(obj, slotDesc);
                bout.setBlockDataMode(true);
                slotDesc.invokeWriteObject(obj, this);  //调用实现类自己的writeobject方法
                bout.setBlockDataMode(false);
                bout.writeByte(TC_ENDBLOCKDATA);
            } finally {
                //省略
            } 
            curPut = oldPut;
        } else {
            defaultWriteFields(obj, slotDesc);
        }
    }
    }

Или следующая картинка тоже может быть:

Можно видеть, что когда операция сериализации фактически выполняется в ObjectOutputStream, он определяет, перезаписал ли сериализованный объект метод writeObject сам по себе. Если он перезаписан, будет вызван собственный метод writeObject сериализованного объекта. будет вызван метод сериализации по умолчанию.

Отношения вызова следующие:

Почему и readObject, и writeObject в HashMap являются частными?

Причина, по которой для него установлено значение private, четко не указана в документации JDK. Метод является закрытым, поэтому метод не может быть переопределен подклассами, в чем польза от этого? Если я реализую класс, который наследует HashMap, и я также хочу иметь свои собственные методы сериализации и десериализации, тогда я также могу реализовать частные методы readObject и writeObject, не заботясь об этой части самого HashMap. Следующий раздел взят из StackOverFlow:

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

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

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

В HashMap, поскольку место хранения записи вычисляется в соответствии с хэш-значением ключа, а затем сохраняется в массиве, для одного и того же ключа хеш-значение, вычисленное в разных реализациях JVM, может отличаться.

Результатом разных значений Hash является то, что результат десериализации объекта HashMap может не соответствовать результату до сериализации. То есть возможно, что перед сериализацией элемент с Key='AAA' помещается в 0-ю позицию массива, а после десериализации значения при получении элемента по Ключу может быть получен из позиции из 2 в массиве, и это Данные, полученные во время сериализации, определенно отличаются от данных до сериализации.

В «Эффективной Яве» боги Джошуа объяснили это:

Например, рассмотрим случай хэш-таблицы. Физическое представление — это последовательность хэш-сегментов, содержащих записи типа "ключ-значение". Сегмент, в котором находится запись, является функцией хэш-кода ее ключа, который, , гарантированно будет одним и тем же от реализации JVM к реализации JVM. На самом деле, даже не гарантируется, что он будет одинаковым от запуска к запуску. Поэтому принятие сериализованной формы по умолчанию для хэш-таблицы будет представлять собой серьезную ошибку. Сериализация и десериализация хэш-таблицы может привести к объекту, инварианты которого серьезно повреждены.

Поэтому, чтобы избежать этой проблемы, HashMap использует следующий метод для ее решения:

    1. Используйте ключевое слово transient для изменения элементов, которые могут привести к несогласованности данных, чтобы избежать операции сериализации объекта методом сериализации по умолчанию в JDK. К несериализованным относятся: таблица Entry[], размер, modCount.
    1. Реализуйте метод writeObject самостоятельно, чтобы обеспечить согласованность результатов сериализации и десериализации.

Итак, какие средства использует HashMap для обеспечения согласованности сериализованных и десериализованных данных? Прежде всего, когда HashMap сериализуется, он сериализует не массив, в котором хранятся данные, а сериализует количество элементов, а также ключ и значение каждого элемента. Во время десериализации позиции Key и Value пересчитываются, а массив перезаполняется. Подумайте об этом, можете ли вы решить несоответствие между сериализацией и десериализацией? Поскольку массив Entry, в котором хранятся элементы, не сериализуется, он перегенерируется при десериализации, что позволяет избежать того, что элементы, полученные по ключу после десериализации, отличаются от элементов, полученных до сериализации.

Об авторе:Мисс сестра вкус(xjjdog), публичная учетная запись, которая не позволяет программистам идти в обход. Сосредоточьтесь на инфраструктуре и Linux. Десять лет архитектуры, десятки миллиардов ежедневного трафика, обсуждение с вами мира высокой параллелизма, дающие вам другой вкус. Мой личный WeChat xjjdog0, добро пожаловать в друзья для дальнейшего общения.​