Главная страница

ОглавлениеCore


Скачать 1.53 Mb.
НазваниеОглавлениеCore
Дата17.05.2023
Размер1.53 Mb.
Формат файлаpdf
Имя файлаpolnaya_metodichka (1).pdf
ТипДокументы
#1138113
страница10 из 25
1   ...   6   7   8   9   10   11   12   13   ...   25
Возможно ли использование блока try-finally (без catch)?
Такая запись допустима, но смысла в такой записи не так много, все же лучше иметь блок catch, в котором будет обрабатываться необходимое исключение.
Работает точно так же: после выхода из блока try выполняется блок finally.
Может ли один блок catch отлавливать сразу несколько исключений?
В Java 7 стала доступна новая языковая конструкция, с помощью которой можно перехватывать несколько исключений одним блоком catch:
try {
//...
} catch(IOException | SQLException ex) {
//...
}
Всегда ли исполняется блок finally? Существуют ли ситуации, когда блок
finally не будет выполнен?
Да, кроме случаев завершения работы программы или JVM:

Finally может не выполниться в случае если в блоке try вызывает System.exit(0).

Runtime.getRuntime().exit(0), Runtime.getRuntime().halt(0) и если во время исполнения блока try виртуальная машина выполнила недопустимую операцию и будет закрыта.

В блоке try{} бесконечный цикл.
Может ли метод main() выбросить исключение во вне и если да, то где
будет происходить обработка данного исключения?
Может, исключение будет передано в виртуальную машину Java (JVM).
В каком порядке следует обрабатывать исключения в catch-блоках?
От наследника к предку.
Что такое механизм try-with-resources?
Данная конструкция, которая появилась в Java 7, позволяет использовать блок try-catch, не заботясь о закрытии ресурсов, используемых в данном сегменте кода. Ресурсы объявляются в скобках сразу после try, а компилятор уже сам неявно создает секцию finally, в которой и происходит освобождение занятых в блоке ресурсов. Под ресурсами подразумеваются сущности, реализующие интерфейс java.lang.Autocloseable.
Стоит заметить, что блоки catch и явный finally выполняются уже после того, как закрываются ресурсы в неявном finally.
Что произойдет, если исключение будет выброшено из блока catch, после
чего другое исключение будет выброшено из блока finally?
finally-секция может «перебить» throw/return при помощи другого throw/return.

Что произойдет, если исключение будет выброшено из блока catch, после
чего другое исключение будет выброшено из метода close() при
использовании try-with-resources?
В try-with-resources добавлена возможность хранения «подавленных» исключений, и брошенное try-блоком исключение имеет больший приоритет, чем исключения,
получившиеся во время закрытия.
Предположим, есть метод, который может выбросить IOException и
FileNotFoundException. В какой последовательности должны идти блоки
catch? Сколько блоков catch будет выполнено?
Общее правило: обрабатывать исключения нужно от младшего к старшему. Т. е. нельзя поставить в первый блок catch(Exception ex) {}, иначе все дальнейшие блоки catch() уже ничего не смогут обработать, т. к. любое исключение будет соответствовать обработчику catch(Exception ex).
Таким образом, исходя из факта, что FileNotFoundException extends IOException сначала нужно обработать FileNotFoundException, а затем уже IOException:
void method() {
try {
//...
} catch (FileNotFoundException ex) {
//...
} catch (IOException ex) {
//...
}
}
Что такое «сериализация» и как она реализована в Java?
Сериализация (Serialization) – процесс преобразования структуры данных в линейную последовательность байтов для дальнейшей передачи или сохранения. Сериализованные объекты можно затем восстановить (десериализовать).
В Java, согласно спецификации Java Object Serialization, существует два стандартных способа сериализации: стандартная сериализация через использование интерфейса
java.io.Serializable и «расширенная» сериализация – java.io.Externalizable.
Сериализация позволяет в определенных пределах изменять класс. Вот наиболее важные изменения, с которыми спецификация Java Object Serialization может справляться автоматически:

добавление в класс новых полей;

изменение полей из статических в нестатические;

изменение полей из транзитных в нетранзитные.

Обратные изменения (из нестатических полей в статические и из нетранзитных в транзитные) или удаление полей требуют определенной дополнительной обработки в зависимости от того, какая степень обратной совместимости необходима.
Для чего нужна сериализация?
Для компактного сохранения состояния объекта и считывание этого состояния.
Опишите процесс сериализации/десериализации с использованием
Serializable
При использовании Serializable применяется алгоритм сериализации, который с помощью рефлексии (Reflection API) выполняет:

запись в поток метаданных о классе, ассоциированном с объектом (имя класса,
идентификатор SerialVersionUID, идентификаторы полей класса);

рекурсивную запись в поток описания суперклассов до класса java.lang.Object (не включительно);

запись примитивных значений полей сериализуемого экземпляра, начиная с полей самого верхнего суперкласса;

рекурсивную запись объектов, которые являются полями сериализуемого объекта.
При этом ранее сериализованные объекты повторно не сериализуются, что позволяет алгоритму корректно работать с циклическими ссылками.
Для выполнения десериализации под объект выделяется память, после чего его поля заполняются значениями из потока. Конструктор объекта при этом не вызывается. Однако при десериализации будет вызван конструктор без параметров родительского несериализуемого класса, а его отсутствие повлечет ошибку десериализации.
Как изменить стандартное поведение сериализации/десериализации?
Реализовать интерфейс
java.io.Externalizable, который позволяет применение пользовательской логики сериализации. Способ сериализации и десериализации описывается в методах writeExternal() и readExternal(). Во время десериализации вызывается конструктор без параметров, а потом уже на созданном объекте вызывается метод readExternal.
Если у сериализуемого объекта реализован один из следующих методов, то механизм сериализации будет использовать его, а не метод по умолчанию :

writeObject() – запись объекта в поток;

readObject() – чтение объекта из потока;

writeReplace() – позволяет заменить себя экземпляром другого класса перед записью;

readResolve() – позволяет заменить на себя другой объект после чтения.
Какие поля не будут сериализованы при сериализации? Будет ли
сериализовано final-поле?
Поля с модификатором transient. В таком случае после восстановления его значение будет null.

Поля 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


написать администратору сайта