Оригинал: 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
Эти два метода имеют две общие черты:
- частные методы
- Хотя частные методы, я не могу найти места для их вызова внутри 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 использует следующий метод для ее решения:
-
- Используйте ключевое слово transient для изменения элементов, которые могут привести к несогласованности данных, чтобы избежать операции сериализации объекта методом сериализации по умолчанию в JDK. К несериализованным относятся: таблица Entry[], размер, modCount.
-
- Реализуйте метод writeObject самостоятельно, чтобы обеспечить согласованность результатов сериализации и десериализации.
Итак, какие средства использует HashMap для обеспечения согласованности сериализованных и десериализованных данных? Прежде всего, когда HashMap сериализуется, он сериализует не массив, в котором хранятся данные, а сериализует количество элементов, а также ключ и значение каждого элемента. Во время десериализации позиции Key и Value пересчитываются, а массив перезаполняется. Подумайте об этом, можете ли вы решить несоответствие между сериализацией и десериализацией? Поскольку массив Entry, в котором хранятся элементы, не сериализуется, он перегенерируется при десериализации, что позволяет избежать того, что элементы, полученные по ключу после десериализации, отличаются от элементов, полученных до сериализации.
Об авторе:Мисс сестра вкус(xjjdog), публичная учетная запись, которая не позволяет программистам идти в обход. Сосредоточьтесь на инфраструктуре и Linux. Десять лет архитектуры, десятки миллиардов ежедневного трафика, обсуждение с вами мира высокой параллелизма, дающие вам другой вкус. Мой личный WeChat xjjdog0, добро пожаловать в друзья для дальнейшего общения.