ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 01.12.2023
Просмотров: 479
Скачиваний: 3
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
Поля static. Значения статических полей автоматически не сохраняются.
Поля с модификатором final сериализуются как и обычные. За одним исключением – их
невозможно десериализовать при использовании Externalizable, поскольку final-поля должны быть инициализированы в конструкторе, а после этого в readExternal изменить значение этого поля будет невозможно. Соответственно, если необходимо сериализовать объект с final-полем неоходимо использовать только стандартную сериализацию
(Serializable за счет рефлексии).
Если final-поля не кастомные, то будут десериализовываться.
Как создать собственный протокол сериализации?
Для создания собственного протокола нужно переопределить writeExternal() и
readExternal().
В отличие от двух других вариантов сериализации, здесь ничего не делается автоматически.
Протокол полностью в наших руках.
Для создания собственного протокола сериализации достаточно реализовать интерфейс
Externalizable, который содержит два метода:
public void writeExternal(ObjectOutput out) throws IOException;
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
Какая роль поля serialVersionUID в сериализации?
serialVersionUID используется для указании версии сериализованных данных.
Если не объявить serialVersionUID в классе явно, среда выполнения Java делает это за нас,
но этот процесс чувствителен ко многим метаданным класса, включая количество полей, тип полей, модификаторы доступа полей, интерфейсов, которые реализованы в классе и пр.
Рекомендуется явно объявлять serialVersionUID т. к. при добавлении, удалении атрибутов класса динамически сгенерированное значение может измениться и в момент выполнения будет выброшено исключение InvalidClassException.
private static final long serialVersionUID = 20161013L;
Когда стоит изменять значение поля serialVersionUID?
serialVersionUID нужно изменять при внесении в класс несовместимых изменений, например,
при удалении какого-либо его атрибута.
В чем проблема сериализации Singleton?
Проблема в том, что после десериализации получим другой объект. Таким образом,
сериализация дает возможность создать Singleton еще раз, что недопустимо.
Существует два способа избежать этого:
•
явный запрет сериализации;
•
определение метода с сигнатурой (default/public/private/protected/) Object readResolve() throws ObjectStreamException, назначением которого станет возврат замещающего объекта вместо объекта, на котором он вызван.
Как исключить поля из сериализации?
Для управления сериализацией при определении полей можно использовать ключевое слово transient, таким образом исключив поля из общего процесса сериализации.
Что обозначает ключевое слово transient?
Поля класса, помеченные модификатором transient, не сериализуются.
Обычно в таких полях хранится промежуточное состояние объекта, которое, к примеру,
проще вычислить. Другой пример такого поля – ссылка на экземпляр объекта, который не требует сериализации или не может быть сериализован.
Какое влияние оказывают на сериализуемость модификаторы полей static и
final?
При стандартной сериализации поля, имеющие модификатор static, не сериализуются.
Соответственно, после десериализации это поле значения не меняет. При использовании реализации Externalizable сериализовать и десериализовать статическое поле можно, но не рекомендуется этого делать, т. к. это может сопровождаться трудноуловимыми ошибками.
Поля с модификатором final сериализуются как и обычные. За одним исключением – их невозможно десериализовать при использовании Externalizable, поскольку final-поля должны быть инициализированы в конструкторе, а после этого в readExternal() изменить значение этого поля будет невозможно. Соответственно, если необходимо сериализовать объект с final-полем, необходимо использовать только стандартную сериализацию.
Как не допустить сериализацию?
Чтобы не допустить автоматическую сериализацию, можно переопределить private методы для создания исключительной ситуации NotSerializableException.
private void writeObject(ObjectOutputStream out) throws IOException {
throw new NotSerializableException();
}
private void readObject(ObjectInputStream in) throws IOException {
throw new NotSerializableException();
}
Любая попытка записать или прочитать этот объект теперь приведет к возникновению исключительной ситуации.
Какие существуют способы контроля за значениями десериализованного
объекта?
Если есть необходимость выполнения контроля за значениями десериализованного объекта,
то можно использовать интерфейс ObjectInputValidation с переопределением метода validateObject().
Если вызвать метод validateObject() после десериализации объекта, то будет вызвано исключение InvalidObjectException при значении возраста за пределами 39...60.
public class Person implements java.io.Serializable,
java.io.ObjectInputValidation {
...
@Override
public void validateObject() throws InvalidObjectException {
if ((age < 39) || (age > 60))
throw new InvalidObjectException("Invalid age");
}
}
Также существуют способы подписывания и шифрования, позволяющие убедиться, что данные не были изменены:
с помощью описания логики в writeObject() и readObject();
поместить в оберточный класс javax.crypto.SealedObject и/или java.security.SignedObject. Данные классы являются сериализуемыми, поэтому при оборачивании объекта в SealedObject создается подобие «подарочной упаковки»
вокруг исходного объекта. Для шифрования необходимо создать симметричный ключ,
управление которым должно осуществляться отдельно. Аналогично для проверки данных можно использовать класс SignedObject, для работы с которым также нужен симметричный ключ, управляемый отдельно.
Расскажите про клонирование объектов
Использование оператора присваивания не создает нового объекта, а лишь копирует ссылку на объект. Таким образом, две ссылки указывают на одну и ту же область памяти, на один и тот же объект. Для создания нового объекта с таким же состоянием используется клонирование объекта.
Класс Object содержит protected метод clone(), осуществляющий побитовое копирование объекта производного класса. Однако сначала необходимо переопределить метод clone()
как public для обеспечения возможности его вызова. В переопределенном методе следует вызвать базовую версию метода super.clone(), которая и выполняет собственно клонирование.
Чтобы окончательно сделать объект клонируемым, класс должен реализовать интерфейс
Cloneable. Интерфейс Cloneable не содержит методов, относится к маркерным интерфейсам, а его реализация гарантирует, что метод clone() класса Object возвратит точную копию вызвавшего его объекта с воспроизведением значений всех его полей. В
противном случае метод генерирует исключение CloneNotSupportedException. Следует отметить, что при использовании этого механизма объект создается без вызова конструктора.
Это решение эффективно только в случае, если поля клонируемого объекта представляют собой значения базовых типов и их оберток или неизменяемых (immutable) объектных типов.
Если же поле клонируемого типа является изменяемым ссылочным типом, то для корректного клонирования требуется другой подход. Причина заключается в том, что при создании копии оригинальное поле и его копия представляют собой ссылку на один и тот же объект. В этой ситуации следует также клонировать и сам объект поля класса.
Такое клонирование возможно только в случае, если тип атрибута класса также реализует интерфейс Cloneable и переопределяет метод clone(). Иначе вызов метода невозможен из-за его недоступности. Отсюда следует, что если класс имеет суперкласс, то для реализации механизма клонирования текущего класса-потомка необходимо наличие корректной реализации такого механизма в суперклассе. При этом следует отказаться от использования объявлений final для полей объектных типов по причине невозможности изменения их значений при реализации клонирования.
Помимо встроенного механизма клонирования в Java для клонирования объекта можно использовать:
•
специализированный конструктор копирования – в классе описывается конструктор, который принимает объект этого же класса и инициализирует поля создаваемого объекта значениями полей переданного;
•
фабричный метод – (factory method), который представляет собой статический метод, возвращающий экземпляр своего класса;
•
механизм сериализации – сохранение и последующее восстановление объекта в/из потока байтов.
В чем отличие между поверхностным и глубоким клонированием?
Поверхностное копирование копирует настолько малую часть информации об объекте,
насколько это возможно. По умолчанию клонирование в Java является поверхностным, т. е.
класс Object не знает о структуре класса, который он копирует. Клонирование такого типа осуществляется JVM по следующим правилам:
•
если класс имеет только члены примитивных типов, то будет создана совершенно новая копия объекта и возвращена ссылка на этот объект;
•
если класс помимо членов примитивных типов содержит члены ссылочных типов,
то тогда копируются ссылки на объекты этих классов. Следовательно, оба объекта будут иметь одинаковые ссылки.
Глубокое копирование дублирует абсолютно всю информацию объекта:
•
нет необходимости копировать отдельно примитивные данные;
•
все члены ссылочного типа в оригинальном классе должны поддерживать клонирование, для каждого такого члена при переопределении метода clone()
должен вызываться super.clone();
•
если какой-либо член класса не поддерживает клонирование, то в методе клонирования необходимо создать новый экземпляр этого класса и скопировать каждый его член со всеми атрибутами в новый объект класса, по одному.
Какой способ клонирования предпочтительней?
Наиболее безопасным и следовательно предпочтительным способом клонирования является использование специализированного конструктора копирования:
•
отсутствие ошибок наследования (не нужно беспокоиться, что у наследников появятся новые поля, которые не будут склонированы через метод clone();
•
поля для клонирования указываются явно;
•
возможность клонировать даже final-поля.
Почему метод clone() объявлен в классе Object, а не в интерфейсе Cloneable?
Метод clone() объявлен в классе Object с указанием модификатора native, чтобы обеспечить доступ к стандартному механизму поверхностного копирования объектов. Одновременно он объявлен и как protected, чтобы нельзя было вызвать этот метод у не переопределивших его объектов. Непосредственно интерфейс Cloneable является маркерным (не содержит объявлений методов) и нужен только для обозначения самого факта, что данный объект готов к тому, чтобы быть клонированным. Вызов переопределенного метода clone() у не
Cloneable объекта вызовет выбрасывание
1 ... 6 7 8 9 10 11 12 13 ... 25
CloneNotSupportedException.
Как создать глубокую копию объекта (2 способа)?
Глубокое клонирование требует выполнения следующих правил:
•
нет необходимости копировать отдельно примитивные данные;
•
все классы-члены в оригинальном классе должны поддерживать клонирование,
Для каждого члена класса должен вызываться super.clone() при переопределении метода clone();
•
если какой-либо член класса не поддерживает клонирование, то в методе клонирования необходимо создать новый экземпляр этого класса и скопировать каждый его член со всеми атрибутами в новый объект класса, по одному.
Сериализация – это еще один способ глубокого копирования. Просто сериализуем нужный объект и десериализуем его. При этом объект должен поддерживать интерфейс Serializable.
Сохраняем объект в массив байт и потом читаем из него.
Рефлексия
Рефлексия (Reflection) – это механизм получения данных о программе во время ее выполнения (runtime). В Java Reflection осуществляется с помощью Java Reflection API,
состоящего из классов пакетов java.lang и java.lang.reflect.
Возможности Java Reflection API:
•
определение класса объекта;
•
получение информации о модификаторах класса, полях, методах, конструкторах и суперклассах;
•
определение интерфейсов, реализуемых классом;
•
создание экземпляра класса;
•
получение и установка значений полей объекта;
•
вызов методов объекта;
•
создание нового массива.
Класс Optional
Опциональное значение Optional – это контейнер для объекта, который может содержать или не содержать значение null. Такая обертка является удобным средством предотвращения NullPointerException, т. к. имеет некоторые функции высшего порядка,
избавляющие от добавления повторяющихся проверок if null/notNull.
Core-2
Что такое generics?
Generics – это технический термин, обозначающий набор свойств языка, позволяющих определять и использовать обобщенные типы и методы. Обобщенные типы или методы отличаются от обычных тем, что имеют типизированные параметры.
Примером использования обобщенных типов может служить Java Collection Framework. Так,
класс LinkedList
LinkedList
Что такое raw type (сырой тип)?
Это имя интерфейса без указания параметризованного типа:
List list = new ArrayList(); // raw type
List
Что такое стирание типов?
Суть заключается в том, что внутри класса не хранится никакой информации о типе- параметре. Эта информация доступна только на этапе компиляции и стирается (становится недоступной) в runtime.
В чем заключается разница между IO и NIO?
Java IO (input-output) является потокоориентированным, а Java NIO (new/non-blocking io) –
буфер-ориентированным. Потокоориентированный ввод/вывод подразумевает чтение/запись из потока/в поток одного или нескольких байт в единицу времени поочередно. Данная информация нигде не кешируются. Таким образом, невозможно произвольно двигаться по потоку данных вперед или назад. В Java NIO данные сначала считываются в буфер, что дает больше гибкости при обработке данных.
Потоки ввода/вывода в Java IO являются блокирующими. Это значит, что когда в потоке выполнения вызывается read() или write() метод любого класса из пакета java.io.*,
происходит блокировка до тех пор, пока данные не будут считаны или записаны. Поток выполнения в данный момент не может делать ничего другого. Неблокирующий режим Java
NIO позволяет запрашивать считанные данные из канала (channel) и получать только то, что доступно на данный момент, или вообще ничего, если доступных данных пока нет. Вместо того, чтобы оставаться заблокированным, пока данные не станут доступными для считывания, поток выполнения может заняться чем-то другим. То же самое справедливо и для неблокирующего вывода. Поток выполнения может запросить запись в канал некоторых данных, но не дожидаться при этом, пока они не будут полностью записаны.
В Java NIO имеются селекторы, которые позволяют одному потоку выполнения мониторить несколько каналов ввода. Т. е. существует возможность зарегистрировать несколько каналов с селектором, а потом использовать один поток выполнения для обслуживания каналов,
имеющих доступные для обработки данные, или для выбора каналов, готовых для записи.