0% нашли этот документ полезным (0 голосов)
3 просмотров99 страниц

Java Basic Questions V 4.1

Документ содержит обширный список вопросов и ответов по Java, охватывающий темы объектно-ориентированного программирования (ООП), Java Virtual Machine (JVM), Java Core, коллекции, многопоточность, базы данных и тестирование. Он включает в себя основные концепции, такие как классы, объекты, интерфейсы, принципы ООП, а также детали работы с базами данных и тестированием. Документ служит справочным материалом для разработчиков, желающих углубить свои знания в Java.

Загружено:

Max
Авторское право
© All Rights Reserved
Мы серьезно относимся к защите прав на контент. Если вы подозреваете, что это ваш контент, заявите об этом здесь.
Доступные форматы
Скачать в формате PDF, TXT или читать онлайн в Scribd
0% нашли этот документ полезным (0 голосов)
3 просмотров99 страниц

Java Basic Questions V 4.1

Документ содержит обширный список вопросов и ответов по Java, охватывающий темы объектно-ориентированного программирования (ООП), Java Virtual Machine (JVM), Java Core, коллекции, многопоточность, базы данных и тестирование. Он включает в себя основные концепции, такие как классы, объекты, интерфейсы, принципы ООП, а также детали работы с базами данных и тестированием. Документ служит справочным материалом для разработчиков, желающих углубить свои знания в Java.

Загружено:

Max
Авторское право
© All Rights Reserved
Мы серьезно относимся к защите прав на контент. Если вы подозреваете, что это ваш контент, заявите об этом здесь.
Доступные форматы
Скачать в формате PDF, TXT или читать онлайн в Scribd

Java

Common
Questions

1
Table of Contents
ООП....................................................................................................................................................... 6
1. Что такое ООП?................................................................................................................................ 6
2. Принципы OOП................................................................................................................................. 6
3. Что представляет собой обмен сообщениями обьектов в ООП?..................................................7
4. Основные понятия ООП: класс, объект, интерфейс.......................................................................7
5. Что такое, в плане принципов ООП, выражения является и имеет?............................................8
6. В чем разница между композицией и агрегацией?.........................................................................8
7. Что такое статическое и динамическое связывание?....................................................................8
JVM........................................................................................................................................................ 8
8. Типы ссылок и значения................................................................................................................... 8
9. Classloader......................................................................................................................................... 9
10. Области данных времени выполнения.........................................................................................9
11. Frames.......................................................................................................................................... 10
12. Execution Engine........................................................................................................................... 12
JAVA Core............................................................................................................................................ 13
13. Чем различаются JRE, JVM и JDK?............................................................................................13
14. Что такое модификаторы доступа, какие есть модификаторы?...............................................13
15. О чем говорит ключевое слово final?..........................................................................................14
16. Какими значениями инициализируются переменные по умолчанию.......................................14
17. Что вы знаете о функции main()?................................................................................................14
18. Какие логические операции и операторы вы знаете.................................................................14
19. Какие побитовые операции вы знаете........................................................................................14
20. Абстрактные классы и методы, модификатор abstract..............................................................14
21. Интерфейс. Чем абстрактный класс отличается от интерфейса.............................................15
22. Виды блоков инициализации, порядок вызова с учётом иерархии..........................................16
23. Что произойдёт, если в блоке инициализации возникнет исключительная ситуация.............16
24. К каким конструкциям Java применим модификатор static?......................................................17
25. Может ли статический метод быть переопределён или перегружен.......................................17
26. Можно ли сузить уровень доступа, возвращаемый тип, тип аргумента, порядок throws при
переопределении метода................................................................................................................... 17
27. Можно ли объявить метод и абстрактным и статическим.........................................................18
28. Какие типы классов бывают в Java?...........................................................................................18
29. Вложенные классы, что это, в каких случаях они применяются...............................................19
30. Особенности вложенных, статических, внутренних классов.....................................................19
31. Что такое локальный класс, каковы его особенности................................................................20
32. Что такое анонимные классы, где они применяются.................................................................20
33. Получение доступа к полю внешнего класса из вложенного класса........................................20
34. Для чего используется оператор assert?....................................................................................21
35. Что такое Heap и Stack память в Java? Какая разница между ними........................................21
36. Как передаются переменные в методы, по значению или по ссылке......................................22
37. Для чего нужен сборщик мусора.................................................................................................22
38. Как работает сборщик мусора.....................................................................................................24
39. Разновидности сборщиков мусора, реализованных в HotSpot JVM.........................................25
40. Aлгоритм работы последовательного сборщика мусора в JVM...............................................25
41. Что такое Пул строк (String pool).................................................................................................27

2
42. Что такое finalize()? Зачем он нужен...........................................................................................28
43. Поведение сборщика мусора, если метод finalize() выполняется долго или в процессе
выполнения будет выброшено исключение......................................................................................28
44. Чем отличаются final, finally и finalize()?.....................................................................................29
45. Приведение типов, понижение и повышение типа....................................................................30
46. Автоупаковка, правила упаковки примитивных типов в обертки..............................................31
47. Какие есть особенности класса String?......................................................................................32
48. Какая основная разница между String, StringBuffer, StringBuilder?...........................................33
49. Что такое класс Object? Какие в нем есть методы.....................................................................33
50. Конструктор, виды конструкторов, конструкторы по умолчанию..............................................34
51. Классы-загрузчики, динамическая загрузка классов.................................................................34
52. Что такое Reflection?.................................................................................................................... 36
53. Метод equals(), свойства, зачем переопределять, ограничения..............................................36
54. Связь hashCode() и equals(). Что, если equals() переопределен..............................................37
55. Метод hashCode(), свойства, переопределение, какие есть условия......................................38
56. Можно ли реализовать equals как [Link]() == [Link]()?...........................................38
57. Разница [Link]() == [Link]() и instanceof MyClass.................................................39
58. Расскажите про клонирование объектов....................................................................................39
59. В чем отличие между поверхностным и глубоким клонированием?........................................40
60. Какой способ клонирования предпочтительней?.......................................................................40
61. Почему метод clone() объявлен в Object, а не в Cloneable?.....................................................41
62. Иерархия исключений, виды, checked, unchecked exception.....................................................42
63. Что представляют из себя ошибки класса Error?......................................................................42
64. Опишите работу блока try-catch-finally........................................................................................43
65. Существуют ли ситуации когда блок finally не будет выполнен................................................44
66. Последовательность, в которой должны идти блоки catch.......................................................44
Обобщения (Generics)........................................................................................................................ 45
67. Generics (понятие, что дают, ограничения, для чего нужен "?")...............................................45
68. Что такое интернационализация, локализация.........................................................................46
JAVA Collections.................................................................................................................................. 47
69. Коллекции. Основные интерфейсы JCF и их реализации.........................................................47
70. Что такое Fail-fast и Fail-safe поведение.....................................................................................48
71. Что такое Enumeration, Iterator И Iterable, различия, использование.......................................49
72. Различия ArrayList и LinkedList, что такое Vector.......................................................................50
73. Оцените количество памяти на один примитив byte в LinkedList..............................................52
74. Оцените количество памяти на один примитив byte в ArrayList...............................................52
JAVA 8.................................................................................................................................................. 53
75. Какие нововведения появились в Java 8 и JDK 8?....................................................................53
76. Лямбды......................................................................................................................................... 54
77. Функциональный интерфейс.......................................................................................................56
78. Лямбда.......................................................................................................................................... 57
Streams................................................................................................................................................ 58
79. Потоки: типы операций................................................................................................................. 58
80. Потоки: как составить поток........................................................................................................59
81. Потоки: примеры операций и их работа.....................................................................................59
Потоки ввода-вывода в JAVA............................................................................................................. 62
82. В чём заключается разница между IO и NIO?............................................................................62
Сериализация..................................................................................................................................... 63
83. Что такое сериализация............................................................................................................... 63
Многопоточность................................................................................................................................. 64

3
84. Модель памяти Java..................................................................................................................... 64
85. Потокобезопасность, разница конкуренции и параллелизма....................................................64
Servlets, JSP, JSTL.............................................................................................................................. 66
86. Что такое сервлет......................................................................................................................... 66
87. Преимущества сервлетов над CGI (Common Gateway Interface)...............................................66
88. Какова структура веб проекта......................................................................................................66
89. Что такое контейнер сервлетов...................................................................................................67
Базы данных........................................................................................................................................ 68
90. ACID, уровни изолированности транзакций...............................................................................68
91. Что такое БД, СУБД, реляционная модель данных...................................................................69
92. Ключи, первичный, внешний, простой, составной и т.д..............................................................70
93. Что такое нормализация............................................................................................................... 71
SQL...................................................................................................................................................... 73
94. Что такое SQL............................................................................................................................... 73
95. Временная таблица. Представление. Для чего используются.................................................73
96. Каков общий синтаксис оператора SELECT?.............................................................................74
97. Что такое JOIN?............................................................................................................................ 74
JDBC.................................................................................................................................................... 76
98. Что такое JDBC, преимущества его использования...................................................................76
99. Основные этапы работы с БД при использовании JDBC...........................................................77
100. Какие уровни изоляции транзакций поддерживаются в JDBC?...............................................78
101. При помощи чего формируются запросы к базе данных..........................................................79
102. Чем отличается Statement от PreparedStatement?...................................................................79
103. Запрос к базе данных и обработка результатов.......................................................................80
Тестирование...................................................................................................................................... 81
104. Что такое модульное тестирование (Unit testing).....................................................................81
105. Что такое интеграционное тестирование.................................................................................81
106. Какие существуют виды тестовых объектов............................................................................82
107. Чем stub отличается от mock?..................................................................................................82
Журналирование................................................................................................................................. 84
108. Какие существуют типы логов, из чего состоит система log4j.................................................84
109. Уровни журналирования, порядок их приоритетности в log4j.................................................85
110. Какие существуют способы конфигурирования log4j?.............................................................86
Patterns. Шаблоны, паттерны проектирования.................................................................................87
111. Принципы SOLID. (описать и пояснить).....................................................................................87
112. Что такое шаблоны (patterns), типы, основные характеристики..............................................87
113. Приведите примеры основных шаблонов проектирования.....................................................88
114. Приведите примеры порождающих шаблонов проектирования..............................................89
115. Приведите примеры структурных шаблонов проектирования.................................................89
116. Приведите примеры поведенческих шаблонов проектирования.............................................89
Основы WEB....................................................................................................................................... 91
117. Какие существуют уровни модели OSI?....................................................................................91
118. Что такое TCP/IP?....................................................................................................................... 91
119. Что такое UDP?........................................................................................................................... 92
120. Чем отличаются TCP и UDP?.....................................................................................................92
121. Что такое Web server?................................................................................................................ 93
122. Что такое Web application?......................................................................................................... 93
123. Что такое Application server?......................................................................................................94
124. Чем отличаются Web server и Application server?.....................................................................94
Spring................................................................................................................................................... 95

4
125. Что такое Spring Framework.......................................................................................................95
126. Какие некоторые из важных особенностей и преимуществ Spring Framework?.....................95
127. Что вы понимаете под Dependency Injection (DI)?....................................................................96
128. Как реализуется DI в Spring Framework?...................................................................................97
129. Какие преимущества использования Spring Tool Suite?..........................................................98
130. Приведите названия некоторых важных Spring модулей........................................................98

5
ООП
1. Что такое ООП?
ООП — методология программирования, основанная на представлении
программы как совокупности объектов, каждый из которых является экземпляром
класса, а классы образуют иерархию наследования.
Программа считается объектно-ориентированной, если выполнены 3 условия:
— объектно-ориентированное программирование использует в качестве
основных логических конструктивных элементов объекты, а не алгоритмы;
— каждый объект является экземпляром определенного класса
— классы образуют иерархии.
В частности, программирование, не использующее наследование, называется не
объектно-ориентированным, а программированием с помощью абстрактных
типов данных. Согласно парадигме ООП программа состоит из объектов,
обменивающихся сообщениями. Объекты могут обладать состоянием,
единственный способ изменить состояние объекта - послать ему сообщение, в
ответ на которое, объект может изменить собственное состояние.
/*************************************************************************************************************************/

2. Принципы OOП
Наследование — это свойство системы, позволяющее описать новый класс
(потомок, производный) на основе уже существующего (родительского,
суперкласса) с частично или полностью заимствующейся функциональностью.
Инкапсуляция — это свойство системы, позволяющее объединить данные и
методы в классе, и скрыть детали реализации от пользователя.
Полиморфизм — это свойство системы использовать объекты с одинаковым
интерфейсом без информации о типе и внутренней структуре объекта. Его
реализации в Java — перегрузка и переопределение методов, интерфейсы.
Абстракция - способ выделить набор значимых характеристик объекта,
исключая не значимые. Абстракция - набор всех таких характеристик.
Статический полиморфизм, или ранее связывание:
- происходит во время компиляции, решает, какой метод выполнять;
- перегрузка, Overloading метода — это пример статического полиморфизма;
- к раннему связыванию относятся private, static и терминальные методы;
- наследование не участвует в раннем связывании;
- в статическом полиморфизме участвуют не объекты, а информация о классе.
Динамический полиморфизм, или позднее связывание:
- происходит во время выполнения, решает, какая конкретно будет реализация;
- переопределение, Overriding метода — пример динамического полиморфизма;
- позднее связывание - назначение объекта, ссылки его типа или суперкласса;
- наследование связано с динамическим полиморфизмом.

6
/*************************************************************************************************************************/

3. Что представляет собой обмен сообщениями обьектов в ООП?


Объекты взаимодействуют, посылая и получая сообщения. Сообщение — это
запрос на выполнение действия, дополненный набором аргументов, которые
могут понадобиться при выполнении действия. В ООП посылка сообщения
(вызов метода) — это единственный путь передать управление объекту. Если
объект должен «отвечать» на это сообщение, то у него должна иметься соот-
ветствующий данному сообщению метод. Также объекты, используя свои
методы, могут и сами посылать сообщения другим объектам. Обмен
сообщениями реализуется с помощью динамических вызовов, что приводит к
чрезвычайно позднему связыванию (extreme late binding).
Например, физическая модель, описывающая разные сталкивающиеся шары.
Процедурный подход к решению этой задачи примерно таков: определяется
набор данных, описывающих каждый шар, ему присваивается уникальный ID
(например, массив, где индекс соответствует номеру шара). Пишется
подпрограмма с названием bounce; на основе номера шара и начальных
параметров она нужным образом изменяет данные, описывающие шар.
В отличие от процедурного подхода, ООП версия моделирует каждый из шаров
объектом. При этом объект, соответствующий конкретному шару, содержит не
только его параметры, но и весь код, описывающий поведение шара при
различных взаимодействиях. Так, каждый шар будет иметь собственный метод
bounce(). Вместо того, чтобы вызывать подпрограмму bounce с аргументом,
определяющим шар №3, необходимо будет передать объекту Шар №3
сообщение, предписывающее ему выполнить столкновение.
/*************************************************************************************************************************/

4. Основные понятия ООП: класс, объект, интерфейс.


Класс – это способ описания сущности, определяющий состояние и поведение,
зависящее от этого состояния, а также правила для взаимодействия с данной
сущностью (контракт). С точки зрения программирования класс можно
рассматривать как набор данных и методов для работы с ними.
Объект (экземпляр) – это отдельный представитель класса, имеющий
конкретное состояние и поведение, полностью определяемое классом. Каждый
объект имеет конкретные значения атрибутов.
Интерфейс – это набор всех публичных методов и публичных атрибутов класса.
Преимущества: 1.Наследование. 2.Инкапсуляция 3.Полиморфизм 4.Абстракция.
5. Объектная модель человеку естественна. 6. Повторное использование кода.
Недостатки: 1. Сложность иерархии классов 2. Неэффективность распределения
памяти на этапе выполнения по причине издержек на динамическое связывание
и проверки типов на этапе выполнения. 3. Излишняя универсальность.
Интерфейс имеет самый высокий уровень абстракции, затем идет
абстрактный класс и затем — обычный класс.

7
/*************************************************************************************************************************/

5. Что такое, в плане принципов ООП, выражения является и имеет?


Является - наследование. Имеет - ассоциация (агрегация или композиция).
/*************************************************************************************************************************/

6. В чем разница между композицией и агрегацией?


Ассоциация - связь между объектами. Композиция и агрегация - частные случаи
ассоциации часть-целое. Агрегация предполагает, что объекты связаны
взаимоотношением part-of, часть. Композиция - более строгий вариант
агрегации, дополнительно к part-of накладывается условие, что экземпляр части
может входить только в одно целое (или никуда не входить), а в случае агрегации
экземпляр части может входить в несколько целых. Например, книга состоит из
страниц, мы не можем вырвать страницу из книги и вложить в другую. Страницы
привязаны к книге, поэтому это композиция. Но мы можем перенести книгу из
одной библиотеки в другую - это уже агрегация.
/*************************************************************************************************************************/

7. Что такое статическое и динамическое связывание?


Связывание - присоединение вызова метода к телу метода. Если связывание
проводится компилятором (компоновщиком) перед запуском программы, то оно
называется статическим или ранним связыванием (early binding). В свою
очередь, позднее связывание (late binding) это связывание, проводимое
непосредственно вовремя выполнения программы, в зависимости от типа
объекта. Позднее связывание также называют динамическим (dynamic) или
связыванием на стадии выполнения (runtime binding). В языках, реализующих
позднее связывание, должен существовать механизм определения фактического
типа объекта во время работы программы, для вызова подходящего метода.
Иначе говоря, компилятор не знает тип объекта, но механизм вызова методов
определяет его и вызывает соответствующее тело метода. Для всех методов
Java используется механизм позднего (динамического) связывания, если только
метод не был объявлен как final (приватные методы являются final по
умолчанию).
/*************************************************************************************************************************/

JVM
8. Типы ссылок и значения
Существует три типа ссылочных типов: типы классов, типы массивов и типы
интерфейсов. Их значения являются ссылками на динамически создаваемые
экземпляры классов, массивы или экземпляры классов, которые реализуют
интерфейсы соответственно.

8
9. Classloader
Загрузчик классов является частью JRE, которая динамически загружает Java
классы в JVM. Обычно классы загружаются только по запросу. Система
исполнения в Java не должна знать о файлах и файловых системах благодаря
загрузчику классов. Делегирование является важной концепцией, которую
выполняет загрузчик. Загрузчик классов отвечает за поиск библиотек, чтение их
содержимого и загрузку классов, содержащихся в библиотеках. Эта загрузка
обычно выполняется по требованию, поскольку она не происходит до тех пор,
пока программа не вызовет класс.
При запуске JVM, используются три загрузчика классов: Bootstrap class loader,
Extensions class loader, System class loader. Загрузчик класса Bootstrap
загружает основные библиотеки Java, расположенные в папке
<JAVA_HOME>/jre/lib. Этот загрузчик является частью ядра JVM, написан на
нативном коде.Загрузчик класса расширений загружает код в каталоги
расширений (<JAVA_HOME>/jre/lib/ext, илилюбой другой каталог, указанный
системным свойством [Link]). Системный загрузчик загружает код,
найденный в [Link], который сопоставляется с пере-менной среды
CLASSPATH. Это реализуется классом [Link]$AppClassLoader.
Загрузчик классов выполняет три основных действия в строгом порядке:
Загрузка: находит и импортирует двоичные данные для типа.
Связывание: выполняет проверку, подготовку и (необязательно) разрешение.
Проверка: обеспечивает правильность импортируемого типа.
Подготовка: выделяет память для переменных класса и инициализация памяти
значениями по умолчанию.
Разрешение: преобразует символические ссылки из типа в прямые ссылки.
Инициализация: вызывает код Java, который инициализирует переменные
класса их правильными начальными значениями.
Пользовательский загрузчик классов. Он написан на Java. Поэтому возможно
создать свой собственный загрузчик классов, не понимая тонких деталей JVM. У
каждого загрузчика классов Java есть родительский загрузчик классов,
определенный при создании экземпляра нового загрузчика классов или в
качестве системного загрузчика классов по умолчанию для JVM. Что делает
возможным загружать или выгружать классы (например, динамически загружать
библиотеки, даже из HTTP) во время выполнения.
/*************************************************************************************************************************/

10. Области данных времени выполнения


Run-Time Data Areas. JVM выделяет множество runtime областей данных,
использующихся во время выполнения программы. Некоторые созданы JVM во
время старта и уничтожаются во время её выключения. Другие создаются для
каждого потока и уничтожаются, когда поток уничтожается.

9
The pc Register (PCR). JVM Java может поддерживать много потоков исполнения
одновременно. Каждый поток JVM имеет свой собственный регистр PC (program
counter). В любой момент каждый поток JVM выполняет код одного метода, а
именно текущий метод для этого потока. Если этот метод не является native,
регистр pc содержит адрес инструкции JVM, выполняемой в настоящее время.
Коротко говоря: один поток-один PCR, который создается при запуске потока.
PCR хранит адрес выполняемой сейчас инструкции JVM.
Java Virtual Machine Stacks. Каждый поток в JVM имеет собственный стек,
созданный одновременно с потоком. Стек в JVM хранит frames. Cтеки в JVM
могут иметь фиксированный размер или динамически расширяться и сжиматься
в соответствии с требованиями вычислений.
Heap. JVM имеет heap (кучу), которая используется всеми потоками JVM. Куча —
это область данных времени выполнения, из которой выделяется память для
всех экземпляров и массивов классов. Куча создается при запуске JVM.
Хранилище для объектов восстанавливается GC (сборщиком мусора), объекты
никогда не освобождаются явно. JVM не предполагает какого-либо конкретного
типа сборщика мусора, и метод управления данными может быть выбран в
соответствии с системными требованиями разработчика. Куча может иметь
фиксированный размер или может быть расширена и сокращена в соответствии
с требованиями вычислений. Память для кучи не должна быть смежной.
Method Area. JVM имеет область методов, которая является общей для всех
потоков. Она хранит структуры для каждого класса, такие как пул констант,
данные полей и методов, и код методов, включая конструкторы и специальные
методы, используемые при инициализации классов, экземпляров и интерфейсов.
Хотя область метода является логически частью кучи, простые реализации могут
не обрабатываться GC. Область метода может иметь фиксированный размер
или быть расширена/сокращена в соответствии с требованиями вычислений.
Run-Time Constant Pool. Он существует для каждого класса или интерфейса в
рантайме и представлен constant_pool таблицей в *.class файле. Он содержит
несколько видов констант: от числовых литералов, известных во время
компиляции, до ссылок на методы и поля, которые должны быть разрешены во
время выполнения. Этот пул выполняет функцию, аналогичную таблице
символов для обычного языка программирования, но содержит более широкий
диапазон данных, чем таблица символов. Каждый пул отделён от JVM’s method
area. JVM создаёт run-time constant pool вместе с созданием class или interface.
Native Method Stacks. JVM может использовать обычные стеки, обычно
называемые стеки Си, для поддержки native methods (методов не на Java).
/*************************************************************************************************************************/

11. Frames
Frame используется для хранения данных и частичных результатов,
выполнения динамического связывания, возврата значений для методов и
отправки исключений. Новый frame создается каждый раз, когда вызывается

10
метод. Frame уничтожается, когда завершается вызов метода, является ли это
завершение нормальным или резким (он генерирует не перехваченное
исключение). Frames выделяются из стека потока, создающего frame. Каждый
frame имеет свой собственный массив ЛП, собственный стек операндов и ссылку
на пул констант во время выполнения класса текущего метода. Размеры массива
ЛП и стека операндов определяются во время компиляции и предоставляются
вместе с кодом для метода, связанного с фреймом. Таким образом, размер
структуры данных frame зависит только от реализации JVM, и память для этих
структур может быть выделена одновременно при вызове метода.
Только один frame активен в любой точке данного потока управления -
метода выполнения, и этот frame называется текущим, а его метод известен как
текущий метод. Класс, в котором определен текущий метод, является текущим
классом. Операции над локальными переменными и стеком операндов обычно
выполняются со ссылкой на текущий frame. Frame перестает быть текущим, если
его метод вызывает другой метод или если его метод завершается. Когда метод
вызывается, новый frame создается и становится текущим, когда управление
переходит к новому методу. При возврате метода текущий frame передает
результат вызова метода, если таковой имеется, в предыдущий frame. Текущий
frame затем отбрасывается, так как предыдущий frame становится текущим.
Обратите внимание, что frame, созданный потоком, является локальным для
этого потока и на него не может ссылаться ни один другой поток.
Локальные переменные (ЛП). Каждый frame содержит массив переменных,
известных как его локальные переменные. Длина массива ЛП frame
определяется во время компиляции и предоставляется в двоичном
представлении класса или интерфейса вместе с кодом для метода, связанного с
frame-ом. Единичная ЛП может хранить значение типа: boolean, byte, char, short,
int, float, reference, or return Address. Пара ЛП может хранить значение типов: long
или double. ЛП адресуются путем индексации. Индекс первой ЛП равен нулю.
Значение типа long или double занимает две последовательные локальные
переменные. JVM использует ЛП для передачи параметров при вызове метода.
При вызове метода класса все параметры передаются в последовательных ЛП,
начиная с переменной 0. При вызове метода экземпляра ЛП 0 всегда
используется для передачи ссылки на объект, для которого вызывается метод
экземпляра (this в Java). Любые параметры затем передаются в
последовательных ЛП, начиная с ЛП 1.
Стеки операндов (Operand Stacks). Каждый frame содержит стек «последний
вошел - первый вышел» (LIFO), известный как стек операндов. Максимальная
глубина стека операндов frame-a определяется во время компиляции и
предоставляется вместе с кодом для метода, связанного с frame-ом. Стек
операнда пуст при создании frame-a, который его содержит. JVM предоставляет
инструкции для загрузки констант или значений из ЛП или полей в стек
операндов. Другие инструкции JVM берут операнды из стека операндов,

11
оперируют с ними и помещают результат обратно в стек операндов. Стек
операндов также используется для подготовки параметров для передачи в
методы и для получения результатов метода. Для примера, инструкция iadd
суммирует два int-овых значения. От стека операндов требуется, чтобы два int-
овых значения были наверху стека. Значения удаляются из стека, операция pop.
Суммируются и их сумма помещается в стек операндов.
Динамическое связывание (Dynamic Linking). Каждый frame содержит ссылку
на run-time constant pool для типа текущего метода для поддержки динамического
связывания кода метода. Доступ к вызываемым методам и переменным
осуществляется через символические ссылки из class файла. Динамическое
связывание преобразует эти символьные ссылки на методы в конкретные ссылки
на методы, загружая классы по мере необходимости для разрешения пока еще
не определенных символов, и преобразует обращения к переменным в
соответствующие смещения в структурах хранения, связанных с расположением
этих переменных во время выполнения. Позднее связывание методов и
переменных вносит изменения в другие классы, которые метод использует с
меньшей вероятностью нарушить этот код.
Нормальное завершение вызова метода. Вызов метода завершается
нормально, если этот вызов не вызывает исключение, либо непосредственно из
JVM, либо в результате выполнения явного оператора throw. Если вызов
текущего метода завершается нормально, то значение может быть возвращено
вызывающему методу. Это происходит, когда вызванный метод выполняет одну
из инструкций возврата, выбор которых должен соответствовать типу
возвращаемого значения (если оно есть).Текущий frame используется в этом
случае для восстановления состояния инициатора, включая его ЛП и стек
операндов, с соответствующим образом увеличенным программным счетчиком
инициатора, чтобы пропустить инструкцию вызова метода. Затем выполнение
обычно продолжается в frame вызывающего метода с возвращенным значением
(если есть), помещаемым в стек операндов этого frame.
Резкое завершение вызова метода. Вызов метода завершается
преждевременно, если при выполнении инструкции JVM в методе выдает
исключение, и это исключение не обрабатывается в методе. Выполнение
команды throw также приводит к явному выбрасыванию исключения, и, если
исключение не перехватывается текущим методом, приводит к неожиданному
завершению вызова метода. Вызов метода, который завершается внезапно,
никогда не возвращает значение своему вызывающему.
/*************************************************************************************************************************/

12. Execution Engine


Байт-код, назначенный run-time data areas, будет выполнен execution engine.
Механизм выполнения считывает байт-код и выполняет его по частям.

12
Interpreter. Интерпретатор интерпретирует байт-код быстро, но выполняется
медленно. Недостаток интерпретатора заключается в том, что, когда один метод
вызывается несколько раз, каждый раз требуется новая интерпретация.
JIT Compiler. Устраняет недостатки интерпретатора. Механизм выполнения
будет использовать помощь интерпретатора при преобразовании байт-кода, но
когда он находит повторный код, он использует JIT-компилятор, который
компилирует весь байт-код и изменяет его на собственный код. Этот нативный
код будет использоваться непосредственно для повторных вызовов методов,
которые улучшают производительность системы.
Intermediate Code Generator. Производит промежуточный код.
Code Optimizer. Отвечает за оптимизацию промежуточного кода из генератора.
Target Code Generator. Отвечает за генерацию машинного кода или родной код.
Профилировщик (Profiler). Специальный компонент, отвечающий за поиск
горячих точек, то есть, вызывается ли метод несколько раз или нет.
Garbage Collector — управляет памятью.
/*************************************************************************************************************************/

JAVA Core
13. Чем различаются JRE, JVM и JDK?
JVM — основная часть среды времени исполнения Java (JRE). JVM исполняет
байт-код Java, предварительно созданный из исходного текста Java-программы
компилятором Java. JVM может также использоваться для выполнения программ,
написанных на других языках программирования. JRE, Java Runtime
Environment — минимально необходимая реализация JVM для исполнения
приложений, состоит из JVM и стандартного набора библиотек классов. JDK,
Java Development Kit - JRE и набор инструментов разработчика, включающий в
себя компилятор, стандартные библиотеки классов, примеры, документацию,
утилиты. Коротко: JDK - среда для разработки, включающая в себя JRE - среду
для обеспечения запуска программ, содержащую JVM - интерпретатор кода.
14. Что такое модификаторы доступа, какие есть модификаторы?
Бывают такие модификаторы, упорядоченные в порядке повышения доступа:
1. private - для методов, полей и конструкторов. Уровень доступа — только
класс, внутри которого он объявлен.
2. package-private(default) - для классов. Доступ только в конкретном
пакете (package), в котором объявлен класс, метод, переменная, конструктор.
3. protected - такой же доступ, как и package-private + для тех классов,
которые наследуются от класса с модификатором protected.
4. public — для всех сущностей. Полноценный доступ во всем приложении.
Во время наследования возможно изменения модификаторов доступа в сторону
большей видимости (для поддержания принципа подстановки Барбары Лисков).

13
15. О чем говорит ключевое слово final?
Модификатор final может применяться к переменным, параметрам методов,
полям и методам класса или самим классам.
- Класс не может иметь наследников;
- Метод не может быть переопределен в классах наследниках;
- Поле не может изменить свое значение после инициализации;
- Параметры методов не могут изменять своё значение внутри метода;
- Локальные переменные не могут быть изменены после присвоения значения.
/*************************************************************************************************************************/

16. Какими значениями инициализируются переменные по умолчанию


Числа - 0 или 0.0, char —\u0000, boolean —false, объекты (в т.ч. String) — null.
/*************************************************************************************************************************/

17. Что вы знаете о функции main()?


Метод main - точка входа программы. В приложении может быть несколько таких
методов. Если метод отсутствует, то компиляция возможна, но при запуске будет
получена ошибка `Error: Main method not found`. public static void main(String[] args) {}
/*************************************************************************************************************************/

18. Какие логические операции и операторы вы знаете


& Логическое AND (И); && Сокращённое AND; | Логическое OR (ИЛИ);
|| Сокращённое OR; ^ Логическое XOR (исключающее OR (ИЛИ));
! Логическое унарное NOT (НЕ); &= AND с присваиванием;
|= OR с присваиванием; ^= XOR с присваиванием; == Равно;
!= Не равно; ?: Тернарный (троичный) условный оператор.
/*************************************************************************************************************************/

19. Какие побитовые операции вы знаете


~ Побитовый унарный оператор NOT; & Побитовый AND;
&= Побитовый AND с присваиванием; | Побитовый OR;
|= Побитовый OR с присваиванием; ^ Побитовое исключающее XOR;
^= Побитовое исключ. XOR с присваиванием; >> Сдвиг вправо (/ 2 в степени сдвига);
>>= Сдвиг вправо с присваиванием; >>> Сдвиг вправо без учёта знака;
>>>= Сдвиг вправо без учета знака с присв.; << Сдвиг влево (* 2 в степени сдвига);
<<= Сдвиг влево с присваиванием.
/*************************************************************************************************************************/

20. Абстрактные классы и методы, модификатор abstract.


Класс, помеченный модификатором abstract называется абстрактным классом.
Такие классы могут выступать только предками для других классов. Создавать
экземпляры самого абстрактного класса не разрешается. При этом наследниками
абстрактного класса могут быть как другие абстрактные классы, так и классы,
допускающие создание объектов. Метод помеченный ключевым словом abstract -
абстрактный метод, т.е. метод, который не имеет реализации. Если в классе
присутствует хотя бы один абстрактный метод, то весь класс должен быть
объявлен абстрактным.

14
21. Интерфейс. Чем абстрактный класс отличается от интерфейса
Interface используется для создания полностью абстрактных классов. Основное
предназначение интерфейса — определять, каким образом мы можем
использовать класс, который его реализует. Создатель интерфейса определяет
имена методов, списки аргументов и типы возвращаемых значений, но не
реализует их поведение. Все методы неявно объявляются как public. Начиная с
Java 8 в интерфейсах разрешается размещать реализацию методов по
умолчанию default и статических static методов. Интерфейс также может
содержать и поля. В этом случае они автоматически являются public static final. В
Java класс может одновременно реализовать несколько интерфейсов, но
наследоваться только от одного класса. Абстрактные классы используются
только тогда, когда присутствует тип отношений «is a» (является). Интерфейсы
могут реализоваться классами, которые не связаны друг с другом. Абстрактный
класс — средство, позволяющее избежать написания повторяющегося кода,
инструмент для частичной реализации поведения. Интерфейс — средство
выражения семантики класса, контракт, описывающий возможности. Все методы
интерфейса неявно объявляются как public abstract или (начиная с Java 8) default
— методами с реализацией по умолчанию, а поля - public static final. Интерфейсы
позволяют создавать структуры типов без иерархии. Наследуясь от абстрактного,
класс «растворяет» собственную индивидуальность. Реализуя интерфейс, он
расширяет собственную функциональность.
В каких случаях следует использовать абстрактный класс, а в каких —
интерфейс. Абстрактные классы содержат частичную реализацию, которая
дополняется или расширяется в подклассах. При этом все подклассы схожи
между собой в части реализации, унаследованной от абстрактного класса и
отличаются лишь в части собственной реализации абстрактных методов
родителя. Поэтому абстрактные классы применяются в случае построения
иерархии однотипных, очень похожих друг на друга классов. В этом случае
наследование от абстрактного класса, реализующего поведение объекта по
умолчанию может быть полезно, так как позволяет избежать написания
повторяющегося кода. Во всех остальных случаях лучше использовать
интерфейсы.
Почему в некоторых интерфейсах вообще не определяют методов. Это так
называемые маркерные интерфейсы. Они просто указывают что класс относится
к определенному типу. Примером может послужить интерфейс Clonable, который
указывает на то, что класс поддерживает механизм клонирования.
Почему нельзя объявить метод интерфейса с модификатором final? В
случае интерфейсов указание модификатора final бессмысленно, т.к. все методы
интерфейсов неявно объявляются как абстрактные, т.е. их невозможно
выполнить, не реализовав где-то еще, а этого нельзя будет сделать, если у
метода идентификатор final.

15
22. Виды блоков инициализации, порядок вызова с учётом иерархии
Блоки инициализации представляют собой код, заключенный в фигурные скобки
и размещаемый внутри класса вне объявления методов или конструкторов.
Существуют статические и нестатические блоки инициализации. Блок
инициализации выполняется перед инициализацией класса загрузчиком классов
или созданием объекта класса с помощью конструктора. Несколько блоков
инициализации выполняются в порядке следования в коде класса. Блок
инициализации способен генерировать исключения, если их объявления
перечислены в throws всех конструкторов класса. Блок инициализации возможно
создать и в анонимном классе.
Статические блоки инициализации выполняют код один раз при
инициализации класса загрузчиком классов, в момент перед созданием объектов
класса при помощи конструктора. В отличие от нестатических, принадлежащих
объекту класса, принадлежат только самому классу (объекту мета-класса Class).
1. Сначала вызываются все статические блоки от 1-го статического блока
корневого предка и выше по иерархии до статических блоков самого класса.
2. Затем вызываются нестатические блоки инициализации корневого предка,
его конструктор и далее до нестатических блоков и конструктора самого класса.
Parent static block(s) → Child static block(s) → Grandchild static block(s)
→ Parent non-static block(s) → Parent constructor →
→ Child non-static block(s) → Child constructor →
→ Grandchild non-static block(s) → Grandchild constructor
Пример:
public class MainClass {
public static void main(String args[]) { [Link](TestClass.v); new TestClass().a(); }
}
public class TestClass { public static String v = «some val» { [Link](«!!Non-static init»; }
static { [Link](«!! Static initializer»; }
public void a() { [Link](«!! a() called»; } }
Результат выполнения: !!! Static initializer Some val !!! Non-static initializer !!! a() called
/*************************************************************************************************************************/

23. Что произойдёт, если в блоке инициализации возникнет


исключительная ситуация
Для нестатических блоков инициализации, если выбрасывание исключения
прописано явным образом, требуется, чтобы объявления этих исключений были
перечислены в throws всех конструкторов класса. Иначе будет ошибка
компиляции. Для статического блока выбрасывание исключения в явном виде,
приводит к ошибке компиляции. В остальных случаях, взаимодействие с
исключениями будет проходить так же как и в любом другом месте. Класс не
будет инициализирован, если ошибка происходит в статическом блоке и объект
класса не будет создан, если ошибка возникает в нестатическом блоке.
Исключение, выбрасываемое при ошибке в блоке инициализации. Если
возникшее исключение - наследник RuntimeException:

16
- для статических блоков будет выброшено [Link];
- для нестатических блоков будет проброшено исключение-источник.
Если исключение - наследник Error, то в обоих случаях будет [Link].
Исключение: [Link] - смерть потока, для него исключения нет.
/*************************************************************************************************************************/

24. К каким конструкциям Java применим модификатор static?


- полям;
- методам;
- вложенным классам;
- членам секции import.
/*************************************************************************************************************************/

25. Может ли статический метод быть переопределён или перегружен


Перегружен - да. Всё работает точно так же, как и с обычными методами - 2
статических метода могут иметь одинаковое имя, если количество их параметров
или типов различается. Переопределён - нет. Выбор вызываемого статического
метода происходит при раннем связывании (на этапе компиляции, а не
выполнения) и выполняться всегда будет родительский метод, хотя
синтаксически переопределение статического метода - это вполне корректная
языковая конструкция. В целом, к статическим полям и методам рекомендуется
обращаться через имя класса, а не объект.
Могут ли нестатические методы перегрузить статические. Да, в итоге
получится два разных метода. Статический будет принадлежать классу и будет
доступен через его имя, а нестатический будет принадлежать конкретному
объекту и доступен через вызов метода этого объекта.
/*************************************************************************************************************************/

26. Можно ли сузить уровень доступа, возвращаемый тип, тип


аргумента, порядок throws при переопределении метода.
При переопределении метода сужать модификатор доступа нельзя, т.к. это
приведёт к нарушению принципа подстановки Барбары Лисков. Расширение
уровня доступа возможно.
Можно изменять все, что не мешает компилятору понять какой метод
родительского класса имеется в виду: изменять тип возвращаемого значения при
переопределении метода разрешено только в сторону сужения типа (вместо
родительского класса - наследника).
При изменении типа, количества, порядка следования аргументов вместо
переопределения будет происходить overloading (перегрузка) метода.
Секцию throws метода можно не указывать, но стоит помнить, что она
остаётся действительной, если уже определена у метода родительского класса.
Так же, возможно добавлять новые исключения, являющиеся наследниками от
уже объявленных или исключения RuntimeException. Порядок следования таких
элементов при переопределении значения не имеет.

17
Доступ к переопределенным методам родительского класса. С помощью
ключевого слова super можно обратиться к любому члену родительского класса -
методу или полю, если они не определены как private. [Link]();
/*************************************************************************************************************************/

27. Можно ли объявить метод и абстрактным и статическим.


Нет, в таком случае компилятор выдаст ошибку: «Illegal combination of
modifiers: ‘abstract’ and ‘static’». Модификатор abstract говорит, что метод будет
реализован в другом классе, а static наоборот указывает, что этот метод будет
доступен по имени класса.
В чем разница члена экземпляра и статического члена класса. Модификатор
static говорит о том, что метод или поле принадлежат классу и доступ к ним
возможен без создания его экземпляра. Static поля инициализируются при
инициализации класса. Static методы имеют ряд ограничений:
- Они могут вызывать только другие статические методы.
- Они должны осуществлять доступ только к статическим переменным.
- Они не могут ссылаться на члены типа this или super.
В отличие от статических, поля экземпляра класса принадлежат объекту и могут
иметь разные значения для каждого. Вызов метода экземпляра возможен только
после предварительного создания объекта класса.
Пример: public class MainClass {
public static void main(String args[]) {
[Link](TestClass.v); new TestClass().a(); [Link](TestClass.v); } }
public class TestClass {
public static String v = "initial val"; { [Link]("!! Non-static init"; v="non-static val"; }
static { [Link](«!! Static initializer»; v = "Some val"; }
public void a() { [Link]("!! a() called"; } }
Результат: !!! Static initializer Some val !!! Non-static init !!! a() called non-static val
Где разрешена инициализация статических и нестатических полей.
Статические поля можно инициализировать при объявлении, в статическом или
нестатическом блоке инициализации. Нестатические поля можно
инициализировать при объявлении, в нестатическом блоке или в конструкторе.
/*************************************************************************************************************************/

28. Какие типы классов бывают в Java?


- Top level class (Обычный класс):
- Abstract class (Абстрактный класс);
- Final class (Финализированный класс).
- Interfaces (Интерфейс).
- Enum (Перечисление).
- Nested class (Вложенный класс):
- Static nested class (Статический вложенный класс);
- Member inner class (Простой внутренний класс);
- Local inner class (Локальный класс);
- Anonymous inner class (Анонимный класс).

18
29. Вложенные классы, что это, в каких случаях они применяются
Вложенные классы (nested classes) - это классы внутри других классов:
class OuterClass { static class StaticNestedClass { } class InnerClass { } }
Вложенный класс нужен только для обслуживания обрамляющего его класса.
Если он полезен в другом контексте, он должен стать классом верхнего уровня.
Вложенные классы имеют доступ ко всем (в том числе приватным) полям и
методам внешнего класса, но не наоборот. Это несколько нарушает
инкапсуляцию. Есть 4 категории вложенных классов: ● Static nested class
(Статический вложенный класс); ● Member inner class (Простой внутренний
класс); ● Local inner class (Локальный класс); ● Anonymous inner class
(Анонимный класс). Все, кроме первого, называют внутренними (Inner).
public class Bicycle { private String model; public Bicycle(String model) { [Link] = model; }
public void start() { [Link]("Поехали!"); }
public class HandleBar { String right() { return "Вправо!"; } String left() { return "Влево!"; } } }
public class Main {
public static void main(String[] args) {
Bicycle b = new Bicycle("Peugeot"); [Link] handleBar = [Link] HandleBar();
[Link](); [Link]([Link]()); [Link]([Link]());
} } Вывод в консоль: Поехали! Влево! Вправо!
Внутренние классы ассоциируются не с внешним классом, а с экземпляром
внешнего. Каждая из категорий имеет рекомендации по своему применению.
Если вложенный класс должен быть виден за пределами одного метода или он
слишком длинный для того, чтобы его можно было удобно разместить в границах
одного метода и если каждому экземпляру такого класса необходима ссылка на
включающий его экземпляр, то используется нестатический внутренний класс. В
случае, если ссылка на обрамляющий класс не требуется - лучше сделать такой
класс статическим. Если класс необходим только внутри какого-то метода и
требуется создавать экземпляры этого класса только в этом методе, то
используется локальный класс. Если класс используется лишь в одном месте и
существует тип, характеризующий его, рекомендуется делать его анонимным.
Что такое статический класс. Это вложенный класс, объявленный с ключевым
словом static. К классам верхнего уровня модификатор static неприменим.
/*************************************************************************************************************************/

30. Особенности вложенных, статических, внутренних классов


Для создания объекта статического вложенного класса объект внешнего класса
не требуется. Из объекта статического вложенного класса нельзя обращаться к
не статическим членам обрамляющего класса напрямую, а только через ссылку
на экземпляр внешнего класса. Обычные вложенные классы не могут содержать
статических методов, блоков инициализации и классов. Статические вложенные
классы — могут. В объекте обычного вложенного класса хранится ссылка на
объект внешнего класса. Внутри статической такой ссылки нет. Доступ к
экземпляру обрамляющего класса осуществляется через указание .this после его
имени. Например: [Link].

19
31. Что такое локальный класс, каковы его особенности
Local inner class (Локальный класс) - это вложенный класс, который может
быть декларирован в любом блоке, в котором разрешается декларировать
переменные. Как и простые внутренние классы (Member inner class) локальные
классы имеют имена и могут использоваться многократно. Как и анонимные
классы, они имеют окружающий их экземпляр только в нестатическом контексте.
Локальные классы имеют следующие особенности:
- Видны только в пределах блока, в котором объявлены;
- Не могут быть объявлены как private/public/protected или static;
- Не могут иметь внутри себя статических объявлений методов и классов, но
могут иметь финальные статические поля, проинициализированные константой;
- Имеют доступ к полям и методам обрамляющего класса;
- Могут обращаться к локальным переменным и параметрам метода, если они
объявлены с модификатором final.

/*************************************************************************************************************************/

32. Что такое анонимные классы, где они применяются


Анонимный класс - вложенный локальный класс без имени, который разрешено
декларировать в любом месте обрамляющего класса, разрешающем
размещение выражений. Создание экземпляра анонимного класса происходит
одновременно с его объявлением. В зависимости от местоположения анонимный
класс ведет себя как статический либо как нестатический вложенный класс - в
нестатическом контексте появляется окружающий его экземпляр.
Анонимные классы имеют несколько ограничений:
- Их можно использовать только в одном месте программы - месте его создания;
- Применяются только если после создания обьекта не нужно на него ссылаться;
- Реализует лишь методы своего интерфейса или суперкласса, не может
объявлять новых методов, так как для доступа к ним нет поименованного типа.
Анонимные классы обычно применяются для:
- создания объекта функции (function object), например, реализация Comparator;
- создания объекта процесса (process object), например, Thread, Runnable и др.;
- в статическом методе генерации;
- инициализации открытого статического поля final, которое соответствует
сложному перечислению типов, когда для каждого экземпляра в перечислении
требуется отдельный подкласс.

/*************************************************************************************************************************/

33. Получение доступа к полю внешнего класса из вложенного класса


Статический вложенный класс имеет прямой доступ только к статическим полям
обрамляющего класса. Простой внутренний класс, может обратиться к любому

20
полю внешнего класса напрямую. В случае, если у вложенного класса уже
существует поле с таким же литералом, то обращаться к такому полю следует
через ссылку на его экземляр. Например: [Link].

/*************************************************************************************************************************/

34. Для чего используется оператор assert?


Assert (Утверждение) — это специальная конструкция, позволяющая проверять
предположения о значениях произвольных данных в произвольном месте
программы. Утверждение может автоматически сигнализировать об обнаружении
некорректных данных, что обычно приводит к аварийному завершению
программы с указанием места обнаружения некорректных данных.
Утверждения существенно упрощают локализацию ошибок в коде. Даже
проверка результатов выполнения очевидного кода может оказаться полезной
при последующем рефакторинге, после которого код может стать не настолько
очевидным и в него может закрасться ошибка.
Обычно утверждения оставляют включенными во время разработки и
тестирования программ, но отключают в релиз-версиях программ.
Т.к. утверждения могут быть удалены на этапе компиляции либо во время
исполнения программы, они не должны менять поведение программы. Если в
результате удаления утверждения поведение программы может измениться, то
это явный признак неправильного использования assert. Таким образом, внутри
assert нельзя вызывать методы, изменяющие состояние программы, либо
внешнего окружения программы.
В Java проверка утверждений реализована с помощью оператора assert, который
имеет форму assert [Выражение типа boolean] или assert [Выражение типа
boolean] : [Выражение любого типа, кроме void].
Во время выполнения программы в том случае, если поверка утверждений
включена, вычисляется значение булевского выражения, и если его результат
false, то генерируется исключение [Link]. В случае
использования второй формы оператора assert выражение после двоеточия
задаёт детальное сообщение о произошедшей ошибке (вычисленное выражение
будет преобразовано в строку и передано конструктору AssertionError).

/*************************************************************************************************************************/

35. Что такое Heap и Stack память в Java? Какая разница между ними.
Heap (куча) используется Java Runtime для выделения памяти под объекты и
классы. Создание нового объекта также происходит в куче. Это же является
областью работы сборщика мусора. Любой объект, созданный в куче, имеет
глобальный доступ и на него могут ссылаться из любой части приложения.
Stack (стек) это область хранения данных также находящееся в общей
оперативной памяти (RAM). Всякий раз, когда вызывается метод, в памяти стека

21
создается новый блок, который содержит примитивы и ссылки на другие объекты
в методе. Как только метод заканчивает работу, блок также перестает
использоваться, тем самым предоставляя доступ для следующего метода.
Размер стековой памяти намного меньше объема памяти в куче. Стек в Java
работает по схеме LIFO (Последний-зашел-Первый-вышел)
Различия между Heap и Stack памятью: Куча используется всеми частями
приложения в то время как стек используется только одним потоком исполнения
программы. Всякий раз, когда создается объект, он всегда хранится в куче, а в
памяти стека содержится лишь ссылка на него. Память стека содержит только
локальные переменные примитивных типов и ссылки на объекты в куче. Объекты
в куче доступны из любой точки программы, в то время как стековая память не
может быть доступна для других потоков. Стековая память существует лишь
какое-то время работы программы, а память в куче живет с самого начала до
конца работы программы. Если память стека полностью занята, то Java Runtime
бросает исключение [Link]. Если заполнена память кучи, то
бросается исключение [Link]: Java Heap Space.
Размер памяти стека намного меньше памяти в куче. Из-за простоты
распределения памяти, стековая память работает намного быстрее кучи. Для
определения начального и максимального размера памяти в куче используются -
Xms и -Xmx опции JVM. Для стека определить размер памяти можно с помощью
опции -Xss.
Верно ли утверждение, что примитивные типы данных всегда хранятся в
стеке, а экземпляры ссылочных типов данных — в куче. Не совсем.
Примитивное поле экземпляра класса хранится не в стеке, а в куче. Любой
объект (всё, что явно или неявно создаётся при помощи оператора new) хранится
в куче.

/*************************************************************************************************************************/

36. Как передаются переменные в методы, по значению или по ссылке


В Java параметры всегда передаются только по значению, что определяется как
«скопировать значение и передать копию». С примитивами это будет копия
содержимого. Со ссылками - тоже копия содержимого, т.е. копия ссылки. При
этом внутренние члены ссылочных типов через такую копию изменить возможно,
а вот саму ссылку, указывающую на экземпляр — нет.

/*************************************************************************************************************************/

37. Для чего нужен сборщик мусора

Сборщик мусора (Garbage Collector) должен делать всего две вещи:


1. Находить мусор - неиспользуемые объекты. (Объект считается
неиспользуемым, если ни одна из сущностей в коде, выполняемом в данный

22
момент, не содержит ссылок на него, либо цепочка ссылок, которая могла бы
связать объект с некоторой сущностью приложения, обрывается);
2. Освобождать память от мусора.
Существует 2 подхода к обнаружению мусора:1. Reference counting. 2. Tracing.
Reference counting (подсчёт ссылок). Суть этого подхода состоит в том, что
каждый объект имеет счетчик. Счетчик хранит информацию о том, сколько
ссылок указывает на объект. Когда ссылка уничтожается, счетчик уменьшается.
Если значение счетчика равно нулю, - объект можно считать мусором. Главным
минусом такого подхода является сложность обеспечения точности счетчика.
Также при таком подходе сложно выявлять циклические зависимости (когда два
объекта указывают друг на друга, но ни один живой объект на них не ссылается),
что приводит к утечкам памяти.
Tracing (трассировка) - главная идея подхода состоит в утверждении, что
живыми могут считаться только те объекты, до которых мы можем добраться из
корневых точек (GC Root) и те объекты, которые доступны с живого объекта. Всё
остальное — мусор.
Существует 4 типа корневых точки:
- Локальные переменные и параметры методов;
- Потоки;
- Статические переменные;
- Ссылки из JNI.
Самое простое Java приложение будет иметь корневые точки:
- Локальные переменные внутри main() метода и параметры main() метода;
- Поток который выполняет main();
- Статические переменные класса, внутри которого находится main() метод.
Таким образом, если мы представим все объекты и ссылки между ними как
дерево, то нам нужно будет пройти от корневых узлов (точек) по всем рёбрам.
При этом узлы, до которых мы сможем добраться - не мусор, все остальные -
мусор. При таком подходе циклические зависимости легко выявляются. HotSpot
VM использует именно такой подход — Tracing.
Для очистки памяти от мусора существуют два основных метода:
- Copying collectors;
- Mark-and-sweep.
Copying collectors — при этом подходе память делится на две части ≪from-
space≫ и ≪to-space≫, при этом сам принцип работы такой:
1. Объекты создаются в ≪from-space≫;
2. Когда ≪from-space≫ заполняется, приложение приостанавливается;
3. Запускается сборщик мусора. Находятся живые объекты в ≪from-space≫ и
копируются в ≪to-space≫;
4. Когда все объекты скопированы ≪from-space≫ полностью очищается;
5. ≪to-space≫ и ≪from-space≫ меняются местами.

23
Главный плюс такого подхода в том, что объекты плотно забивают память.
Минусы подхода: 1. Приложение должно быть остановлено на время,
необходимое для полного прохождения цикла сборки мусора. 2. В худшем случае
(когда все объекты живые) ≪from-space≫ и ≪to-space≫ будут обязаны быть
одинакового размера.
Mark-and-sweep — алгоритм его работы можно описать так:
1. Объекты создаются в памяти;
2. В момент, когда нужно запустить сборщик мусора, приложение
приостанавливается;
3. Сборщик проходится по дереву объектов, помечая живые объекты;
4. Сборщик проходится по всей памяти, находя все не отмеченные куски памяти
и сохраняя их в ≪free list≫;
5. Когда новые объекты начинают создаваться они создаются в памяти,
доступной во ≪free list≫.
Минусы этого способа:
- Приложение не работает, пока происходит сборка мусора;
- Время остановки напрямую зависит от размеров памяти и количества объектов;
- Если не использовать compacting, память будет использоваться неэффективно.
Сборщики мусора HotSpot VM используют комбинированный подход
Generational Garbage Collection, который позволяет использовать разные
алгоритмы для разных этапов сборки мусора. Этот подход опирается на том, что:
- большинство создаваемых объектов быстро становятся мусором;
- существует мало связей между объектами, которые были созданы в прошлом и
только что созданными объектами.

/*************************************************************************************************************************/

38. Как работает сборщик мусора


Механизм сборки мусора - это процесс освобождения места в куче, для
возможности добавления новых объектов. Объекты создаются посредством
оператора new, тем самым присваивая объекту ссылку. Для окончания работы с
объектом достаточно просто перестать на него ссылаться, например, присвоив
переменной ссылку на другой объект или значение null; прекратить выполнение
метода, чтобы его локальные переменные завершили свое существование
естественным образом. Объекты, ссылки на которые отсутствуют, принято
называть мусором (garbage), который будет удален. JVM Java, применяя
механизм сборки мусора, гарантирует, что любой объект, обладающий ссылками,
остается в памяти —все объекты, которые недостижимы из исполняемого кода,
ввиду отсутствия ссылок на них, удаляются с высвобождением отведенной для
них памяти.
Точнее говоря, объект не попадает в сферу действия процесса сборки мусора,
если он достижим посредством цепочки ссылок, начиная с корневой (GC Root)

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

/*************************************************************************************************************************/

39. Разновидности сборщиков мусора, реализованных в HotSpot JVM.


Java HotSpot VM предоставляет разработчикам на выбор четыре различных
сборщика мусора:
- Serial (последовательный) — самый простой вариант для приложений с
небольшим объемом данных и не требовательных к задержкам. На данный
момент используется сравнительно редко, но на слабых компьютерах может
быть выбран виртуальной машиной в качестве сборщика по умолчанию.
Использование Serial GC включается опцией -XX:+UseSerialGC.
- Parallel (параллельный) — наследует подходы к сборке от последовательного
сборщика, но добавляет параллелизм в некоторые операции, а также
возможности по автоматической подстройке под требуемые параметры
производительности. Параллельный сборщик включается опцией -XX:
+UseParallelGC.
- Concurrent Mark Sweep (CMS) — нацелен на снижение максимальных
задержек путем выполнения части работ по сборке мусора параллельно с
основными потоками приложения. Подходит для работы с относительно
большими объемами данных в памяти. Использование CMS GC включается
опцией -XX:+UseConcMarkSweepGC.
- Garbage-First (G1) — создан для замены CMS, особенно в серверных
приложениях, работающих на многопроцессорных серверах и оперирующих
большими объемами данных. G1 включается опцией Java -XX:+UseG1GC.

/*************************************************************************************************************************/

40. Aлгоритм работы последовательного сборщика мусора в JVM.


Serial Garbage Collector (Последовательный сборщик мусора) был одним из
первых сборщиков мусора в HotSpot VM. Во время работы этого сборщика

25
приложения приостанавливается и продолжает работать только после
прекращения сборки мусора.
Память приложения делится на три пространства:
- Young generation. Объекты создаются именно в этом участке памяти.
- Old generation. В этот участок памяти перемещаются объекты, которые
переживают ≪minor garbage collection≫.
- Permanent generation. Тут хранятся метаданные об объектах, CDS (Class data
sharing), пул строк (String pool). Permanent область делится на две: только для
чтения и для чтения-записи. Очевидно, что в этом случае область только для
чтения не чистится сборщиком мусора никогда.
Область памяти Young generation состоит из трёх областей: Eden и двух
меньших по размеру Survivor spaces - To space и From space. Большинство
объектов создаются в области Eden, за исключением очень больших объектов,
которые не могут быть размещены в ней и поэтому сразу размещаются в Old
generation. В Survivor spaces перемещаются объекты, которые пережили по
крайней мере одну сборку мусора, но ещё не достигли порога ≪старости ≫
(tenuring threshold), чтобы быть перемещенными в Old generation.
Когда Young generation заполняется, то в этой области запускается процесс
лёгкой сборки (minor collection), в отличие от процесса сборки, проводимого над
всей кучей (full collection). Он происходит следующим образом: в начале работы
одно из Survivor spaces - To space, является пустым, а другое - From space,
содержит объекты, пережившие предыдущие сборки. Сборщик мусора ищет
живые объекты в Eden и копирует их в To space, а затем копирует туда же и
живые ≪молодые≫ (то есть не пережившие еще заданное число сборок мусора)
объекты из From space. Старые объекты из From space перемещаются в Old
generation. После лёгкой сборки From space и To space меняются ролями,
область Eden становится пустой, а число объектов в Old generation
увеличивается.
Если в процессе копирования живых объектов To space переполняется, то
оставшиеся живые объекты из Eden и From space, которым не хватило места в
To space, будут перемещены в Old generation, независимо от того, сколько сборок
мусора они пережили. Поскольку при использовании этого алгоритма сборщик
мусора просто копирует все живые объекты из одной области памяти в другую,
то такой сборщик мусора называется copying (копирующий). Очевидно, что для
работы копирующего сборщика мусора у приложения всегда должна быть
свободная область памяти, в которую будут копироваться живые объекты, и
такой алгоритм может применяться для областей памяти сравнительно
небольших по отношению к общему размеру памяти приложения. Young
generation как раз удовлетворяет этому условию (по умолчанию на машинах
клиентского типа эта область занимает около 10% кучи (значение может
варьироваться в зависимости от платформы)). Однако, для сборки мусора в Old

26
generation, занимающем большую часть всей памяти, используется другой
алгоритм.
В Old generation сборка мусора происходит с использованием алгоритма
mark-sweep-compact, который состоит из трёх фаз. В фазе Mark (пометка)
сборщик мусора помечает все живые объекты, затем, в фазе Sweep (очистка) все
не помеченные объекты удаляются, а в фазе Сompact (уплотнение) все живые
объекты перемещаются в начало Old generation, в результате чего свободная
память после очистки представляет собой непрерывную область. Фаза
уплотнения выполняется для того, чтобы избежать фрагментации и упростить
процесс выделения памяти в Old generation.
Когда свободная память представляет собой непрерывную область, то для
выделения памяти под создаваемый объект можно использовать очень быстрый
(около десятка машинных инструкций) алгоритм bump-the-pointer: адрес начала
свободной памяти хранится в специальном указателе, и когда поступает запрос
на создание нового объекта, код проверяет, что для нового объекта достаточно
места, и, если это так, то просто увеличивает указатель на размер объекта.
Последовательный сборщик мусора отлично подходит для большинства
приложений, использующих до 200 мегабайт кучи, работающих на машинах
клиентского типа и не предъявляющих жёстких требований к величине пауз,
затрачиваемых на сборку мусора. В то же время модель ≪stop-the-world≫
может вызвать длительные паузы в работе приложения при использовании
больших объёмов памяти. Кроме того, последовательный алгоритм работы не
позволяет оптимально использовать вычислительные ресурсы компьютера, и
последовательный сборщик мусора может стать узким местом при работе
приложения на многопроцессорных машинах.

/*************************************************************************************************************************/

41. Что такое Пул строк (String pool).


Пул строк – это набор строк, хранящийся в Heap. Пул строк возможен благодаря
неизменяемости строк в Java и реализации идеи интернирования строк. Пул
строк помогает экономить память, но по этой же причине создание строки
занимает больше времени. Когда для создания строки используются «», то
сначала ищется строка в пуле с таким же значением, если находится, то просто
возвращается ссылка, иначе создается новая строка в пуле, а затем
возвращается ссылка на неё. При использовании оператора new создаётся
новый объект String. Затем при помощи метода intern() эту строку можно
поместить в пул или же получить из пула ссылку на другой объект String с таким
же значением. Пул строк является примером паттерна ≪Приспособленец≫
(Flyweight).

/*************************************************************************************************************************/

27
42. Что такое finalize()? Зачем он нужен
Через вызов метода finalize() (который наследуется от [Link]) JVM
реализуется функциональность аналогичная функциональности деструкторов в
С++, используемых для очистки памяти перед возвращением управления
операционной системе. Данный метод вызывается при уничтожении объекта
сборщиком мусора (garbage collector) и переопределяя finalize() можно
запрограммировать действия необходимые для корректного удаления
экземпляра класса - например, закрытие сетевых соединений, соединений с
базой данных, снятие блокировок на файлы и т. д.
После выполнения этого метода объект должен быть повторно собран
сборщиком мусора (и это считается серьезной проблемой метода finalize() т.к. он
мешает сборщику мусора освобождать память). Вызов этого метода не
гарантируется, т.к. приложение может быть завершено до того, как будет
запущена сборка мусора.
Объект не обязательно будет доступен для сборки сразу же - метод finalize()
может сохранить куда-нибудь ссылку на объект. Подобная ситуация называется
≪возрождением≫ объекта и считается антипаттерном. Главная проблема
такого трюка - в том, что ≪возродить≫ объект можно только 1 раз. Пример:
public class MainClass { public static void main(String args[]) {
TestClass a=new TestClass(); a.a(); a=null; a=new TestClass(); a.a(); [Link](«!! done»); } }
public class TestClass { public void a() { [Link](«!! a() called»; }
@Override
protected void finalize() throws Throwable { [Link](«!!finalize called»; [Link](); } }

Так как в данном случае сборщик мусора может и не быть вызван (в силу
простоты приложения), то результат выполнения программы с большой
вероятностью будет следующий: !! a() called !! a() called !! done
Теперь несколько усложним программу, добавив принудительный вызов GC:
public class MainClass { public static void main(String args[]) {
Test a=new Test(); a.a(); a=null; [Link](); a=new Test(); a.a(); [Link](«!! done»); } }

Как и было сказано ранее, Garbage Collector может отработать в разное время,
поэтому результат выполнения может различаться от запуска к запуску:
Вариант 1: !! a() called !! a() called !! done !! finalize() called
Вариант 2: !! a() called !! a() called !! finalize() called !! done

/*************************************************************************************************************************/

43. Поведение сборщика мусора, если метод finalize() выполняется


долго или в процессе выполнения будет выброшено исключение

Непосредственно вызов finalize() происходит в отдельном потоке Finalizer


([Link]. FinalizerThread), который создаётся при запуске JVM (в

28
статической секции при загрузке класса Finalizer). Методы finalize() вызываются
последовательно в том порядке, в котором были добавлены в список сборщиком
мусора. Соответственно, если какой-то finalize() зависнет, он подвесит поток
Finalizer, но не сборщик мусора. Это в частности означает, что объекты, не
имеющие метода finalize(), будут исправно удаляться, а вот имеющие будут
добавляться в очередь, пока поток Finalizer не освободится, не завершится
приложение или не кончится память. То же самое применимо и выброшенным в
процессе finalize() исключениям: метод runFinalizer() у потока Finalizer игнорирует
все исключения выброшенные в момент выполнения finalize(). Таким образом
возникновение исключительной ситуации никак не скажется на
работоспособности сборщика мусора.

/*************************************************************************************************************************/

44. Чем отличаются final, finally и finalize()?


Модификатор final:
- Класс не может иметь наследников;
- Метод не может быть переопределен в классах наследниках;
- Поле не может изменить свое значение после инициализации;
- Локальные переменные не могут быть изменены после присвоения значения;
- Параметры методов не могут изменять своё значение внутри метода.
Оператор finally гарантирует, что определенный в нём участок кода будет
выполнен независимо от того, какие исключения были возбуждены и
перехвачены в блоке try-catch.
Метод finalize() вызывается перед тем как сборщик мусора будет проводить
удаление объекта. Пример:

public class MainClass {


public static void main(String args[]) {
Test a = new Test(); [Link](«result of a.a() is+ a.a()); a = null; [Link]();
a = new Test(); [Link](«result of a.a() is» + a.a()); [Link](«!! done»; } }

public class Test { public int a() {


try { [Link](«!!a()called»; throw new Exception(«»); }
catch (Exception e) { [Link](«!! Exception in a()»; return 2; }
finally { [Link](«!! finally in a() «; } }
@Override
protected void finalize() throws Throwable { [Link](«!!finalize()»; [Link](); } }

Результат: !! a() called !! Exception in a() !! finally in a() result of a.a() is 2!! a() called !!
Exception in a() !! finally in a() !! finalize() result of a.a() is 2 !! done

/*************************************************************************************************************************/

29
45. Приведение типов, понижение и повышение типа
Java является строго типизированным языком программирования, а это
означает, то что каждое выражение и каждая переменная имеет строго
определенный тип уже на момент компиляции. Однако
определен механизм приведения типов (casting) - способ преобразования
значения переменной одного типа в значение другого типа. В Java существуют
несколько разновидностей приведения:
Тождественное (identity). Преобразование выражения любого типа к точно
такому же типу всегда допустимо и происходит автоматически.
Расширение (повышение, upcasting) примитивного типа (widening primitive).
Означает, что осуществляется переход от менее емкого типа к более ёмкому.
Например, от типа byte (длина 1 байт) к типу int (длина 4 байта). Такие
преобразование безопасны в том смысле, что новый тип всегда
гарантировано вмещает в себя все данные, которые хранились в старом типе и
таким образом не происходит потери данных. Этот тип приведения всегда
допустим и происходит автоматически.
Сужение (понижение, downcasting) примитивного типа (narrowing primitive).
Означает, что переход осуществляется от более емкого типа к менее емкому.
При таком преобразовании есть риск потерять данные. Например, если число
типа int было больше 127, то при приведении его к byte значения битов старше
восьмого будут потеряны. В Java такое преобразование должно совершаться
явным образом, при этом все старшие биты, не умещающиеся в новом типе,
просто отбрасываются - никакого округления или других действий для получения
более корректного результата не производится.
Расширение объектного типа (widening reference). Означает неявное
восходящее приведение типов или переход от более конкретного типа к менее
конкретному, т.е. переход от потомка к предку. Разрешено всегда и происходит
автоматически.
Сужение объектного типа (narrowing reference). Означает нисходящее
приведение, то есть приведение от предка к потомку (подтипу). Возможно только
если исходная переменная является подтипом приводимого типа. При
несоответствии типов в момент выполнения выбрасывается исключение
ClassCastException. Требует явного указания типа.
Преобразование к строке (to String). Любой тип может быть приведен к строке,
т.е. к экземпляру класса String.
Запрещенные преобразования (forbidden). Не все приведения между
произвольными типами допустимы. Например, к запрещенным преобразованиям
относятся приведения от любого ссылочного типа к примитивному и наоборот
(кроме преобразования к строке). Кроме того, невозможно привести друг к другу
классы, находящиеся на разных ветвях дерева наследования и т.п. При
приведении ссылочных типов с самим объектом ничего не происходит, -
меняется лишь тип ссылки, через которую происходит обращение к объекту.

30
Для проверки возможности приведения нужно воспользоваться оператором
instanceof: Parent parent = new Child(); if (parent instanceof Child) { Child child = (Child) parent; }
Когда в приложении может произойти исключение ClassCastException?
ClassCastException (потомок RuntimeException) - исключение, которое будет
выброшено при ошибке приведения типа.

/*************************************************************************************************************************/

46. Автоупаковка, правила упаковки примитивных типов в обертки


Автоупаковка (autoboxing) - это механизм неявной инициализации объектов
классов-оберток (Byte, Short, Integer, Long, Float, Double, Character, Boolean)
значениями соответствующих им исходных примитивных типов (byte, short, int...),
без явного использования конструктора класса. Автоупаковка происходит при
прямом присваивании примитива классу-обертке (с помощью оператора =), либо
при передаче примитива в параметры метода (типа класса-обертки).
Автоупаковке в классы-обертки могут быть подвергнуты как переменные
примитивных типов, так и константы времени компиляции (литералы и final-
примитивы). При этом литералы должны быть синтаксически корректными для
инициализации переменной исходного примитивного типа.
Автоупаковка переменных примитивных типов требует точного соответствия типа
исходного примитива типу класса-обертки. Например, попытка упаковать
переменную типа byte в Short, без предварительного явного приведения byte в
short вызовет ошибку компиляции.
Автоупаковка констант примитивных типов допускает более широкие границы
соответствия. В этом случае компилятор способен предварительно осуществлять
неявное расширение/сужение типа примитивов: неявное расширение/сужение
исходного типа примитива до типа примитива соответствующего классу-обертке
(для преобразования int в Byte, сначала компилятор самостоятельно неявно
сужает int к byte) автоупаковку примитива в соответствующий класс-обертку.
Однако, в этом случае существуют два дополнительных ограничения: a)
присвоение примитива обертке может производится только оператором = (нельзя
передать такой примитив в параметры метода без явного приведения типов) b)
тип левого операнда не должен быть старше чем Character, тип правого не дожен
старше, чем int: допустимо расширение/сужение byte в/из short, byte в/из char,
short в/из char и только сужение byte из int, short из int, char из int. Все остальные
варианты требуют явного приведения типов).
Дополнительной особенностью целочисленных классов-оберток, созданных
автоупаковкой констант в диапазоне -128...+127 является то, что они кэшируются
JVM. Поэтому такие обертки с одинаковыми значениями будут являться
ссылками на один объект.

/*************************************************************************************************************************/

31
47. Какие есть особенности класса String?
Это неизменяемый (immutable) и финализированный тип данных;
Все объекты класса String JVM хранит в пуле строк;
Объект класса String можно получить, используя двойные кавычки;
Можно использовать оператор + для конкатенации строк;
Начиная с Java 7, строки можно использовать в конструкции switch.

Почему String - неизменяемый и финализированный класс. Есть


несколько преимуществ в неизменности строк: Пул строк возможен только
потому, что строка неизменяемая, таким образом виртуальная машина сохраняет
больше свободного места в Heap, поскольку разные строковые переменные
указывают на одну и ту же переменную в пуле. Если бы строка была изменяемой,
то интернирование строк не было бы возможным, потому что изменение
значения одной переменной отразилось бы также и на остальных переменных,
ссылающихся на эту строку.
Если строка будет изменяемой, тогда это станет серьезной угрозой
безопасности приложения. Например, имя пользователя базы данных и пароль
передаются строкой для получения соединения с базой данных и в
программировании сокетов реквизиты хоста и порта передаются строкой. Так как
строка неизменяемая, её значение не может быть изменено, в противном случае
злоумышленник может изменить значение ссылки и вызвать проблемы в
безопасности приложения. Неизменяемость позволяет избежать синхронизации:
строки безопасны для многопоточности и один экземпляр строки может быть
совместно использован различными потоками.
Строки используются Classloader и неизменность обеспечивает правильность
загрузки класса. Поскольку строка неизменяемая, её hashCode() кэшируется в
момент создания и не нужно рассчитывать его снова. Это делает строку
отличным кандидатом для ключа в HashMap, его обработка происходит быстрее.
Почему char[] предпочтительнее String для хранения пароля. С момента
создания строка остаётся в пуле, до тех пор, пока не будет удалена сборщиком
мусора. Поэтому, даже после окончания использования пароля, она некоторое
время продолжает оставаться доступным в памяти и способа избежать этого не
существует. Это представляет определённый риск безопасности, поскольку кто-
либо, имеющий доступ к памяти сможет найти пароль в виде текста. В случае
использования массива символов для хранения пароля имеется возможность
очистить его сразу по окончанию работы с паролем, позволяя избежать риска
безопасности, свойственного строке.
Почему строка является популярным ключом в HashMap в Java?
Поскольку строки неизменяемы, их хэш код вычисляется и кэшируется в момент
создания, не требуя повторного пересчета при дальнейшем использовании.
Поэтому в качестве ключа HashMap они будут обрабатываться быстрее.

32
Что делает метод intern() в классе String? Он нужен для сохранения строки в
пуле строк или получения ссылки, если такая строка уже находится в пуле.
Можно ли использовать строки в конструкции switch? Да, начиная с Java 7 в
операторе switch можно использовать строки, ранние версии Java не
поддерживают этого. При этом: участвующие строки чувствительны к регистру;
используется метод equals() для сравнения полученного значения со значениями
case, поэтому во избежание NullPointerException стоит предусмотреть проверку
на null. согласно документации, Java 7 для строк в switch, компилятор Java
формирует более эффективный байткод для строк в конструкции switch, чем для
сцепленных условий if-else.

/*************************************************************************************************************************/

48. Какая основная разница между String, StringBuffer, StringBuilder?


Класс String является неизменяемым (immutable) - модифицировать объект
такого класса нельзя, можно лишь заменить его созданием нового экземпляра.
Класс StringBuffer изменяемый - использовать StringBuffer следует тогда, когда
необходимо часто модифицировать содержимое.
Класс StringBuilder был добавлен в Java 5 и он во всем идентичен классу
StringBuffer за исключением того, что он не синхронизирован и поэтому его
методы выполняются значительно быстрей.

/*************************************************************************************************************************/

49. Что такое класс Object? Какие в нем есть методы.


Object — это базовый класс для всех остальных объектов в Java. Любой класс
наследуется от Object и, соответственно, наследует его методы:
public boolean equals(Object obj) – сравнивает объекты по значению;
int hashCode() – возвращает hash код для объекта;
String toString() – возвращает строковое представление объекта;
Class getClass() – возвращает класс объекта во время выполнения;
protected Object clone() – создает и возвращает копию объекта;
void notify() – возобновляет поток, ожидающий монитор; void
notifyAll() – возобновляет все потоки, ожидающие монитор;
void wait() – остановка вызвавшего метод потока до момента пока другой поток
не вызовет метод notify() или notifyAll() для этого объекта;
void wait(long timeout) – остановка вызвавшего метод потока на определённое
время или пока другой поток не вызовет notify() или notifyAll() для этого объекта;
void wait(long timeout, int nanos) – остановка вызвавшего метод потока на
определённое время или пока другой поток не вызовет метод notify() или
notifyAll() для этого объекта;
protected void finalize() –может вызываться сборщиком мусора в момент
удаления объекта при сборке мусора.

33
/*************************************************************************************************************************/

50. Конструктор, виды конструкторов, конструкторы по умолчанию


Конструктор — это специальный метод, у которого отсутствует возвращаемый
тип и который имеет то же имя, что и класс, в котором он используется.
Конструктор вызывается при создании нового объекта класса и определяет
действия необходимые для его инициализации.
Если у какого-либо класса не определить конструктор, то компилятор сгенерирует
конструктор без аргументов - так называемый ≪конструктор по умолчанию ≫,
public class ClassName() {}
Если у класса уже определен какой-либо конструктор, то конструктор по
умолчанию создан не будет и, если он необходим, его нужно описывать явно.
Чем отличаются конструктор по умолчанию, конструктор
копирования, конструктор с параметрами. У конструктора по умолчанию
отсутствуют какие-либо аргументы. Конструктор копирования принимает в
качестве аргумента уже существующий объект класса для последующего
создания его клона. Конструктор с параметрами имеет в своей сигнатуре
аргументы (обычно необходимые для инициализации полей класса).
Где и как вы можете использовать приватный конструктор. Приватный
(помеченный ключевым словом private, скрытый) конструктор может
использоваться публичным статическим методом генерации объектов данного
класса. Также доступ к нему разрешён вложенным классам и может
использоваться для их нужд.

/*************************************************************************************************************************/

51. Классы-загрузчики, динамическая загрузка классов


Основа работы с классами в Java — классы-загрузчики, обычные Java-объекты,
предоставляющие интерфейс для поиска и создания объекта класса по его
имени во время работы приложения. В начале работы программы создается 3
основных загрузчика классов:
- Базовый загрузчик (Bootstrap/primordial). Загружает основные системные и
внутренние классы JDK (Core API - пакеты java.* ([Link] и [Link]) . Важно
заметить, что базовый загрузчик является ≪Изначальным≫ или ≪Корневым ≫
и частью JVM, вследствие чего его нельзя создать внутри кода программы.
- Загрузчик расширений (Extension). Загружает различные пакеты расширений,
которые располагаются в директории <JAVA_HOME>/lib/ext или другой
директории, описанной в системном параметре [Link]. Это позволяет
обновлять и добавлять новые расширения без необходимости модифицировать
настройки используемых приложений. Загрузчик расширений реализован
классом [Link]$ExtClassLoader.

34
- Системный загрузчик (System/application). Загружает классы, пути к которым
указаны в переменной окружения CLASSPATH или пути, которые указаны в
командной строке запуска JVM после ключей -classpath или -cp. Системный
загрузчик реализован классом [Link]. Launcher$AppClassLoader.
Загрузчики классов являются иерархическими: каждый из них (кроме базового)
имеет родительский загрузчик и в большинстве случаев, перед тем как
попробовать загрузить класс самостоятельно, он посылает вначале запрос
родительскому загрузчику загрузить указанный класс. Такое делегирование
позволяет загружать классы тем загрузчиком, который находится ближе всего к
базовому в иерархии делегирования. Как следствие поиск классов будет
происходить в источниках в порядке их доверия: сначала в библиотеке Core API,
потом в папке расширений, потом в локальных файлах CLASSPATH.

Процесс загрузки класса состоит из трех частей:


1. Loading – на этой фазе происходит поиск и физическая загрузка файла класса
в определенном источнике (в зависимости от загрузчика). Этот процесс
определяет базовое представление класса в памяти. На этом этапе такие
понятия как ≪методы≫, ≪поля≫ и т.д. пока не известны.
2. Linking – процесс, который может быть разбит на 3 части:
- Bytecode verification – проверка байт-кода на соответствие требованиям,
определенным в спецификации JVM.
- Class preparation – создание и инициализация необходимых структур,
используемых для представления полей, методов, реализованных интерфейсов
и т.п., определенных в загружаемом классе.
- Resolving – загрузка набора классов, на которые ссылается загружаемый класс.
3. Initialization – вызов статических блоков инициализации и присваивание полям
класса значений по умолчанию.

Динамическая загрузка классов в Java имеет ряд особенностей:


- Отложенная (lazy) загрузка и связывание классов. Загрузка классов
производится только при необходимости, что позволяет экономить ресурсы и
распределять нагрузку.
- Проверка корректности загружаемого кода (type safeness). Все действия
связанные с контролем использования типов производятся только во время
загрузки класса, позволяя избежать дополнительной нагрузки во время
выполнения кода.
- Программируемая загрузка. Пользовательский загрузчик полностью
контролирует процесс получения запрошенного класса — самому ли искать байт-
код и создавать класс или делегировать создание другому загрузчику.
Дополнительно существует возможность выставлять различные атрибуты
безопасности для загружаемых классов, позволяя таким образом работать с
кодом из ненадежных источников.

35
- Множественные пространства имен. Каждый загрузчик имеет своё
пространство имён для создаваемых классов. Соответственно, классы,
загруженные двумя различными загрузчиками на основе общего байт-кода, в
системе будут различаться.

Существует несколько способов инициировать загрузку требуемого класса:


- Явный: вызов [Link]() или [Link]() (по умолчанию
используется загрузчик, создавший текущий класс, но есть возможность и явного
указания загрузчика);
- Неявный: когда для дальнейшей работы приложения требуется ранее не
использованный класс, JVM инициирует его загрузку.

/*************************************************************************************************************************/

52. Что такое Reflection?


Рефлексия (Reflection) - это механизм получения данных о программе во время
её выполнения (runtime). В Java Reflection осуществляется с помощью Java
Reflection API, состоящего из классов пакетов [Link] и [Link].
Возможности Java Reflection API:
- Определение класса объекта;
- Получение информации о модификаторах класса, полях, методах,
конструкторах и суперклассах;
- Определение интерфейсов, реализуемых классом;
- Создание экземпляра класса;
- Получение и установка значений полей объекта;
- Вызов методов объекта;
- Создание нового массива.

/*************************************************************************************************************************/

53. Метод equals(), свойства, зачем переопределять, ограничения


Метод equals() определяет отношение эквивалентности объектов. При сравнении
объектов с помощью == сравнение происходит лишь между ссылками. При
сравнении по переопределённому разработчиком equals() - по внутреннему
состоянию объектов.

Свойства порождаемого equals() отношения эквивалентности.


1. Рефлексивность: для любой ссылки на значение x, [Link](x) вернет true;
2. Симметричность: для любых ссылок на значения x и y, [Link](y) должно
вернуть true, тогда и только тогда, когда [Link](x) возвращает true.
3. Транзитивность: для любых ссылок на значения x, y и z, если [Link](y) и
[Link](z) возвращают true, тогда и [Link](z) вернёт true;

36
4. Непротиворечивость: для любых ссылок на значения х и у, если несколько
раз вызвать х.equals(y), постоянно будет возвращаться значение true либо
постоянно будет возвращаться значение false при условии, что никакая
информация, используемая при сравнении объектов, не поменялась.
5. Для любой ненулевой ссылки на значение х выражение х.equals(null) должно
возвращать false.

Правила переопределения метода [Link](), какие есть условия


1. Использование оператора == для проверки, является ли аргумент ссылкой
на указанный объект. Если является, возвращается true. Если сравниваемый
объект == null, должно вернуться false.
2. Использование оператор instanceof и вызова метода getClass() для
проверки, имеет ли аргумент правильный тип. Если не имеет, возвращается false.
3. Приведение аргумента к правильному типу. Поскольку эта операция
следует за проверкой instanceof она гарантированно будет выполнена.
4. Обход всех значимых полей класса и проверка того, что значение поля в
текущем объекте и значение того же поля в проверяемом на эквивалентность
аргументе соответствуют друг другу. Если проверки для всех полей прошли
успешно, возвращается результат true, в противном случае - false.
5. По окончанию переопределения метода equals() следует проверить:
является ли порождаемое отношение эквивалентности рефлексивным,
симметричным, транзитивным и непротиворечивым? Если ответ отрицательный,
метод подлежит соответствующей правке.

/*************************************************************************************************************************/

54. Связь hashCode() и equals(). Что, если equals() переопределен.


Равные объекты должны возвращать одинаковые хэш коды. При
переопределении equals() нужно обязательно переопределять и метод
hashCode().
Проблемы при переопределении equals(), не переопределяя hashCode().
Классы и методы, которые используют правила этого контракта могут работать
некорректно. Так, для HashMap это может привести к тому, что пара ≪ключ-
значение≫, которая была в неё помещена при использовании нового экземпляра
ключа не будет в ней найдена.
Как реализованы методы hashCode() и equals() в классе Object?
Реализация метода [Link]() сводится к проверке на равенство двух
ссылок: public boolean equals(Object obj) { return (this == obj); }
Реализация метода [Link]() описана как native, т.е. определена не с
помощью Java кода и обычно возвращает адрес объекта в памяти:
public native int hashCode();

37
Могут ли у разных объектов (ref0 != ref1) быть [Link](ref1) == true? Да,
могут. Для этого в классе этих объектов должен быть переопределен метод
equals(). Если используется метод [Link](), то для двух ссылок x и y метод
вернет true тогда и только тогда, когда обе ссылки указывают на один и тот же
объект (т.е. x == y возвращает true).
Могут ли у разных ссылок на один объект(ref0 == ref1) быть
[Link](ref1) == false? В общем случае - могут, если метод equals()
реализован некорректно и не выполняет свойство рефлексивности: для любых
ненулевых ссылок x метод [Link](x) должен возвращать true.

/*************************************************************************************************************************/

55. Метод hashCode(), свойства, переопределение, какие есть условия


Метод hashCode() необходим для вычисления хэш кода, переданного в
качестве входного параметра объекта. В Java это целое число, в более широком
смыле - битовая строка фиксированной длины, полученная из массива
произвольной длины. Этот метод реализован таким образом, что для одного и
того же входного объекта, хэш код всегда будет одинаковым. Следует понимать,
что в Java множество возможных хэш кодов ограничено типом int, а множество
объектов ничем не ограничено. Из-за этого, вполне возможна ситуация, что хэш
коды разных объектов могут совпасть:
- если хэш коды разные, то и объекты гарантированно разные;
- если хэш коды равны, то объекты не обязательно могут быть равны.
Каковы правила переопределения метода [Link]()?
Рекомендации о том, какие поля следует использовать в hashCode().
Общий совет: выбирать поля, которые с большой долью вероятности будут
различаться. Для этого необходимо использовать уникальные, лучше всего
примитивные поля, например, такие как id, uuid. При этом нужно следовать
правилу, если поля задействованы при вычислении hashCode(), то они должны
быть задействованы и при выполнении equals().
Могут ли у разных объектов быть одинаковые hashCode()?
Да, могут. Метод hashCode() не гарантирует уникальность возвращаемого
значения. Ситуация, когда у разных объектов одинаковые хэш коды называется
коллизией. Вероятность возникновения коллизии зависит от используемого
алгоритма генерации хэш кода.
/*************************************************************************************************************************/

56. Можно ли реализовать equals как [Link]() == [Link]()?


Строго говоря нельзя, поскольку метод hashCode() не гарантирует уникальность
значения для каждого объекта. Однако для сравнения экземпляров класса Object
такой код допустим, т.к. метод hashCode() в классе Object возвращает

38
уникальные значения для разных объектов (его вычисление основано на
использовании адреса объекта в памяти).
Есть класс Point {int x,y;}. Почему хэшкод вида 31*x+y предпочтительнее,
чем x + y? Множитель создает зависимость значения хэш кода от очередности
обработки полей, что в итоге порождает лучшую хэш функцию.
/*************************************************************************************************************************/

57. Разница [Link]() == [Link]() и instanceof MyClass.


Оператор instanceof сравнивает объект и указанный тип. Его можно использовать
для проверки, является ли данный объект экземпляром некоторого класса, либо
экземпляром его дочернего класса, либо экземпляром класса, который реализует
указанный интерфейс. В отличие от него, [Link]() == [Link]()
проверяет два класса на идентичность, поэтому для корректной реализации
контракта метода equals() необходимо использовать точное сравнение с
помощью метода getClass().
/*************************************************************************************************************************/

58. Расскажите про клонирование объектов.


Использование оператора присваивания не создает нового объекта, а лишь
копирует ссылку на объект. Таким образом, две ссылки указывают на одну и ту
же область памяти, на один и тот же объект. Для создания нового объекта с
таким же состоянием используется клонирование объекта.
Класс Object содержит protected метод clone(), осуществляющий побитовое
копирование объекта производного класса. Но сначала нужно переопределить
метод clone() как public для обеспечения возможности его вызова. В
переопределенном методе следует вызвать базовую версию метода
[Link](), которая и выполняет клонирование. Чтобы окончательно сделать
объект клонируемым, класс должен реализовать интерфейс Cloneable.
Интерфейс Cloneable не содержит методов относится к маркерным
интерфейсам, а его реализация гарантирует, что метод clone() класса Object
возвратит точную копию вызвавшего его объекта с воспроизведением значений
всех его полей. В противном случае метод генерирует исключение
CloneNotSupportedException. Следует отметить, что при использовании этого
механизма объект создается без вызова конструктора.
Это решение эффективно только в случае, если поля клонируемого объекта
представляют собой значения базовых типов и их обёрток или неизменяемых
(immutable) объектных типов. Если же поле клонируемого типа является
изменяемым ссылочным типом, то для корректного клонирования требуется
другой подход. Причина заключается в том, что при создании копии поля
оригинал и копия представляют собой ссылку на один и тот же объект.
В этой ситуации следует также клонировать и сам объект поля класса.
Такое клонирование возможно только в случае, если тип атрибута класса также

39
реализует интерфейс Cloneable и переопределяет метод clone(). Так как, если
это будет иначе вызов метода невозможен из-за его недоступности. Отсюда
следует, что если класс имеет суперкласс, то для реализации механизма
клонирования текущего класса-потомка необходимо наличие корректной
реализации такого механизма в суперклассе. При этом следует отказаться от
использования объявлений final для полей объектных типов по причине
невозможности изменения их значений при реализации клонирования.
Помимо встроенного механизма клонирования в Java для клонирования
объекта можно использовать:
1. Специализированный конструктор копирования - в классе описывается
конструктор, который принимает объект этого же класса и инициализирует поля
создаваемого объекта значениями полей переданного.
2. Фабричный метод - (Factory method), который представляет собой
статический метод, возвращающий экземпляр своего класса.
3. Механизм сериализации - сохранение и последующее восстановление
объекта в/из потока байтов.
/*************************************************************************************************************************/

59. В чем отличие между поверхностным и глубоким клонированием?


Поверхностное копирование копирует настолько малую часть информации об
объекте, насколько возможно. По умолчанию, клонирование в Java является
поверхностным, т.е. класс Object не знает о структуре класса, который копирует.
Клонирование такого типа осуществляется JVM по следующим правилам:
1. Если класс имеет только члены примитивных типов, то будет создана
совершенно новая копия объекта и возвращена ссылка на этот объект.
2. Если класс содержит члены ссылочных типов, то копируются ссылки на
объекты этих классов. Тогда оба объекта будут иметь одинаковые ссылки.
Глубокое копирование дублирует абсолютно всю информацию объекта:
1. Нет необходимости копировать отдельно примитивные данные;
2. Все члены ссылочного типа в оригинальном классе должны поддерживать
клонирование. Для каждого такого члена при переопределении метода clone()
должен вызываться [Link]();
3. Если какой-либо член класса не поддерживает клонирование, то в методе
клонирования необходимо создать новый экземпляр этого класса и скопировать
каждый его член со всеми атрибутами в новый объект класса, по одному.
/*************************************************************************************************************************/

60. Какой способ клонирования предпочтительней?


Наиболее безопасный и, следовательно, предпочтительный способ
клонирования - использование специализированного конструктора копирования:
1. Отсутствие ошибок наследования (не нужно беспокоиться, что у
наследников появятся новые поля, которые не склонируются методом clone());

40
2. Поля для клонирования указываются явно;
3. Возможность клонировать даже final поля.
/*************************************************************************************************************************/

61. Почему метод clone() объявлен в Object, а не в Cloneable?


Метод clone() объявлен в классе Object с указанием модификатора native, чтобы
обеспечить доступ к стандартному механизму поверхностного копирования
объектов. Одновременно он объявлен и как protected, чтобы нельзя было
вызвать этот метод у не переопределивших его объектов. Непосредственно
интерфейс Cloneable является маркерным (не содержит объявлений методов) и
нужен только для обозначения самого факта, что данный объект готов к тому,
чтобы быть клонированным. Вызов переопределённого метода clone() у не
Cloneable объекта вызовет выбрасывание CloneNotSupportedException.
/*************************************************************************************************************************/

41
Исключения (Exceptions)
62. Иерархия исключений, виды, checked, unchecked exception.
Исключения делятся на несколько классов, но все они имеют общего предка —
класс Throwable, потомками которого являются классы Exception и Error.
Ошибки (Errors) представляют собой более серьёзные проблемы, которые,
согласно спецификации Java, не следует обрабатывать в собственной
программе, поскольку они связаны с проблемами уровня JVM. Например,
исключения такого рода возникают, если закончилась память доступная
виртуальной машине.
Исключения (Exceptions) являются результатом проблем в программе, которые
в принципе решаемы, предсказуемы и последствия которых возможно устранить
внутри программы. Например, произошло деление целого числа на ноль.
В Java все исключения делятся на 2 типа:
- Checked (контролируемые/проверяемые исключения) должны обрабатываться
блоком catch или описываться в сигнатуре метода (например, throws
IOException). Наличие такого обработчика/модификатора сигнатуры проверяются
на этапе компиляции;
- Unchecked (неконтролируемые/непроверяемые исключения), к ним относятся
ошибки Error (например, OutOfMemoryError), обрабатывать которые не
рекомендуется, и исключения времени выполнения, представленные классом
RuntimeException и его наследниками (напр. NullPointerException), которые могут
не обрабатываться блоком catch и не быть описанными в сигнатуре метода.
Какой оператор позволяет принудительно выбросить исключение. Это
оператор throw: throw new Exception();
О чем говорит ключевое слово throws? Модификатор throws прописывается
в сигнатуре метода и указывает на то, что метод потенциально может выбросить
исключение с указанным типом.
Как написать собственное пользовательское исключение
Необходимо унаследоваться от базового класса требуемого типа исключений
(например от Exception или RuntimeException).
class CustomException extends Exception { public CustomException() { super(); }
public CustomException(final String string) { super(string + «is invalid»; }
public CustomException(final Throwable cause) { super(cause); } }
Какие существуют unchecked exception? Наиболее часто встречающиеся:
ArithmeticException, ClassCastException, ConcurrentModificationException,
IllegalArgumentException, IllegalStateException, IndexOutOfBoundsException,
NoSuchElementException, NullPointerException, UnsupportedOperationException.

63. Что представляют из себя ошибки класса Error?

42
Ошибки класса Error представляют собой наиболее серьёзные проблемы уровня
JVM. Например, исключения такого рода возникают, если закончилась память
доступная виртуальной машине. Обрабатывать такие ошибки не запрещается, но
делать этого не рекомендуется.
Что вы знаете об OutOfMemoryError? OutOfMemoryError выбрасывается, когда
JVM Java не может создать (разместить) объект из-за нехватки памяти, а
сборщик мусора не может высвободить достаточное её количество. Область
памяти, занимаемая java процессом, состоит из нескольких частей. Тип
OutOfMemoryError зависит от того, в какой из них не хватило места:
[Link]: Java heap space: Не хватает места в куче, а
именно, в области памяти в которую помещаются объекты, создаваемые в
приложении программно. Обычно проблема кроется в утечке памяти. Размер
задается параметрами -Xms и -Xmx.
[Link]: PermGen space: (до версии Java 8) Эта ошибка
возникает при нехватке места в Permanent области, размер которой задается
параметрами -XX:PermSize и -XX:MaxPermSize.
[Link]: GC overhead limit exceeded: Эта ошибка может
возникнуть как при переполнении первой, так и второй областей. Связана она с
тем, что памяти осталось мало и сборщик мусора постоянно работает, пытаясь
высвободить немного места. Данную ошибку можно отключить с помощью
параметра -XX:-UseGCOverheadLimit.
[Link]: unable to create new native thread: Происходит,
когда нет возможности создавать новые потоки.

64. Опишите работу блока try-catch-finally.


Try — данное ключевое слово используется для отметки начала блока кода,
который потенциально может привести к ошибке. Catch — ключевое слово для
отметки начала блока кода, предназначенного для перехвата и обработки
исключений в случае их возникновения. Finally — ключевое слово для отметки
начала блока кода, который является дополнительным. Этот блок помещается
после последнего блока catch. Управление передаётся в блок finally в любом
случае, было выброшено исключение или нет. Общий вид конструкции для
обработки исключительной ситуации выглядит следующим образом:
try { // код, который потенциально может привести к исключительной ситуации }
catch(SomeException e ) { // в скобках указывается класс ожидаемой ошибки
// код обработки исключительной ситуации }
finally { // необязательный блок код которого выполняется в любом случае }
Что такое механизм try-with-resources? Данная конструкция, которая
появилась в Java 7, позволяет использовать блок try-catch, не заботясь о
закрытии ресурсов, используемых в данном коде. Ресурсы объявляются в
скобках сразу после try, а компилятор уже сам неявно создаёт секцию finally, в
которой и происходит освобождение занятых в блоке ресурсов. Под ресурсами

43
подразумеваются сущности, реализующие интерфейс [Link].
Общий вид: try(/* объявление ресурсов */) { … } catch(Exception ex) { … } finally { … }
Стоит заметить, что блоки catch и явный finally выполняются уже после того, как
закрываются ресурсы в неявном finally.
Возможно ли использование блока try-finally без catch? Такая запись
допустима, но смысла в такой записи не так много, всё же лучше иметь блок
catch, в котором будет обрабатываться необходимое исключение.
Может ли один блок catch отлавливать несколько исключений. C Java 7
доступна новая языковая конструкция, позволяющая перехватывать несколько
исключений одним блоком catch: try {…} catch(IOException | SQLException ex) {…}
В нормальных условиях, всегда ли исполняется блок finally? Код в блоке
finally будет выполнен всегда, независимо от того, выброшено исключение или
нет.

65. Существуют ли ситуации когда блок finally не будет выполнен


Например когда JVM «умирает» - в такой ситуации finally недостижим и не будет
выполнен, так как происходит принудительный системный выход из программы:
try { [Link](0); } catch(Exception e) { [Link](); } finally {...}
Если метод main() выбросит исключение, где оно будет обработано? Да,
метод main () может выбросить исключение, оно будет передано в JVM.

66. Последовательность, в которой должны идти блоки catch


Общее правило: обрабатывать исключения нужно от «младшего» к старшему.
Т.е. нельзя поставить в первый блок catch(Exception ex) {}, иначе все дальнейшие
блоки catch() уже ничего не смогут обработать, т.к. любое исключение будет
соответствовать обработчику catch(Exception ex). Таким образом, исходя из
факта, что FileNotFoundException extends IOException сначала нужно обработать
FileNotFoundException, а затем уже IOException:
void method() { try {…} catch (FileNotFoundException ex) {…} catch (IOException ex) {…} }

44
Обобщения (Generics)
67. Generics (понятие, что дают, ограничения, для чего нужен "?").
Generics (обобщения, обобщенные типы) – это типы с параметром, средства
языка Java для реализации обобщённого программирования: особого подхода к
описанию данных и алгоритмов, позволяющего работать с различными типами
данных без изменения их описания. Обобщенные типы или методы отличаются
от обычных тем, что имеют типизированные параметры. Когда мы объявляем
generic-переменную, то мы указываем не один тип, а два: тип переменной и тип
данных, которые она у себя хранит. Дженерики были добавлены в JSE 5.0.
Примером использования обобщений может служить JCF. Так, класс
LinkedList<E> - типичный обобщенный тип, где параметр E представляет тип
элементов, которые будут храниться в коллекции. Создание объектов
обобщенных типов происходит посредством замены параметризированных типов
реальными типами данных. Вместо того, чтобы просто использовать LinkedList,
ничего не говоря о типе элемента в списке, предлагается использовать точное
указание типа LinkedList<String>, LinkedList<Integer> и т. п.
Еще один пример обобщения – ArrayList. Когда мы создаём новый объект типа
ArrayList, нам удобно указать, значения какого типа будут храниться внутри него.
ArrayList<String> list = new ArrayList<String>(); // можно хранить только String
ArrayList<Integer> list = new ArrayList<Integer>(); // только Integer и int.
Нельзя использовать примитивные типы в качестве параметра.
Одно из назначений дженериков - более сильная проверка типов во время
компиляции и устранение необходимости явного приведения. Например:
List<String> list = new ArrayList<>(); [Link]("Hello!"); [Link](123);
Тут не только не нужно приведение к String, но и компилятор не даст
скомпилировать класс, пока мы не переведем 123 в строку, т.к. это Integer.
Raw types - сырые типы. Говоря о дженериках, мы имеем две категории:
типизированные типы (Generic Types) и сырые типы. Raw Types - типы без
указания типа в фигурных скобках: List list = new ArrayList<>();
Типизированные типы - с указанием типа: List<String> list = new ArrayList<>();
Это синтаксис, добавленный в JSE 7, и называется он "the diamond". Также
Diamond синтаксис связан с понятием "Type Inference", или же выведение типов.
Ведь компилятор, видя справа <> смотрит на левую часть, где расположено
объявление типа переменной, в которую присваивается значение. И по этой
части понимает, каким типом типизируется значение справа.
Правило: всегда использовать diamond синтаксис, если мы используем Generic
types. В ином случае рискуем пропустить, где используется raw type.
Cтирание типов. При использовании типизированных методов стоит всегда
помнить про стирание типов. Посмотрим на пример:
public static <T> T getValue(Object obj) { return (T) obj; }
List list=[Link](2,3); for (Object e:list) { [Link](Util.<Integer>getValue(e)+1); } }

45
Он работает потому, что компилятор понимает, что у метода тип Integer.
Типизированные классы (Generic Types). Типизировать можно и методы, и
классы. Если используем класс, дженерик указывается после его имени:
public static class SomeType<T> {
public <E> void test(Collection<E> c) { for (E e : c) { [Link](e); } }
public void test(List<Integer> c) { for (Integer e : c) { [Link](e); } } }
Правило #2: Если класс типизирован, всегда указывать тип в дженерике.
Ограничения. К типам в дженериках мы можем применить ограничение.
Например, если хотим, чтобы контейнер принимал на вход только Number:
public static class NumberContainer<T extends Number> { ... }
public static void main(String []args) {
NumberContainer number1 = new NumberContainer(2L);
NumberContainer number2 = new NumberContainer(1);
NumberContainer number3 = new NumberContainer("f"); } }
Как видим, мы ограничили тип дженерика как класс/интерфейс Number и
наследники. Интересно, что можно указать не только класс, но и интерфейсы:
public static class NumberContainer<T extends Number & Comparable> { ... }
Wildcard. Ещё у дженериков есть понятие Wildcard:
- Upper Bounded Wildcards - <? extends Number>
- Unbounded Wildcards - <?>
- Lower Bounded Wildcards - <? super Integer>
С Wildcard действует так называемый Get Put principle. Можно их выразить в
следующем виде:
- Use an extends wildcard when you only get values out of a structure.
- Use a super wildcard when you only put values into a structure.
- And don't use a wildcard when you both want to get and put from/to a structure.
Этот принцип называют принципом PECS (Producer Extends Consumer Super).

68. Что такое интернационализация, локализация.


Интернационализация (internationalization) - способ создания приложений, при
котором их можно легко адаптировать для разных аудиторий, говорящих на
разных языках. Локализация (localization) - адаптация интерфейса приложения
под несколько языков. Добавление нового языка может внести определенные
сложности в локализацию интерфейса.

46
JAVA Collections
69. Коллекции. Основные интерфейсы JCF и их реализации
Коллекция - это структура данных, набор каких-либо объектов. Данными
(объектами в наборе) могут быть числа, строки, объекты различных классов и т.п.
Основные интерфейсы JCF. На вершине иерархии в Java Collection
Framework располагаются 2 интерфейса: Collection и Map. Эти интерфейсы
разделяют все коллекции, входящие во фреймворк на две части по типу
хранения данных: простые последовательные наборы элементов и наборы пар
«ключ —значение». Интерфейс Collection расширяют интерфейсы:
- List (список) - коллекция, которая допускает дублирующие значения. Ее
элементы пронумерованы с 0, их можно получить по индексу. Реализации:
- ArrayList - инкапсулирует в себе обычный массив, длина которого
автоматически увеличивается при добавлении новых элементов.
- LinkedList (двунаправленный связный список) - состоит из узлов, каждый из
которых содержит как собственно данные, так и две ссылки на следующий и
предыдущий узел.
- Vector — динамический массив объектов, методы которого синхронизированы.
- Stack — реализация стека LIFO (last-in-first-out).
- Set (сет) описывает неупорядоченную коллекцию, не содержащую
повторяющихся элементов. Реализации:
- HashSet - использует HashMap для хранения данных. В качестве ключа и
значения используется добавляемый элемент. Из-за особенностей реализации
порядок элементов не гарантируется при добавлении.
- LinkedHashSet — гарантирует, что порядок элементов при обходе коллекции
будет идентичен порядку добавления элементов.
- TreeSet — предоставляет возможность управлять порядком элементов в
коллекции при помощи объекта Comparator, либо сохраняет элементы с
использованием «natural ordering».
- Queue (очередь) предназначена для хранения элементов с предопределённым
способом вставки и извлечения FIFO (first-in-first-out):
- PriorityQueue — предоставляет возможность управлять порядком элементов в
коллекции при помощи объекта Comparator, либо сохраняет элементы с
использованием «natural ordering».
- ArrayDeque — реализация интерфейса Deque, расширяющий интерфейс Queue
методами, позволяющими реализовать конструкцию вида LIFO (last-in-first-out).
Интерфейс Map реализован классами:
- Hashtable — хэш-таблица, методы которой синхронизированы. Не позволяет
использовать null в качестве значения или ключа и не является упорядоченной.

47
- HashMap — хэш-таблица. Позволяет использовать null в качестве значения или
ключа и не является упорядоченной.
- LinkedHashMap — упорядоченная реализация хэш-таблицы.
- TreeMap — реализация, основанная на красно-чёрных деревьях. Является
упорядоченной и предоставляет возможность управлять порядком элементов в
коллекции при помощи объекта Comparator, либо сохраняет элементы с
использованием «natural ordering».
- WeakHashMap — реализация хэш-таблицы, которая организована с
использованием weak references для ключей (сборщик мусора автоматически
удалит элемент из коллекции при следующей сборке мусора, если на ключ этого
элемента нет жёстких ссылок).
В виде иерархии интерфейсы List, Set, Map, SortedSet, SortedMap, Collection,
Iterable,Iterator,NavigableSet,NavigableMap располагаются так:
Iterable → Collection → List → Set → SortedSet → NavigableSet → Map →
SortedMap → NavigableMap → Iterator.
Почему Map — не Collection, в то время как List иSet являются Collection?
Collection представляет собой совокупность некоторых элементов. Map - это
совокупность пар «ключ-значение».
В чем разница между классами [Link] и [Link]?
[Link] - набор статических методов для работы с коллекциями.
[Link] - один из основных интерфейсов Java Collections Framework.
/*************************************************************************************************************************/

70. Что такое Fail-fast и Fail-safe поведение.


Fail-fast поведение означает, что при возникновении ошибки или состояния,
которое может привести к ошибке, система немедленно прекращает дальнейшую
работу и уведомляет об этом. Использование fail-fast подхода позволяет
избежать недетерминированного поведения программы в течение времени.
В Java Collections API некоторые итераторы ведут себя как fail-fast и
выбрасывают ConcurrentModificationException, если после его создания была
произведена модификация коллекции, т.е. добавлен или удален элемент
напрямую из коллекции, а не используя методы итератора. Реализация такого
поведения осуществляется за счет подсчета количества модификаций коллекции
(modification count):
- при изменении коллекции счетчик модификаций так же изменяется;
- при создании итератора ему передается текущее значение счетчика;
- при каждом обращении к итератору сохраненное значение счетчика
сравнивается с текущим, и, если они не совпадают, возникает исключение.

48
Какая разница между fail-fast и fail-safe? В противоположность fail-fast,
итераторы fail-safe не вызывают никаких исключений при изменении структуры,
потому что они работают с клоном коллекции вместо оригинала.
Примеры итераторов, реализующих поведение fail-safe. Итератор
коллекции CopyOnWriteArrayList и итератор представления keySet коллекции
ConcurrentHashMap являются примерами итераторов fail-safe.
/*************************************************************************************************************************/

71. Что такое Enumeration, Iterator И Iterable, различия, использование


Хотя оба интерфейса Enumeration, Iterator и предназначены для обхода
коллекций между ними имеются существенные различия:
- с помощью Enumeration нельзя добавлять/удалять элементы;
- в Iterator исправлены имена методов для повышения читаемости кода
(Enumeration. hasMoreElements() соответствует [Link](),
[Link]() соответствует Iterator. next() и т.д);
- Enumeration присутствуют в устаревших классах, таких как Vector/Stack, тогда
как Iterator есть во всех современных классах-коллекциях.
Как между собой связаны Iterable и Iterator? Интерфейс Iterable имеет только
один метод - iterator(), который возвращает Iterator.
Как между собой связаны Iterable, Iterator и «for-each» Классы, реализующие
интерфейс Iterable, могут применяться в конструкции for-each, которая
использует Iterator.
Сравните Iterator и ListIterator.
ListIterator расширяет интерфейс Iterator. ListIterator может быть использован
только для перебора элементов коллекции List;
Iterator позволяет перебирать элементы только в одном направлении, при
помощи метода next(). Тогда как ListIterator позволяет перебирать список в обоих
направлениях, при помощи методов next() и previous();
ListIterator не указывает на конкретный элемент: его текущая позиция
располагается между элементами, которые возвращают методы previous() и
next().
При помощи ListIterator вы можете модифицировать список, добавляя/удаляя
элементы с помощью методов add() и remove(). Iterator не поддерживает данного
функционала.
Что произойдет при вызове [Link]() без предварительного вызова
[Link]()? Если итератор указывает на последний элемент коллекции, то
возникнет исключение NoSuchElementException, иначе будет возвращен
следующий элемент.
Сколько элементов будет пропущено если[Link]() будет вызван после
10-ти вызовов [Link]()? Нисколько - hasNext() осуществляет только
проверку наличия следующего элемента.

49
Как поведёт себя коллекция если вызвать [Link]()?
Если вызову [Link]() предшествовал вызов [Link](), то
[Link]() удалит элемент коллекции, на который указывает итератор, в
противном случае будет выброшено IllegalStateException().
Как поведёт себя уже инстанциированный итератор для collection,
если вызвать [Link]()? При следующем вызове методов итератора
будет выброшено ConcurrentModificationException.
Как избежать ConcurrentModificationException во время перебора коллекции
- Попробовать подобрать другой итератор, работающий по принципу fail-safe. К
примеру, для List можно использовать ListIterator.
- Использовать ConcurrentHashMap и CopyOnWriteArrayList.
- Преобразовать список в массив и перебирать массив.
- Блокировать изменения списка на время перебора с помощью блока
synchronized.
Отрицательная сторона последних двух вариантов - ухудшение
производительности.
Какая коллекция реализует дисциплину обслуживания FIFO? FIFO, First-In-
First-Out («первым пришел-первым ушел») - по этому принципу построена
коллекция Queue.
Какая коллекция реализует дисциплину обслуживания FILO? FILO, First-In-
Last-Out («первым пришел, последним ушел») - по этому принципу построена
коллекция Stack.
/*************************************************************************************************************************/

72. Различия ArrayList и LinkedList, что такое Vector


ArrayList - список, реализованный на основе массива, а LinkedList — классический
двусвязный список, основанный на объектах со ссылками между ними.
ArrayList:
- доступ к произвольному элементу по индексу за константное время O(1);
- доступ к элементам по значению за линейное время O(N);
- вставка в конец в среднем производится за константное время O(1);
- удаление произвольного элемента из списка занимает значительное время т.к.
при этом все элементы, находящиеся ≪правее≫ смещаются на одну ячейку
влево (реальный размер массива (capacity) не изменяется);
- вставка элемента в произвольное место списка занимает значительное время
т.к. при этом все элементы, находящиеся ≪правее≫ смещаются на одну ячейку
вправо;
- минимум накладных расходов при хранении.
LinkedList:
- получение элемента по индексу или значению требует линейного времени O(N);
- добавление и удаление в начало или конец списка требует константного O(1);
- вставка или удаление в/из произвольного места требует константного O(1);

50
- требует больше памяти для хранения такого же количества элементов, потому
что кроме самого элемента хранятся еще указатели на следующий и
предыдущий элементы списка.
В целом, LinkedList в абсолютных величинах проигрывает ArrayList и по
потребляемой памяти, и по скорости выполнения операций. LinkedList лучше
применять, когда нужны частые операции вставки/удаления или в случаях, когда
необходимо гарантированное время добавления элемента в список.
Зачем добавили ArrayList, если уже был Vector?
Методы класса Vector синхронизированы, а ArrayList - нет;
По умолчанию, Vector удваивает свой размер, когда заканчивается выделенная
под элементы память. ArrayList же увеличивает свой размер только на половину.
Vector это устаревший класс и его использование не рекомендовано.
Что работает быстрее - ArrayList или LinkedList? Смотря какие действия будут
выполняться над структурой. см. Чем отличается ArrayList от LinkedList.
Какое худшее время работы метода contains() для элемента LinkedList?
O(N). Время поиска элемента линейно пропорционально к-ву элементов в списке.
Какое худшее время работы метода contains() для элемента в ArrayList?
O(N). Время поиска элемента линейно пропорционально к-ву элементов в списке.
Какое худшее время работы метода add() для LinkedList?
O(N). Добавление в начало/конец списка осуществляется за время O(1).
Какое худшее время работы метода add() для ArrayList? O(N). Вставка
элемента в конец списка осуществляется за время O(1), но если вместимость
массива недостаточна, то происходит создание нового массива с увеличенным
размером и копирование всех элементов из старого массива в новый.
Необходимо добавить 1 млн элементов, какую структуру вы используете.
Однозначный ответ можно дать только исходя из информации о том в какую
часть списка происходит добавление элементов, что потом будет происходить с
элементами списка, существуют ли какие-то ограничения по памяти или скорости
выполнения. см. Чем отличается ArrayList от LinkedList.
Как происходит удаление элементов из ArrayList? Как меняется его размер?
При удалении произвольного элемента из списка, все элементы, находящиеся
≪правее≫ смещаются на одну ячейку влево и реальный размер массива (его
емкость, capacity) не изменяется никак. Механизм автоматического
≪расширения≫ массива существует, а вот автоматического ≪сжатия≫ нет,
можно только явно выполнить ≪сжатие≫ командой trimToSize().
Сколько необходимо дополнительной памяти при вызове [Link]()?
Если в массиве достаточно места для размещения нового элемента, то
дополнительной памяти не требуется. Иначе происходит создание нового
массива размером в 1,5 раза превышающим существующий (это верно для JDK
выше 1.7, в более ранних версиях размер увеличения иной).
Сколько выделяется дополнительно памяти при вызове [Link]()?
Создается один новый экземпляр вложенного класса Node.

51
73. Оцените количество памяти на один примитив byte в LinkedList.

Каждый элемент LinkedList хранит ссылку на предыдущий, следующий элемент и


ссылку на данные. private static class Node<E> { E item; Node<E> next; Node<E> prev; … }
Для 32-битных систем каждая ссылка занимает 32 бита (4 байта). Сам объект
(заголовок) вложенного класса Node занимает 8 байт. 4+4+4+8=20 байт, а т.к.
размер каждого объекта в Java кратен 8, соответственно получаем 24 байта.
Примитив типа byte занимает 1 байт памяти, но в JCF примитивы упаковываются:
объект типа Byte занимает в памяти 16 байт (8 байт на заголовок объекта, 1 байт
на поле типа byte и 7 байт для кратности 8). Также напомню, что значения от -128
до 127 кэшируются и для них новые объекты каждый раз не создаются. Таким
образом, в x32 JVM 24 байта тратятся на хранение одного элемента в списке и 16
байт - на хранение упакованного объекта типа Byte. Итого 40 байт. Для 64-битной
JVM каждая ссылка занимает 64 бита (8 байт), размер заголовка каждого объекта
составляет 16 байт (два машинных слова). Вычисления аналогичны: 8 + 8 + 8 +
16 = 40байт и 24 байта. Итого 64 байта.
/*************************************************************************************************************************/

74. Оцените количество памяти на один примитив byte в ArrayList.


ArrayList основан на массиве, для примитивных типов данных осуществляется
автоматическая упаковка значения, поэтому 16 байт тратится на хранение
упакованного объекта и 4 байта (8 для x64) - на хранение ссылки на этот объект в
самой структуре данных. Таким образом, в x32 JVM 4 байта используются на
хранение одного элемента и 16 байт - на хранение упакованного объекта типа
Byte. Для x64 - 8 байт и 24 байта соответственно.
Для чего операция добавления элемента в середину [Link]([Link]()\2,
newElement)) будет медленнее - ArrayList или для LinkedList?
Для ArrayList:
- проверка массива на вместимость. Если ее недостаточно, то увеличение
размера массива и копирование всех элементов в новый массив (O(N));
- копирование всех элементов, расположенных правее от позиции вставки, на
одну позицию вправо (O(N));
- вставка элемента (O(1)).
Для LinkedList:
- поиск позиции вставки (O(N))
- вставка элемента (O(1)).
В худшем случае вставка в середину списка эффективнее для LinkedList. В
остальных - скорее всего, для ArrayList, поскольку копирование элементов
осуществляется за счет вызова быстрого системного метода [Link]().

52
JAVA 8

75. Какие нововведения появились в Java 8 и JDK 8?

Методы интерфейсов по умолчанию;


Лямбда-выражения;
Функциональные интерфейсы;
Ссылки на методы и конструкторы;
Повторяемые аннотации;
Аннотации на типы данных;
Рефлексия для параметров методов;
Stream API для работы с коллекциями;
Параллельная сортировка массивов;
Новое API для работы с датами и временем;
Новый движок JavaScript Nashorn;
Добавлено несколько новых классов для потокобезопасной работы;
Добавлен новый API для Calendar и Locale;
Добавлена поддержка Unicode 6.2.0;
Добавлен стандартный класс для работы с Base64;
Добавлена поддержка беззнаковой арифметики;
Улучшена производительность конструктора [Link](byte[], *) и метода
[Link].
getBytes();
Новая реализация [Link], позволяющая устанавливать
подмножество при-
вилегий без необходимости проверки всех остальных уровней доступа;
Password-based алгоритмы стали более устойчивыми;
Добавлена поддержка SSL/TLS Server Name Indication (NSI) в JSSE Server;
Улучшено хранилище ключей (KeyStore);
Добавлен алгоритм SHA-224;
Удален мост JDBC - ODBC;
Удален PermGen, изменен способ хранения мета-данных классов;
Возможность создания профилей для платформы Java SE, которые включают в
себя не всю плат-
форму целиком, а некоторую ее часть;
Инструментарий
Добавлена утилита jjs для использования JavaScript Nashorn;
Команда java может запускать JavaFX приложения;
Добавлена утилита jdeps для анализа .class-файлов.

53
76. Лямбды
2.1. Лямбды и Функциональные интерфейсы
Лямбды - наиболее большое изменение языка в Java 8. Они позволяют нам
задавать функциональность как аргумент метода (объявляя функцию вокруг), или
задавать код как данные. Простейшая форме лямбды - разделенный запятыми
список параметров, символ –> и тело. Например: [Link]( "a", "b",
"d" ).forEach( e -> [Link]( e ) );
Функциональный интерфейс – это интерфейс с только одним методом. В
результате он может быть неявно преобразован в лямбда-выражение.
[Link] и [Link] - два замечательных примера
функциональных интерфейсов.
@FunctionalInterface public interface Functional { void method(); }
2.2. Методы интерфейсов по умолчанию и статические методы
Методы по умолчанию позволяют добавлять новые методы к существующим
интерфейсам, не нарушая обратную совместимость для ранее написанных
версий этих интерфейсов. Они делают интерфейсы несколько похожими на
трейты, но служат немного другой цели.
Разница между методами по умолчанию и абстрактными методами в том,
что абстрактные методы должны быть реализованы, а методы по умолчанию -
нет. Вместо этого каждый интерфейс должен предоставить так называемую
реализацию по умолчанию, и все наследники будут получать ее по умолчанию, с
возможностью ее переопределить. Давайте посмотрим на пример ниже.
private interface Def { default String notReq() { return "Default"; } }
private static class DefImp implements Def { }
private static class OverImp implements Def { @Override public String notReq() {return "Over"; }}

Статические методы. Интерфейсы могут предложить реализацию статических


методов. Это еще одна интересная особенность, добавленная в Java 8:
private interface DefFactory { static Def create( Supplier<Def> supplier ) { return [Link]();} }
Этот фрагмент объединяет метод по умолчанию и статический метод:
public static void main( String[] args ) {
Def def = [Link]( DefImp::new ); [Link]( [Link]() );
def = [Link]( OverImp::new ); [Link]( [Link]() );
}

2.3. Ссылка на метод


Ссылка на метод (или ссылочный метод) - это сокращенный синтаксис
выражения лямбда, который выполняет только один метод. С помощью ссылки
можно обращаться к методу, не вызывая его. Ссылки на методы и Lambda
аналогичны тем, что они оба требуют целевого типа, который состоит из
совместимого функционального интерфейса. Если лямбда-выражение может

54
быть передано в некоторый метод в качестве параметра, то ссылка на метод
также может быть передана в качестве параметра. Для ссылки на метод
используется оператор double colon ::. Вы можете получить доступ к методу как к
лямбда-выражению, используя нотацию ::.
Рассмотрим пример класса с разными типами ссылочных методов:
public static class Car {
public static Car create( final Supplier<Car> supplier ) { return [Link](); }
public static void collide( final Car car ) { [Link]( "Collided " + [Link]() ); }
public void follow(final Car another) {[Link]("Following the "+[Link]());}
public void repair() { [Link]( "Repaired "+ [Link]() ); }
}

Виды ссылок на методы: есть 4 вида ссылок на методы


1. Ссылки на статические методы Class::static_method [Link]( Car::collide );
interface IFigure { double Volume(double radius); }

class Volume { double SphereVol(double r) { return 4.0/3.0*[Link]*r*r*r; } }

class FigureOp { double Volume(IFigure ref,double r) { return [Link](r); } }

public static void main(String[] args) {


FigureOp fo = new FigureOp(); double vol = [Link](Volume::SphereVol, 5.0); } }

2. Ссылки на методы экземпляра Class::method [Link]( Car::repair );


interface IArrayFunction<T extends Number> { T[] Function(T[] array); }

class ProcessArray {
public static <T extends Number> T[] ProcessArrayReverse(T[] array) {
return [Link]([Link](array)); } }

class DemoRefMethods {
<T extends Number> T[] DemoReverse(IArrayFunction<T> ref, T[] array) {
return [Link](array); } }

public static void main(String[] args) {


DemoRefMethods obj = new DemoRefMethods(); Integer AI[] = {1, 3, 5};
Integer AI2[] = [Link](ProcessArray::ProcessArrayReverse, AI); } }

3. Ссылки на конструкторы Class::new или Class<T>::new у дженериков.


final Car car = [Link]( Car::new ); final List<Car> cars = [Link]( car );

class Triangle { private int a, b, c;


public Triangle(int a, int b, int c) { this.a = a; this.b = b; this.c = c; } }

55
interface IFunction { Triangle Function(int a, int b, int c); }

public static void main(String[] args) {


IFunction ref; ref = Triangle::new; Triangle tr=[Link](5,4,5); } }

4. Ссылки на обобщенные (шаблонные) методы instance::method


final Car police = [Link]( Car::new ); [Link]( police::follow );

class Ar<T extends Number> {Ar(){} Int Sum(T[] A) { return [Link](A).sum(); } }

interface InstanceFunc<T extends Number> { Ar<T> Func(); }

public static void main(String[] args) {


InstanceFunc<Integer> ref; ref=Array::new; Array<Integer> objInterface = [Link]();
Integer[] AI = { 1, 3, 7 }; Double sum = [Link](AI); }

2.4. Повторяющиеся аннотации. Java 8 позволяет тем же аннотациям


повторяться несколько раз в том месте где они объявлены. С тех пор как в Java 5
была введена поддержка аннотаций, эта возможность стала очень популярной и
очень широко используемой. Тем не менее, было ограничение, что одна и та же
аннотация не может быть объявлена более одного раза в одном месте. В Java 8
его уже нет. Повторяющиеся аннотации следует аннотировать себя с
использованием аннотации @Repeatable. На самом деле, это не изменение
языка, а трюк компилятора, но техника остается той же. Пример:
public class RepeatingAnnotations {
@Target( [Link] ) @Retention( [Link] )
public @interface Filters { Filter[] value(); }
@Target( [Link] ) @Retention( [Link] )
@Repeatable( [Link] ) public @interface Filter { String value(); };
@Filter( "filter1" ) @Filter( "filter2" ) public interface Filterable { }
public static void main(String[] args) {
for( Filter filter: [Link]( [Link] ) ) {
[Link]( [Link]() );
}
}}
/*************************************************************************************************************************/

77. Функциональный интерфейс.


Функциональный интерфейс – это интерфейс с только одним методом. В
результате он может быть неявно преобразован в лямбда-выражение.
[Link] и [Link] два замечательных примера
функциональных интерфейсов. На практике функциональные интерфейсы очень
хрупки: если кто-то добавит хотя бы один другой метод в определение

56
интерфейса, он не будет больше функциональным и процесс компиляции не
завершится. Чтобы избежать этой хрупкости и явно определить намерения
интерфейса как функционального в Java 8 была добавлена специальная
аннотация @FunctionalInterface (все существующие интерфейсы в библиотеке
Java получили аннотацию @FunctionalInterface). Давайте посмотрим на это
простое определение функционального интерфейса:
@FunctionalInterface public interface Functional { void method(); }
/*************************************************************************************************************************/

78. Лямбда.
Лямбды (также известные как закрытые или анонимные методы) наиболее
большое и наиболее ожидаемое изменение языка во всем релизе Java 8. Они
позволяют нам задавать функциональность как аргумент метода (объявляя
функцию вокруг), или задавать код как данные: понятия с которыми знаком
каждый разработчик функционального программирования. Много языков на
платформе JVM (Groovy, Scala, …) имели лямбды с первого дня, но у
разработчиков Java не было выбора кроме как представлять лямбды через
анонимные классы.
Обсуждение дизайна лямбд заняло много времени и усилий общественности.
Но в конце концов компромиссы были найдены, что привело к появлению новых
кратких конструкций. В своей простейшей форме лямбда может быть
представлена в виде разделенных запятыми списка параметров, символа –> и
тела. Например: [Link]( "a", "b", "d" ).forEach( e -> [Link]( e ) )
Обратите внимание: тип аргумента e определен компилятором. Кроме того вы
можете явно указать тип параметра обернув параметр в скобки. Например:
[Link]( "a", "b", "d" ).forEach( ( String e ) -> [Link]( e ) );
В случае, если тело лямбды более сложное, оно может быть обернуто в
фигурные скобки подобно определению обычной функции в Java. Например:
[Link]( "a", "b", "d" ).forEach( e -< { [Link]( e ); } );
Разработчики языка долго думали как сделать уже существующие функции
лямбда-дружелюбными. В результате появилось понятие функционального
интерфейса.

57
Streams
79.Потоки: типы операций.

Что такое потоки(stream) в Java 8? Класс Stream был создан для того, чтобы
можно было легко конструировать цепочки потоков данных. Для этого у объекта
типа Stream<T> есть методы, которые возвращают новые объекты типа Stream.
Каждый из потоков данных умеет делать одно простое действие, но если их
объединить в цепочки и добавить лямбда-функции, получаем мощную вещь.
[Link] поддерживает распараллеливание вычислений в потоках.
Позволяет обрабатывать последовательность элементов, исполняя одну или
несколько операций, которые могут выполняться либо последовательно, либо
паралельно. Самая большая польза от этого - в работе с коллекциями. Операции
над потоком относятся либо к промежуточным, либо к терминальным.
Жизненный цикл потока проходит следующие три стадии:
1. Создание 2. Промежуточные операции 3. Терминальная операция, результат.
После использования терминальных операций другие терминальные или
промежуточные операции к этому же потоку не могут быть применены,
поток уже употреблен. Например, в следующем случае мы получим ошибку:
[Link](s->[Link](s)); // применена терминальная операция
long number = [Link](); // здесь ошибка, так как поток уже употреблен

Для создания потока данных можно применять различные методы. В


качестве источника потока мы можем использовать коллекции. В частности, в
JDK 8 в интерфейс Collection, который реализуется всеми классами коллекций,
были добавлены два метода для работы с потоками:
- default Stream<E> stream: возвращает поток данных из коллекции;
- default Stream<E> parallelStream: возвращает параллельный поток коллекции.
Все промежуточные операции возвращают поток, так что мы можем
объединять несколько промежуточных операций без использования точки с
запятой. Терминальные операции возвращают void или непотоковый результат.
Методы работы со стримами. Java Stream API предлагает два вида методов:
1. Конвейерные - возвращают другой stream, то есть работают как builder,
2. Терминальные - возвращают другой объект, коллекции, примитивы и т.д.
Чем отличаются конвейерные и терминальные методы. Общее правило: у
stream'a может быть сколько угодно вызовов конвейерных вызовов и в конце
один терминальный, при этом все конвейерные методы выполняются лениво и
пока не будет вызван терминальный метод никаких действий на самом деле не
происходит, так же, как создать объект Thread или Runnable, но не вызвать start.

58
В целом, этот механизм похож на конструирования SQL запросов, может
быть сколько угодно вложенных Select'ов и только один результат в итоге.
Например, в выражении [Link]().filter((s) -> [Link](«1»)).skip(2).findFirst() ,
filter и skip — конвейерные, а findFirst — терминальный, он возвращает объект
Optional и это заканчивает работу со stream'ом.
/*************************************************************************************************************************/

80. Потоки: как составить поток.


Перечислим несколько способов создать стрим:
1. Классический: Создание стрима из коллекции [Link]()
Collection<String> c=[Link]("a1","a2"); Stream<String> streamCollection=[Link]();

2. Создание стрима из значений [Link](значение1,… значениеN)


Stream<String> streamFromValues = [Link]("a1", "a2", "a3");

3. Создание стрима из массива [Link](массив)


String[] array = {"a1","a2","a3"}; Stream<String> streamFromArrays = [Link](array);

4. Создание стрима из
(строка файла
файла-элемент стрима)
[Link](путь_к_файлу) Stream<String> strFromFiles = [Link]([Link]("[Link]"))

5. Создание стрима из строки «строка».chars() IntStream strString = "123".chars()


6. С помощью [Link]().add() [Link]().add("a1").add("a2").add("a3").build()
7. Создание параллельного стрима [Link]()
Stream<String> stream = [Link]();
8. Создание бесконечного стрима со [Link](нач_усл, выр_генерации)
Stream<Integer> streamFromIterate = [Link](1, n -> n + 1)
9. Создание бесконечного стрима со [Link](выражение_генерации)
Stream<String> streamFromGenerate = [Link](() -> "a1")

/*************************************************************************************************************************/

81. Потоки: примеры операций и их работа.


Если вы не знаете лямбды. Выражение "a3"::equals - это аналог boolean func(s)
{ return "a3".equals(s);}, а (s) -> [Link]("1") это аналог boolean func(s) { return
[Link](«1»);} обернутых в анонимный класс.
2.1 Краткое описание конвейерных методов работы со стримами
filter Отфильтровывает записи, возвращает только соответствующие условию
[Link]().filter("1"::equals).count() [Link]().filter((s)->[Link]("1")).count()
skip Позволяет пропустить N первых элементов
[Link]().skip([Link]()-1).findFirst().orElse("1")=a1 [Link]().skip(1).limit(2).toArray()=[a2,a3]
distinct Возвращает стрим без дубликатов (для метода equals)
[Link]().distinct().collect([Link]()) [Link]().distinct().collect([Link]())

59
map Преобразует каждый элемент стрима
[Link]().map((s) -> s + "_1").collect([Link]())
peek Возвращает тот же стрим, но применяет функцию к каждому его элементу
[Link]().map(String::toUpperCase).peek((e)->[Link]("e)).collect([Link]())
limit Позволяет ограничить выборку некоторым количеством первых элементов
[Link]().limit(2).collect([Link]())
sorted Сортирует значения в натуральном порядке или задавая Comparator:
[Link]().sorted().collect([Link]())
mapToInt, mapToDouble, mapToLong Аналог map, но возвращает числовой стрим
[Link]().mapToInt((s) -> [Link](s)).toArray()
flatMap,flatMapToInt,flatMapToDouble Как map, но создает из 1-го элемента несколько
[Link]().flatMap((p)->[Link]([Link](",")).stream()).toArray(String[]::new)

Обратите внимание:
1. Если вы используете distinct с классом, у которого переопределен equals,
обязательно так же корректно переопределить hashCode в соответствие с
контрактом equals/hashCode (самое главное чтобы hashCode для всех equals
объектов, возвращал одинаковое значение), иначе distinct может не удалить
дубликаты (аналогично, как при использовании HashSet/HashMap).
2. Если вы используете параллельные стримы и вам не важен порядок
элементов после удаления дубликатов - намного лучше для
производительности сделать сначала стрим неупорядоченным с помощь
unordered(), а уже потом применять distinct(), так как подержание стабильности
сортировки при параллельном стриме довольно затратно по ресурсам и distinct()
на упорядоченным стриме будет выполнятся значительно дольше, чем при
неупорядоченном.
2.2 Краткое описание терминальных методов работы со стримами
findFirst Возвращает первый элемент из стрима (возвращает Optional)
[Link]().findFirst().orElse(«1»)
findAny Возвращает любой подходящий элемент из стрима (Optional)
[Link]().findAny().orElse(«1») с.stream().skip(с.size() - 1).findAny().orElse(«empty»)
collect Представление результатов в виде коллекций и других структур данных
[Link]().filter((s) -> [Link](«1»)).collect([Link]())
count Возвращает количество элементов в стриме
[Link]().filter(«a1»::equals).count()
anyMatch Возвращает true, если условие верно хотя бы для одного элемента
[Link]().anyMatch(«a1»::equals)
noneMatch Возвращает true, если условие неверно ни для одного элемента
[Link]().noneMatch(«a8»::equals)
allMatch Возвращает true, если условие выполняется для всех элементов
[Link]().allMatch((s) -> [Link](«1»))
min Возвращает минимальный элемент, в качестве условия - компаратор

60
[Link]().min(String::compareTo).get()
max Возвращает максимальный элемент, в качестве условия - компаратор
[Link]().max(String::compareTo).get()
forEach Применяет функцию к каждому объекту стрима, порядок при
параллельном стриме не гарантируется [Link]().forEach((p) -> [Link]("_1"));
forEachOrdered Применяет функцию к каждому объекту стрима, порядок
элементов гарантирует [Link]().forEachOrdered((p) -> [Link]("_new"));
toArray Возвращает массив значений стрима
[Link]().map(String::toUpperCase).toArray(String[]::new);
reduce Позволяет выполнять агрегатные функции на всей коллекцией и
возвращать один результат [Link]().reduce((s1, s2) -> s1 + s2).orElse(0)
Обратите внимание: методы findFirst, findAny, anyMatch это short-circuiting
методы, то есть обход стримов организуется таким образом, чтобы найти
подходящий элемент максимально быстро, не обходя весь изначальный стрим.
2.3 Краткое описание дополнительных методов у числовых стримов:
sum Сумма всех чисел [Link]().mapToInt((s) -> [Link](s)).sum()
average Среднее арифметическое [Link]().mapToInt((s) -> [Link](s)).average()
mapToObj Числовой стрим в объектный [Link]((id)-> new Key(id)).toArray()
2.4 Несколько других полезных методов стримов:
isParallel Узнать, является ли стрим параллельным.
parallel Вернуть параллельный стрим, если он уже, может вернуть самого себя.
sequential Вернуть последовательный, если он уже, может вернуть самого себя.

С помощью, методов parallel и sequential можно определять какие операции могут


быть параллельными, а какие только последовательными. Также из любого
последовательного стрима можно сделать параллельный и наоборот:
[Link]().peek(...). // операция последовательна
parallel().map(...). // операция может выполняться параллельно,
sequential().reduce(...) // операция снова последовательна
Внимание: крайне не рекомендуется использовать параллельные стримы для
сколько-нибудь долгих операций (получение данных из базы, сетевых
соединений), так как все параллельные стримы работают c одним пулом fork/join
и такие долгие операции могут остановить работу всех параллельных стримов в
JVM из-за отсутствия доступных потоков в пуле, т.е. параллельные стримы стоит
использовать лишь для коротких, на миллисекунды, операций.
/*************************************************************************************************************************/

61
Потоки ввода-вывода в JAVA
82. В чём заключается разница между IO и NIO?

Java IO (input-output) является потокоориентированным, а Java NIO (new/non-


blocking io) –бу-фер-ориентированным. Потокоориентированный ввод/вывод
подразумевает чтение/запись из потока/в поток одного или нескольких байт в
единицу времени поочередно. Данная информация нигде не кэшируются. Таким
образом, невозможно произвольно двигаться по потоку данных вперед или
назад. В Java NIO данные сначала считываются в буфер, что дает больше
гибкости при обработке данных.
Потоки ввода/вывода в Java IO являются блокирующими. Это значит, что когда в
потоке выполнения вызывается read() или write() метод любого класса из пакета
[Link].*, происходит блокировка до тех пор, пока данные не будут считаны или
записаны. Поток выполнения в данный момент не может делать ничего другого.
Неблокирующий режим Java NIO позволяет запрашивать считанные данные из
канала (channel) и получать только то, что доступно на данный момент, или
вообще ничего, если доступных данных пока нет. Вместо того, чтобы оставаться
заблокированным пока данные не станут доступными для считывания, поток
выполнения может заняться чем-то другим.
Тоже самое справедливо и для неблокирующего вывода. Поток выполнения
может запросить запись в канал некоторых данных, но не дожидаться при этом
пока они не будут полностью записаны.
В Java NIO имеются селекторы, которые позволяют одному потоку выполнения
мониторить несколько каналов ввода. Т.е. существует возможность
зарегистрировать несколько каналов с селектором, а потом использовать один
поток выполнения для обслуживания каналов, имеющих доступные для
обработки данные, или для выбора каналов, готовых для записи.

62
Сериализация
83.Что такое сериализация

Сериализация (Serialization) - процесс преобразования структуры данных в


линейную последовательность байтов для дальнейшей передачи или
сохранения. Сериализованные объекты можно затем восстановить
(десериализовать). В Java, согласно спецификации Java Object Serialization
существует два стандартных способа сериализации: стандартная сериализация,
через использование интерфейса [Link] и ≪расширенная≫
сериализация - [Link].
Сериализация позволяет в определенных пределах изменять класс. Вот
наиболее важные изменения, с которыми спецификация Java Object Serialization
может справляться автоматически:
- добавление в класс новых полей;
- изменение полей из статических в нестатические;
- изменение полей из транзитных в нетранзитные.
Обратные изменения (из нестатических полей в статические и из нетранзитных в
транзитные) или удаление полей требуют определенной дополнительной
обработки в зависимости от того, какая степень обратной совместимости
необходима.

Опишите процесс сериализации/десериализации, используя Serializable.

При использовании Serializable применяется алгоритм сериализации, который с


помощью рефлексии (Reflection API) выполняет:
запись в поток метаданных о классе, ассоциированном с объектом (имя класса,
идентификатор SerialVersionUID, идентификаторы полей класса);
рекурсивную запись в поток описания суперклассов до класса [Link] (не
включительно);
запись примитивных значений полей сериализуемого экземпляра, начиная с
полей самого верхнего суперкласса;
рекурсивную запись объектов, которые являются полями сериализуемого
объекта. При этом ранее сериализованные объекты повторно не сериализуются,
что позволяет алгоритму корректно работать с циклическими ссылками.
Для выполнения десериализации под объект выделяется память, после чего его
поля заполняются значениями из потока. Конструктор объекта при этом не
вызывается. Однако при десериализации будет вызван конструктор без
параметров родительского несериализуемого класса, а его отсутствие повлечёт
ошибку десериализации.

63
Многопоточность
84.Модель памяти Java

Модель памяти Java (Java Memory Model, JMM) описывает поведение потоков в
среде исполнения Java. Это часть семантики языка Java, набор правил,
описывающий выполнение многопоточных программ и правил, по которым потоки
могут взаимодействовать друг с другом посредством основной памяти.
Формально модель памяти определяет набор действий межпоточного
взаимодействия (эти действия включают в себя, в частности, чтение и запись
переменной, захват и освобождений монитора, чтение и запись volatile
переменной, запуск нового потока), а также модель памяти определяет
отношение между этими действиями - happens-before - абстракции
обозначающей, что если операция X связана отношением happens-before с
операцией Y, то весь код следуемый за операцией Y, выполняемый в одном
потоке, видит все изменения, сделанные другим потоком, до операции X.
Существует несколько основных правил для отношения happens-before:
В рамках одного потока любая операция happens-before любой операцией
следующей за ней в исходном коде;
Освобождение монитора (unlock) happens-before захват того же монитора (lock);
Выход из synchronized блока/метода happens-before вход в synchronized
блок/метод на том же мониторе;
Запись volatile поля happens-before чтение того же самого volatile поля;
Завершение метода run() экземпляра класса Thread happens-before выход из
метода join() или возвращение false методом isAlive() экземпляром того же
потока;
Вызов метода start() экземпляра класса Thread happens-before начало метода
run() экземпляра того же потока;
Завершение конструктора happens-before начало метода finalize() этого класса;
Вызов метода interrupt() на потоке happens-before обнаружению потоком факта,
что данный метод был вызван либо путем выбрасывания исключения
InterruptedException, либо с помощью методов isInterrupted() или interrupted().

85.Потокобезопасность, разница конкуренции и параллелизма

Потокобезопасность – свойство объекта или кода, которое гарантирует, что при


исполнении или использовании несколькими потоками, код будет вести себя, как
предполагается. Например потокобезопасный счётчик не пропустит ни один счёт,
даже если один и тот же экземпляр этого счётчика будет использоваться
несколькими потоками.
В чём разница между «конкуренцией» и «параллелизмом»

64
Конкуренция —это способ одновременного решения множества задач.
Признаки:
Наличие нескольких потоков управления (например Thread в Java, корутина в
Kotlin), если поток управления один, то конкурентного выполнения быть не может
Недетерминированный результат выполнения. Результат зависит от случайных
событий, реализации и того как была проведена синхронизация. Даже если
каждый поток полностью детерминированный, итоговый результат будет
недетерминированным.
Параллелизм — это способ выполнения разных частей одной задачи.
Признаки:
Необязательно имеет несколько потоков управления
Может приводить к детерминированному результату, так, например, результат
умножения каждого элемента массива на число, не изменится, если умножать его
по частям параллельно.
Что такое кооперативная многозадачность, какой тип многозадачности
использует Java и почему
Кооперативная многозадачность - это способ деления процессорного времени
между потоками, при котором каждый поток обязан отдавать управление
следующему добровольно. Преимущества такого подхода - простота
реализации, меньшие накладные расходы на переключение контекста.
Недостатки - если один поток завис или ведет себя некорректно, то зависает
целиком вся система и другие потоки никогда не получат управление.
Java использует вытесняющую многозадачность, при которой решение о
переключении между потоками процесса принимает операционная система.
В отличие от кооперативной многозадачности управление операционной системе
передаётся вне зависимости от состояния работающих приложений, благодаря
чему, отдельные зависшие потоки процесса, как правило, не ≪подвешивают ≫
всю систему целиком. За счёт регулярного переключения между задачами также
улучшается отзывчивость приложения и повышается оперативность
освобождения ресурсов, которые больше не используются.
В реализации вытесняющая многозадачность отличается от кооперативной, в
частности, тем, что требует обработки системного прерывания от аппаратного
таймера.

65
Servlets, JSP, JSTL
86.Что такое сервлет

Сервлет является интерфейсом, реализация которого расширяет


функциональные возможности сервера. Сервлет взаимодействует с клиентами
посредством принципа запрос-ответ. Хотя сервлеты могут обслуживать любые
запросы, они обычно используются для расширения веб-серверов.
Большинство необходимых для создания сервлетов классов и интерфейсов
содержатся в пакетах [Link] и [Link].
Основные методы сервлета:
public void init(ServletConfig config) throws ServletException запускается сразу
после загрузки сервлета в память;
public ServletConfig getServletConfig() возвращает ссылку на объект, который
предоставляет доступ к информации о конфигурации сервлета;
public String getServletInfo() возвращает строку, содержащую информацию о
сервлете, например: автор и версия сервлета;
public void service(ServletRequest request, ServletResponse response) throws
ServletException, java.
[Link] вызывается для обработки каждого запроса;
public void destroy() выполняется перед выгрузкой сервлета из памяти.
Текущая спецификация - Servlet 3.1 описана в JSR-340 и принята в 2013 году.

87.Преимущества сервлетов над CGI (Common Gateway Interface).

Сервлеты предоставляют лучшую производительность обработки запросов и


более эффективное использование памяти за счет использования преимущество
многопоточности (на каждый запрос создается новая нить, что быстрее
выделения памяти под новый объект для каждого запроса, как это происходит в
CGI). Сервлеты, как платформа и система являются независимыми. Таким
образом веб-приложение написанное с использованием сервлетов может быть
запущена в любом контейнере сервлетов, реализующим этот стандарт и в любой
операционной системе. Использование сервлетов повышает надежность
программы, т.к. контейнер сервлетов самостоятельно заботится о жизненном
цикле сервлетов (а значит и за утечками памяти), безопасности и сборщике
мусора. Сервлеты относительно легки в изучении и поддержке, таким образом
разработчику необходимо заботиться только о бизнес-логике приложения, а не
внутренней реализации веб-технологий.

88.Какова структура веб проекта.

66
src/main/java Исходники приложения/библиотеки
src/main/resources Ресурсные файлы приложения/библиотеки
src/main/filters Файлы сервлетных фильтров
src/main/webapp Исходники веб-приложения
src/test/java Исходники тестов
src/test/resources Ресурсные файлы тестов
src/test/filters Тесты сервлетных фильтров
src/it Интеграционные тесты
src/assembly Описание сборки
src/site Сайт
[Link] Лицензия проекта
[Link] Замечания и определения библиотек зависимостей.
[Link] Описание проекта

89.Что такое контейнер сервлетов

Контейнер сервлетов — программа, представляющая собой сервер, который


занимается системной поддержкой сервлетов и обеспечивает их жизненный цикл
в соответствии с правилами, определёнными в спецификациях. Может работать
как полноценный самостоятельный веб-сервер, быть поставщиком страниц для
другого веб-сервера, или интегрироваться в Java EE сервер приложений.
Контейнер сервлетов обеспечивает обмен данными между сервлетом и
клиентами, берёт на себя выполнение таких функций, как создание программной
среды для функционирующего сервлета, идентификацию и авторизацию
клиентов, организацию сессии для каждого из них.
Наиболее известные реализации контейнеров сервлетов:
Apache, Tomcat, Jetty, JBoss, WildFly, GlassFish, IBM WebSphere, Oracle Weblogic

67
Базы данных
90. ACID, уровни изолированности транзакций.
Atomicity - Атомарность. Гарантирует, что каждая транзакция будет выполнена
полностью или не будет выполнена совсем. Не допускаются промежуточные
состояния. Поскольку операции внутри транзакции невозможно выполнить
моментально, вводится понятие «отката» (rollback): если транзакцию не удаётся
полностью завершить, результаты всех её до сих пор произведённых действий
будут отменены.
Consistency - Согласованность. Транзакция, достигающая своего нормального
завершения (EOT - end of transaction) и тем самым фиксирующая свои
результаты, сохраняет согласованность базы данных. Согласованность является
широким понятием. Например, в банковской системе может существовать
требование равенства суммы, списываемой с одного счёта, сумме, зачисляемой
на другой.
Isolation - Изолированность. Во время выполнения транзакции параллельные
транзакции не должны оказывать влияния на её результат. Изолированность —
требование дорогое, поэтому в реальных БД существуют режимы, не полностью
изолирующие транзакцию (уровни изолированности Repeatable Read и ниже).
Durability - Надежность. Она означает, что когда транзакция будет применена, она
останется в системе, даже если БД упала сразу после выполнения этой транзакции.
Любые изменения, внесённые транзакцией, должны оставаться навсегда. Если
БД сообщила об успешном выполнении транзакции, то она должна быть
действительно применена. Другими словами, если пользователь получил
подтверждение от системы, что транзакция выполнена, он может быть уверен,
что сделанные им изменения не будут отменены из-за какого-либо сбоя.
Уровень изолированности транзакций - значение, определяющее уровень, при
котором в транзакции допускаются несогласованные данные, то есть степень
изолированности одной транзакции от другой. Более высокий уровень
изолированности повышает точность данных, но при этом может снижаться
количество параллельно выполняемых транзакций. С другой стороны, более
низкий уровень изолированности позволяет выполнять больше параллельных
транзакций, но снижает точность данных.
При параллельном выполнении транзакций возможны такие проблемы:
● Потерянное обновление (lost update) — при одновременном изменении
одного блока данных разными транзакциями, одно из изменений теряется;
● Грязное чтение (dirty read) — чтение данных, добавленных или изменённых
транзакцией, которая впоследствии не подтвердится (откатится);

68
● Неповторяющееся чтение (non-repeatable read) — при повторном чтении в
рамках одной транзакции, ранее прочитанные данные оказываются
изменёнными;
● Фантомное чтение (phantom reads) — Одна транзакция в ходе своего
выполнения несколько раз выбирает множество строк по одним и тем же
критериям. Другая транзакция в интервалах между этими выборками добавляет
или удаляет строки или изменяет столбцы некоторых строк, используемых в
критериях выборки первой транзакции, и успешно заканчивается. В результате
получится, что одни и те же выборки в первой транзакции дают разные
множества строк.
Уровни изоляции транзакций:
● Serializable (упорядочиваемость) Самый высокий уровень изолированности;
транзакции полностью изолируются друг от друга. Только на этом уровне
параллельные транзакции не подвержены эффекту "фантомного чтения".
● Repeatable read (повторяемость чтения) Уровень, при котором чтение одной и
той же строки или строк в транзакции дает одинаковый результат. (Пока
транзакция не завершена, никакие другие транзакции не могут модифицировать
эти данные.)
● Read committed (чтение фиксированных данных). Завершенное чтение, при
котором отсутствует черновое, «грязное» чтение (то есть чтение одним
пользователем данных, которые не были зафиксированы в БД командой
COMMIT). Тем не менее в процессе работы одной транзакции другая может быть
успешно завершена и сделанные ею изменения зафиксированы. В итоге первая
транзакция будет работать с другим набором данных. Это проблема
неповторяемого чтения.
● Read uncommitted (чтение незафиксированных данных). Низший уровень
изоляции, соответствующий уровню 0. Он гарантирует только отсутствие
потерянных обновлений. Если несколько транзакций одновременно пытались
изменять одну и ту же строку, то в окончательном варианте строка будет иметь
значение, определенное последней успешно выполненной транзакцией.

91. Что такое БД, СУБД, реляционная модель данных.

База данных — организованный и адаптированный для обработки


вычислительной системой набор информации.
Система управления базами данных (СУБД) - набор средств общего или
специального назначения,
обеспечивающий создание, доступ к материалам и управление базой данных.
Основные функции СУБД:
- управление данными
- журнализация изменений данных
- резервное копирование и восстановление данных;

69
- поддержка языка определения данных и манипулирования ими.
Реляционная модель данных —это логическая модель данных и прикладная
теория построения
реляционных баз данных.
Реляционная модель данных включает в себя следующие компоненты:
Структурный аспект — данные представляют собой набор отношений.
Аспект целостности — отношения отвечают определенным условиям
целостности: уровня домена (типа данных), уровня отношения и уровня базы
данных.
Аспект обработки (манипулирования) —поддержка операторов манипулирования
отношениями (реляционная алгебра, реляционное исчисление).
Нормальная форма - свойство отношения в реляционной модели данных,
характеризующее его с точки зрения избыточности и определённое как
совокупность требований, которым должно удовлетворять отношение.

92.Ключи, первичный, внешний, простой, составной и т.д.

Простой ключ состоит из одного атрибута (поля). Составной - из двух и более.


Потенциальный ключ - простой или составной ключ, который уникально
идентифицирует каждую запись набора данных. При этом потенциальный ключ
должен обладать критерием неизбыточности: при удалении любого из полей
набор полей перестает уникально идентифицировать запись. Из множества всех
потенциальных ключей набора данных выбирают первичный ключ, все
остальные ключи называют альтернативными.
Что такое первичный ключ (primary key)? Каковы критерии его выбора
Первичный ключ (primary key) в реляционной модели данных один из
потенциальных ключей отношения, выбранный в качестве основного ключа
(ключа по умолчанию).
Если в отношении имеется единственный потенциальный ключ, он является и
первичным ключом. Если потенциальных ключей несколько, один из них
выбирается в качестве первичного, а другие называют ≪альтернативными≫. В
качестве первичного обычно выбирается тот из потенциальных ключей, который
наиболее удобен. Поэтому в качестве первичного ключа, как правило, выбирают
тот, который имеет наименьший размер (физического хранения) и/или включает
наименьшее количество атрибутов. Другой критерий выбора первичного ключа —
сохранение его уникальности со временем. Поэтому в качестве первичного ключа
стараются выбирать такой потенциальный ключ, который с наибольшей
вероятностью никогда не утратит уникальность.
Что такое внешний ключ (foreign key)?
Внешний ключ (foreign key) —подмножество атрибутов некоторого отношения A,
значения которых должны совпадать со значениями некоторого потенциального
ключа некоторого отношения B.

70
93.Что такое нормализация

Нормализация - это процесс преобразования отношений базы данных к виду,


отвечающему нормальным формам (пошаговый, обратимый процесс замены
исходной схемы другой схемой, в которой наборы данных имеют более простую и
логичную структуру). Нормализация предназначена для приведения структуры
базы данных к виду, обеспечивающему минимальную логическую избыточность,
и не имеет целью уменьшение или увеличение производительности работы или
же уменьшение или увеличение физического объёма базы данных. Конечной
целью нормализации является уменьшение потенциальной противоречивости
хранимой в базе данных информации.

Какие существуют нормальные формы


Первая нормальная форма (1NF) - Отношение находится в 1NF, если значения
всех его атрибутов атомарны (неделимы).
Вторая нормальная форма (2NF) - Отношение находится в 2NF, если оно
находится в 1NF, и при
этом все неключевые атрибуты зависят только от ключа целиком, а не от какой-
то его части.
Третья нормальная форма (3NF) - Отношение находится в 3NF, если оно
находится в 2NF и все
неключевые атрибуты не зависят друг от друга.
Четвёртая нормальная форма (4NF) - Отношение находится в 4NF , если оно
находится в 3NF и
если в нем не содержатся независимые группы атрибутов, между которыми
существует отношение ≪многие-ко-многим≫.
Пятая нормальная форма (5NF) - Отношение находится в 5NF, когда каждая
нетривиальная зависимость соединения в ней определяется потенциальным
ключом (ключами) этого отношения.
Шестая нормальная форма (6NF) - Отношение находится в 6NF, когда она
удовлетворяет всем нетривиальным зависимостям соединения, т.е. когда она
неприводима, то есть не может быть подвергнута дальнейшей декомпозиции без
потерь. Каждая переменная отношения, которая находится в 6NF, также
находится и в 5NF. Введена как обобщение пятой нормальной формы для
хронологической базы данных.
Нормальная форма Бойса-Кодда, усиленная 3 нормальная форма (BCNF) -
Отношение находится в BCNF, когда каждая её нетривиальная и неприводимая
слева функциональная зависимость имеет в качестве своего детерминанта
некоторый потенциальный ключ.
Доменно-ключевая нормальная форма (DKNF) - Отношение находится в
DKNF, когда каждое наложенное на неё ограничение является логическим

71
следствием ограничений доменов и ограничений ключей, наложенных на данное
отношение.

72
SQL
94.Что такое SQL

SQL, Structured query language (≪язык структурированных запросов≫) —


формальный непроцедурный язык программирования, применяемый для
создания, модификации и управления данными в произвольной реляционной
базе данных, управляемой соответствующей системой управления базами
данных (СУБД).
Какие существуют операторы SQL?
DDL, Data Definition Language (Операторы определения данных):
CREATE создает объект БД (базу, таблицу, представление, пользователя и т. д.);
ALTER изменяет объект;
DROP удаляет объект.
DML, Data Manipulation Language (Операторы манипуляции данными):
SELECT выбирает данные, удовлетворяющие заданным условиям;
INSERT добавляет новые данные;
UPDATE изменяет существующие данные;
DELETE удаляет данные.
DCL, Data Control Language (Операторы определения доступа к данным):
GRANT предоставляет пользователю (группе) разрешения на определенные
операции с объектом;
REVOKE отзывает ранее выданные разрешения;
DENY задает запрет, имеющий приоритет над разрешением.
TCL, Transaction Control Language (Операторы управления транзакциями):
COMMIT применяет транзакцию;
ROLLBACK откатывает все изменения, сделанные в текущей транзакции;
SAVEPOINT разбивает транзакцию на более мелкие.
Что означает NULL в SQL?
NULL - специальное значение (псевдозначение), которое может быть записано в
поле таблицы базы данных. NULL соответствует понятию ≪пустое поле≫, то
есть ≪поле, не содержащее никакого значения≫. NULL означает отсутствие,
неизвестность информации. Значение NULL не является значением в полном
смысле слова: по определению оно означает отсутствие значения и не
принадлежит ни одному типу данных. Поэтому NULL не равно ни логическому
значению FALSE, ни пустой строке, ни 0. При сравнении NULL с любым
значением будет получен результат NULL, а не FALSE и не 0. Более того, NULL
не равно NULL!

95. Временная таблица. Представление. Для чего используются.

73
Временная таблица - это объект базы данных, который хранится и управляется
системой базы данных на временной основе. Они могут быть локальными или
глобальными. Используется для сохранения результатов вызова хранимой
процедуры, уменьшение числа строк при соединениях, агрегирование данных из
различных источников или как замена курсоров и параметризованных
представлений.
Что такое «представление»(view) и для чего оно применяется
Представление, View - виртуальная таблица, представляющая данные одной или
более таблиц альтернативным образом. В действительности представление –
всего лишь результат выполнения оператора SELECT, который хранится в
структуре памяти, напоминающей SQL таблицу. Они работают в запросах и
операторах DML точно так же, как и основные таблицы, но не содержат никаких
собственных данных. Представления значительно расширяют возможности
управления данными. Это способ дать публичныйдоступ к некоторой (но не всей)
информации в таблице.

96. Каков общий синтаксис оператора SELECT?

SELECT - оператор DML SQL, возвращающий набор данных (выборку) из базы


данных, удовлетворяющих заданному условию. Имеет следующую структуру:
SELECT
[DISTINCT | DISTINCTROW | ALL]
select_expression,...
FROM table_references
[WHERE where_definition]
[GROUP BY {unsigned_integer | column | formula}]
[HAVING where_definition]
[ORDER BY {unsigned_integer | column | formula} [ASC | DESC], …]

97.Что такое JOIN?

JOIN - оператор языка SQL, который является реализацией операции соединения


реляционной алгебры. Предназначен для обеспечения выборки данных из двух
таблиц и включения этих данных в один результирующий набор.
Особенностями операции соединения являются следующее: в схему
таблицы-результата входят столбцы обеих исходных таблиц (таблиц-операндов),
то есть схема результата является ≪сцеплением≫ схем операндов;
каждая строка таблицы-результата является ≪сцеплением≫ строки из одной
таблицы-операнда со строкой второй таблицы-операнда; при необходимости
соединения не двух, а нескольких таблиц, операция соединения применяется
несколько раз (последовательно).
SELECT field_name [,... n]

74
FROM Table1
{INNER | {LEFT | RIGHT | FULL} OUTER | CROSS } JOIN Table2
{ON <condition> | USING (field_name [,... n])}
Какие существуют типы JOIN?
(INNER) JOIN Результатом объединения таблиц являются записи, общие для
левой и правой таблиц. Порядок таблиц для оператора не важен, поскольку
оператор является симметричным.
LEFT (OUTER) JOIN Производит выбор всех записей первой таблицы и
соответствующих им записей второй таблицы. Если записи во второй таблице не
найдены, то вместо них подставляется пустой результат (NULL). Порядок таблиц
для оператора важен, поскольку оператор не является симметричным.
RIGHT (OUTER) JOIN - LEFT JOIN с операндами, расставленными в обратном
порядке. Порядок таблиц для оператора важен, поскольку оператор не является
симметричным.
FULL (OUTER) JOIN Результатом объединения таблиц являются все записи,
которые присутствуют в таблицах. Порядок таблиц для оператора не важен,
поскольку оператор является симметричным.
CROSS JOIN (декартово произведение) При выборе каждая строка одной
таблицы объединяется с каждой строкой второй таблицы, давая тем самым все
возможные сочетания строк двух таблиц. Порядок таблиц для оператора не
важен, поскольку оператор является симметричным.
Что лучше использовать - JOIN или подзапросы
Обычно лучше использовать JOIN, поскольку в большинстве случаев он более
понятен и лучше оптимизируется СУБД (но 100% этого гарантировать нельзя).
Так же JOIN имеет заметное преимущество над подзапросами в случае, когда
список выбора SELECT содержит столбцы более чем из одной таблицы.
Подзапросы лучше использовать в случаях, когда нужно вычислять агрегатные
значения и использовать их для сравнений во внешних запросах.

75
JDBC
98.Что такое JDBC, преимущества его использования.

JDBC, Java DataBase Connectivity (соединение с базами данных на Java) —


промышленный стандарт взаимодействия Java-приложений с различными СУБД.
Реализован в виде пакета [Link], входящего в состав Java SE. JDBC основан на
концепции драйверов, которые позволяют получать соединение с базой данных
по специально описанному URL. При загрузке драйвер регистрирует себя в
системе и в дальнейшем автоматически вызывается, когда программа требует
URL, содержащий протокол, за который этот драйвер отвечает.

В чем заключаются преимущества использования JDBC?


Преимуществами JDBC считают: Лёгкость разработки: разработчик может не
знать специфики базы данных, с которой работает; Код практически не меняется,
если компания переходит на другую базу данных (количество изменений зависит
исключительно от различий между диалектами SQL); Не нужно дополнительно
устанавливать клиентскую программу; К любой базе данных можно
подсоединиться через легко описываемый URL.

Что из себя представляет JDBC URL?


JDBC URL состоит из:
<protocol>: (протокола) - всегда jdbc:.
<subprotocol>: (подпротокола) - это имя драйвера или имя механизма соединения
с базой данных.
Подпротокол может поддерживаться одним или несколькими драйверами.
Лежащий на поверхности пример подпротокола - это ≪odbc≫, отведенный для
URL, обозначающих имя источника данных ODBC. В случае необходимости
использовать сервис имен (т.е. имя базы данных в JDBC URL не будет
действительным именем базы данных), то подпротоколом может выступать
сервис имен.
<subname> (подимени) - это идентификатор базы данных. Значение подимени
может меняться в зависимости от подпротокола, и может также иметь под-
подимя с синтаксисом, определяемым разработчиком драйвера. Назначение
подимени - это предоставление всей информации, необходимой для поиска базы
данных. Например, если база данных находится в Интернет, то в состав
подимени JDBC URL должен быть включен сетевой адрес, подчиняющийся
следующим соглашениям: //<hostname>:<port>/<subsubname.
Пример JDBC URL для подключения к MySQL базе данных ≪Test ≫
расположенной по адресу localhost и ожидающей соединений по порту 3306:
jdbc:mysql://localhost:3306/Test

76
Из каких частей состоит JDBC?
JDBC состоит из двух частей:
- JDBC API, который содержит набор классов и интерфейсов, определяющих
доступ к базам данных. Эти классы и методы объявлены в двух пакетах - [Link]
и [Link];
- JDBC-драйвер, компонент, специфичный для каждой базы данных.
JDBC превращает вызовы уровня API в ≪родные≫ команды того или иного
сервера баз данных.

Перечислите основные классы и интерфейсы JDBC.


[Link] - позволяет загрузить и зарегистрировать необходимый
JDBC-драйвер, а
затем получить соединение с базой данных.
[Link] - решает те же задачи, что и DriverManager, но более
удобным и универсальным образом. Существуют также
[Link] и [Link] задача которых -
обеспечение поддержки пула соединений.
[Link] - обеспечивает формирование запросов к источнику данных и
управление транзакциями. Также предусмотрены интерфейсы
[Link] и [Link].
[Link] , [Link] и [Link] - эти
интерфейсы позволяют отправить запрос к источнику данных.
[Link] - объявляет методы, которые позволяют перемещаться по
набору данных и считывать значения отдельных полей в текущей записи.
[Link] - позволяет получить информацию о структуре набора
данных.
[Link] - позволяет получить информацию о структуре
источника данных.

99.Основные этапы работы с БД при использовании JDBC.

1. Регистрация драйверов;
2. Установление соединения с базой данных;
3. Создание запроса(ов) к базе данных;
4. Выполнение запроса(ов) к базе данных;
5. Обработка результата(ов);
6. Закрытие соединения с базой данных.

Как зарегистрировать драйвер JDBC?


Регистрацию драйвера можно осуществить несколькими способами:
[Link](%объект класса драйвера%).

77
[Link](≪полное имя класса драйвера≫).newInstance().
[Link](≪полное имя класса драйвера≫);
Какустановитьсоединениесбазойданных
Для установки соединения с базой данных используется статический вызов
[Link].
getConnection(...) .
В качестве параметра может передаваться:
URL базы данных
static Connection getConnection(String url)
URL базы данных и набор свойств для инициализации
static Connection getConnection(String url, Properties info)
URL базы данных, имя пользователя и пароль
static Connection getConnection(String url, String user, String password)
В результате вызова будет установлено соединение с базой данных и создан
объект класса [Link].
Connection - своеобразная ≪сессия≫, внутри контекста которой и будет
происходить дальнейшая работа с базой данных.

100. Какие уровни изоляции транзакций поддерживаются в JDBC?

Уровень изолированности транзакций —значение, определяющее уровень, при


котором в транзакции допускаются несогласованные данные, то есть степень
изолированности одной транзакции от другой. Более высокий уровень
изолированности повышает точность данных, но при этом может снижаться
количество параллельно выполняемых транзакций. С другой стороны, более
низкий уровень изолированности позволяет выполнять больше параллельных
транзакций, но снижает точность данных.
Во время использования транзакций, для обеспечения целостности данных,
СУБД использует блокировки, чтобы заблокировать доступ других обращений к
данным, участвующим в транзакции.
Такие блокировки необходимы, чтобы предотвратить:
- ≪грязное≫ чтение (dirty read) —чтение данных, добавленных или изменённых
транзакцией, которая впоследствии не подтвердится (откатится);
- неповторяющееся чтение (non-repeatable read) — при повторном чтении в
рамках одной транзакции ранее прочитанные данные оказываются изменёнными;
- фантомное чтение (phantom reads) — ситуация, когда при повторном чтении в
рамках одной транзакции одна и та же выборка дает разные множества строк.
Уровни изоляции транзакций определены в виде констант интерфейса
[Link]:
-TRANSACTION_NONE – драйвер не поддерживает транзакции;
-TRANSACTION_READ_UNCOMMITTED – позволяет транзакциям видеть
несохраненные из-

78
менения данных: разрешает грязное, непроверяющееся и фантомное чтения;
-TRANSACTION_READ_COMMITTED – любое изменение, сделанное в
транзакции, не видно
вне неё, пока она не сохранена: предотвращает грязное чтение, но разрешает
непроверяющееся и
фантомное;
- TRANSACTION_REPEATABLE_READ –запрещает грязное и непроверяющееся,
фантомное чтение разрешено;
- TRANSACTION_SERIALIZABLE –грязное, непроверяющееся и фантомное
чтения запрещены.
Важно: Сервер базы данных может не поддерживать все уровни изоляции.
Интерфейс [Link] предоставляет информацию об уровнях
изолированности транзакций, которые поддерживаются данной СУБД. Уровень
изоляции транзакции используемый СУБД можно задать с помощью метода
setTransactionIsolation() объекта [Link]. Получить информацию о
применяемом уровне изоляции поможет метод getTransactionIsolation().
/*************************************************************************************************************************/

101. При помощи чего формируются запросы к базе данных

Для выполнения запросов к базе данных в Java используются три интерфейса:


[Link] - для операторов SQL без параметров;
[Link] - для операторов SQL с параметрами и часто
выполняемых операторов;
[Link] - для исполнения хранимых в базе процедур.
Объекты-носители интерфейсов создаются при помощи методов объекта
[Link]:
[Link]() возвращает объект Statement;
[Link]() возвращает объект PreparedStatement;
[Link]() возвращает объект CallableStatement;
/*************************************************************************************************************************/

102. Чем отличается Statement от PreparedStatement?

Statement: используется для простых случаев запроса без параметров.


PreparedStatement: предварительно компилирует запрос, который может
содержать входные параметры и выполняться несколько раз с разным набором
этих параметров. Перед выполнением СУБД разбирает каждый запрос,
оптимизирует его и создает ≪план≫ (query plan) его выполнения. Если один и
тот же запрос выполняется несколько раз, то СУБД в состоянии кэшировать план
его выполнения и не производить этапов разборки и оптимизации повторно. Бла-
годаря этому запрос выполняется быстрее. Суммируя: PreparedStatement
выгодно отличается от Statement тем, что при повторном использовании с одним

79
или несколькими наборами параметров позволяет получить преимущества
заранее прекомпилированного и кэшированного запроса, помогая при этом
избежать SQL Injection.
/*************************************************************************************************************************/

103. Запрос к базе данных и обработка результатов

Выполнение запросов осуществляется при помощи вызова методов объекта,


реализующего интерфейс [Link]:
executeQuery() - для запросов, результатом которых является один набор
значений, например запросов SELECT. Результатом выполнения является
объект класса [Link];
executeUpdate() - для выполнения операторов INSERT, UPDATE или DELETE, а
также для операторов DDL (Data Definition Language). Метод возвращает целое
число, показывающее, сколько записей было модифицировано;
execute() –исполняет SQL-команды, которые могут возвращать различные
результаты. Например, может использоваться для операции CREATE TABLE.
Возвращает true, если первый результат содержит ResultSet и false, если первый
результат - это количество модифицированных записей или результат
отсутствует. Чтобы получить первый результат необходимо вызвать метод
getResultSet() или getUpdateCount(). Остальные результаты доступны через
вызов getMoreResults(), который при необходимости может быть произведён
многократно. Объект с интерфейсом [Link] хранит в себе результат
запроса к базе данных — некий набор данных, внутри которого есть курсор,
указывающий на один из элементов набора данных - текущую запись. Используя
курсор можно перемещаться по набору данных при помощи метода next().
Важно: Сразу после получения набора данных его курсор находится перед
первой записью и чтобы сделать её текущей необходимо вызвать метод next().
Содержание полей текущей записи доступно через вызовы методов getInt(),
getFloat(), getString(), getDate() и им подобных.

80
Тестирование
104. Что такое модульное тестирование (Unit testing)

Модульное/компонентное тестирование (unit testing) - процесс в


программировании, позволяющий проверить на корректность отдельные модули
исходного кода программы. Идея состоит в том, чтобы писать тесты для каждой
нетривиальной функции или метода. Это позволяет достаточно быстро
проверить, не привело ли очередное изменение кода к регрессии, то есть к
появлению ошибок в уже оттестированных местах программы, а также облегчает
обнаружение и устранение таких ошибок.
Модульные тесты можно условно поделить на две группы:
1. Тесты состояния (state based), проверяющие что вызываемый метод объекта
отработал корректно, проверяя состояние тестируемого объекта после вызова
метода.
2. Тесты взаимодействия (interaction tests), в которых тестируемый объект
производит манипуляции с другими объектами. Применяются, когда требуется
удостовериться, что тестируемый объект корректно взаимодействует с другими
объектами.
/*************************************************************************************************************************/

105. Что такое интеграционное тестирование

Интеграционное тестирование (integration testing) —это тестирование,


проверяющие работоспособность двух или более модулей системы в
совокупности — то есть нескольких объектов как единого блока. В тестах
взаимодействия же тестируется конкретный, определенный объект и то, как
именно он взаимодействует с внешними зависимостями.
Чем интеграционное тестирование отличается от модульного. С
технологической точки зрения интеграционное тестирование является
количественным развитием модульного, поскольку так же, как и модульное
тестирование, оперирует интерфейсами модулей и подсистем и требует
создания тестового окружения, включая заглушки на месте отсутствующих
модулей. Основная разница между модульным и интеграционным тестированием
состоит в целях, то есть в типах обнаруживаемых дефектов, которые, в свою
очередь, определяют стратегию выбора входных данных и методов анализа.
Допустим, есть класс, который при определенных условиях взаимодействует с
web-сервисом через зависимый объект. И нам надо проверить, что
определенный метод зависимого объекта действительно вызывается. Если в
качестве зависимого класса передать:

81
- реальный класс, работающий с web-сервисом, то это будет интеграционное
тестирование.
- заглушку, то это будет тестирование состояния.
- шпиона, а в конце теста проверить, что определенный метод зависимого
объекта действительно был вызван, то это будет тест взаимодействия.
/*************************************************************************************************************************/

106. Какие существуют виды тестовых объектов

- пустышка (dummy) - объект, который обычно передается в тестируемый класс в


качестве параметра, но не имеет поведения: с ним ничего не происходит и
никакие его методы не вызываются.
Примером dummy-объектов являются new object(), null, ≪Ignored String≫ и т.д.
- фальшивка (fake object) применяется в основном для ускорения запуска
ресурсоёмких тестов и является заменой тяжеловесного внешнего зависимого
объекта его легковесной реализацией. Основные примеры —эмулятор базы
данных (fake database) или фальшивый web-сервис.
- заглушка (test stub) используется для получения данных из внешней
зависимости, подменяя её. При этом заглушка игнорирует все данные
поступающие из тестируемого объекта, возвращая заранее определённый
результат. Тестируемый объект использует чтение из конфигурационного
файла? Тогда передаем ему заглушку ConfigFileStub возвращающую тестовые
строки конфигурации без обращения к файловой системе.
- шпион (test spy) - разновидность заглушки, которая умеет протоколировать
сделанные к ней обращения из тестируемой системы, чтобы проверить их
правильность в конце теста. При этом фиксируется количество, состав и
содержание параметров вызовов. Если существует необходимость проверки, что
определённый метод тестируемого класса вызывался ровно 1 раз, то шпион -
именно то, что нам нужно.
- фикция (mock object) похож на шпиона, но обладает расширенной
функциональностью, заранее заданными поведением и реакцией на вызовы.
/*************************************************************************************************************************/

107. Чем stub отличается от mock?

Stub используется как заглушка сервисов, методов, классов и т.д. с заранее


запрограммированным ответом на вызовы.
Mock использует подмену результатов вызова, проверяет сам факт
взаимодействия, протоколирует и контролирует его.
Что такое фикстуры
Фикстуры (fixtures) - состояние среды тестирования, которое требуется для
успешного выполнения теста. Основная задача фикстур заключается в
подготовке тестового окружения с заранее фиксированным/известным
состоянием, чтобы гарантировать повторяемость процесса тестирования.

82
Какие аннотации фикстур существуют в JUnit?
@BeforeClass - определяет код, который должен единожды выполниться перед
запуском набора тестовых методов.
@AfterClass - код, выполняемый один раз после исполнения набора тестовых
методов.
@Before - определяет код, который должен выполняться каждый раз перд
запуском любого тестовым методом.
@After - код, выполняемый каждый раз после исполнения любого тестового
метода.
Для чего в JUnit используется аннотация @Ignore?
@Ignore указывает JUnit на необходимость пропустить данный тестовый метод.

83
Журналирование
108. Какие существуют типы логов, из чего состоит система log4j.
- Лог (журнал) Системы (System);
- Лог Безопасности (Security);
- Лог Приложения (Application, Buisness).
Пользователь входит в приложение, проверяется пароль. Это действие
относится к безопасности (Security). Дальше он запускает какой-нибудь модуль.
Это событие уровня приложения (Application). Модуль при старте обращается к
другому модулю за какими-то дополнительными данными, производит какие-либо
еще вызовы –это уже системные действия (System).
Из каких частей состоит система журналирования log4j?
Система журналирования состоит из трёх основных частей:
- управляющей журналированием - logger;
- добавляющей в журнал - appender;
- определяющей формат добавления — layout.
Что такое Logger в log4j? Logger - это объект класса [Link],
который используется как управляющий интерфейс для журналирования
сообщений с возможностью задавать уровень детализации. Именно logger
проверяет нужно ли обрабатывать сообщение и если журналирование
необходимо, то сообщение передаётся в appender, если нет - система завершает
обработку данного сообщения.
Что такое Appender в log4j? Appender - это именованный объект журнала
событий, реализующий интерфейс [Link].log4j. Appender и добавляющий
события в журнал. Appender вызывает разные вспомогательные инструменты -
компоновщик, фильтр, обработчик ошибок (если они определены и необходимы).
В ходе этой работы окончательно устанавливается необходимость записи
сообщения, сообщению придаются окончательные содержание и форма.
В log4j журнал может представлять:
- консоль;
- файл;
- сокет;
- объект класса реализующего [Link] или [Link];
- JDBC хранилище;
- тему (topic) JMS;
- NT Event Log;
- SMTP;
- Syslog;
- Telnet.

84
Наиболее часто используемые log4j appenders:
[Link] - вывод в консоль;
[Link] - добавление в файл;
[Link] - добавление в файл с обновлением
файла через заданный промежуток времени;
[Link] - добавление в файл с обновлением файла
по достижению определенного размера;
[Link] — расширение
RollingFileAppender обновляющее файл по команде принятой с заданного порта;
[Link] - сообщение по SMTP;
[Link] - позволяет, используя отдельный поток,
организовать асинхронную работу, когда сообщения фиксируются лишь при
достижении определенного уровня заполненности промежуточного буфера.
[Link] - добавление в NT Event Log;
[Link] - добавление в Syslog;
[Link] - запись в хранилище JDBC;
[Link].log4j.lf5.LF5Appender - сообщение передаётся в специальный GUI
интерфейс LogFactor5
[Link] - трансляция сообщения по указанному
адресу и порту;
[Link] - рассылка сообщения сразу нескольким
удалённым серверам соединённым по заданному порту;
[Link] - отсылка сообщения по протоколу Telenet;
[Link] - добавление сообщения в JMS.
Что такое Layout в log4j?
Layout - наследник класса [Link] предоставляющий возможность
форматирования сообщения перед добавлением в журнал.
В log4j существуют следующие типы layout:
[Link] - на выходе получается строка содержащая лишь
уровень вывода и сообщение;
[Link] - форматирует сообщение в виде элемента HTML-
таблицы;
[Link] - компонует сообщение в виде XML формате;
[Link] - на выходе сообщение дополняется информацией о
времени, потоке, имени логгера и вложенном диагностическом контексте;
[Link] / [Link] -
настройка форматирования сообщения при помощи шаблона заданного
пользователем.
/*************************************************************************************************************************/

109. Уровни журналирования, порядок их приоритетности в log4j

85
OFF — отсутствие журналирования; TRACE – трассировка всех сообщений;
DEBUG — детальная информация для отладки; INFO — информация;
WARN — предупреждение; ERROR — ошибка; FATAL — фатальная ошибка.
Между уровнями логирования установлен следующий порядок приоритетов:
OFF < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < ALL
/*************************************************************************************************************************/

110. Какие существуют способы конфигурирования log4j?


Для того, чтобы log4j начал работать нужно предоставить ему конфигурацию. Это
можно сделать несколькими путями:
- Создать конфигурацию программно, т.е. получить logger, определить уровень
журналирования, прикрепить appender и задать способ форматирования.
- Указать файл или URL как аргумент при запуске java-машины -
[Link]=путь/к/файлу/конфигурации, а затем прочитать его в
программе при помощи [Link](…) /
[Link](...) для формата .properties или XML соответственно.
- Загрузить конфигурацию из файла в формате XML или .properties: log4j ищет
файл конфигурации в classpath. Сначала ищется файл [Link] и, если таковой
не найден, - файл [Link].

86
Patterns. Шаблоны, паттерны проектирования
/*************************************************************************************************************************/

111. Принципы SOLID. (описать и пояснить).


Single responsibility principle - Принцип единственной обязанности. На каждый
класс должна быть возложена одна-единственная обязанность.
Open/closed principle - Принцип открытости/закрытости. Программные сущности
должны быть открыты для расширения, но закрыты для изменения.
Liskov substitution principle - Принцип подстановки Барбары Лисков. Объекты в
программе могут быть заменены их наследниками без изменения свойств
программы. См. также контрактное программирование.
Interface segregation principle - Принцип разделения интерфейсов. Много
специализированных интерфейсов лучше, чем один универсальный.
Dependency inversion principle - Принцип инверсии зависимостей. Зависимости
внутри системы строятся на основе абстракций. Модули верхнего уровня не
зависят от модулей нижнего уровня. Абстракции не должны зависеть от деталей.
Детали должны зависеть от абстракций.
/*************************************************************************************************************************/

112. Что такое шаблоны (patterns), типы, основные характеристики


Шаблон проектирования (design pattern) — готовое к использованию и
проверенное решение. Это не класс и не библиотека, которую можно подключить
к проекту, это нечто большее - он не зависит от языка программирования, не
является законченным образцом, который может быть преобразован в код, он
может быть реализован по-разному в разных языках программирования.
Плюсы использования шаблонов:
- снижение сложности разработки за счёт готовых абстракций для решения
целого класса проблем.
- облегчение коммуникации между разработчиками, позволяя ссылаться на
известные шаблоны.
- унификация деталей решений: модулей и элементов проекта.
- возможность отыскав удачное решение, пользоваться им снова и снова.
- помощь в выборе выбрать наиболее подходящего варианта проектирования.
Минусы:
- слепое следование некоторому выбранному шаблону может привести к
усложнению программы.
- желание попробовать некоторый шаблон в деле без особых на то оснований.
Основные характеристики шаблонов:
- Имя - все шаблоны имеют уникальное имя, служащее для их идентификации;

87
- Назначение назначение данного шаблона;
- Задача - задача, которую шаблон позволяет решить;
- Способ решения - способ, предлагаемый в шаблоне для решения задачи в том
контексте, где этот шаблон был найден;
- Участники - сущности, принимающие участие в решении задачи;
- Следствия - последствия от использования шаблона как результат действий,
выполняемых в шаблоне;
- Реализация - возможный вариант реализации шаблона.
Типы шаблонов проектирования:
- Основные (Fundamental) - основные строительные блоки других шаблонов.
Большинство других шаблонов использует эти шаблоны в той или иной форме.
- Порождающие шаблоны (Creational) — шаблоны, абстрагирующие процесс
создания экземпляра. Они позволяют сделать систему независимой от способа
создания, композиции и представления объектов. Шаблон, порождающий классы,
использует наследование, чтобы изменять созданный объект, а шаблон,
порождающий объекты, делегирует создание объектов другому объекту.
- Структурные шаблоны (Structural) определяют различные сложные структуры,
которые изменяют интерфейс уже существующих объектов или его реализацию,
позволяя облегчить разработку и оптимизировать программу.
- Поведенческие шаблоны (Behavioral) определяют взаимодействие между
объектами, увеличивая таким образом его гибкость.
/*************************************************************************************************************************/

113. Приведите примеры основных шаблонов проектирования


- Делегирование (Delegation pattern) - Сущность внешне выражает некоторое
поведение, но в реальности передаёт ответственность за выполнение этого
поведения связанному объекту.
- Функциональный дизайн (Functional design) - гарантирует, что каждая
сущность имеет только одну обязанность и исполняет её с минимумом побочных
эффектов на другие.
- Неизменяемый интерфейс (Immutable interface) - неизменяемый объект.
- Интерфейс (Interface) - общий метод структурирования сущностей
облегчающий их понимание.
- Интерфейс-маркер (Marker interface) - В качестве атрибута (как пометки
объектной сущности) применяется наличие или отсутствие реализации
интерфейса-маркера. В современных языках программирования вместо этого
применяются атрибуты или аннотации.
- Контейнер свойств (Property container) - добавляет дополнительные свойства
сущности в контейнер внутри себя, вместо расширения новыми свойствами.
- Канал событий (Event channel) - Создаёт централизованный канал для
событий. Использует две сущности-представителя - для подписки и публикации
события в канале. Представитель существует отдельно от реального издателя

88
или подписчика. Подписчик может получать опубликованные события от более
чем одной сущности, даже если он зарегистрирован только на одном канале.
/*************************************************************************************************************************/

114. Приведите примеры порождающих шаблонов проектирования


- Абстрактная фабрика (Abstract factory) - Класс, который представляет собой
интерфейс для создания других классов.
- Строитель (Builder) - Класс, который представляет собой интерфейс для
создания сложного объекта.
- Фабричный метод (Factory method) - Делегирует создание объектов
наследникам родительского класса. Это позволяет использовать в коде
программы не специфические классы, а манипулировать абстрактными
объектами на более высоком уровне.
- Прототип (Prototype) - Определяет интерфейс создания объекта через
клонирование другого объекта вместо создания через конструктор.
- Одиночка (Singleton) - Класс, который может иметь только один экземпляр.
/*************************************************************************************************************************/

115. Приведите примеры структурных шаблонов проектирования


- Адаптер (Adapter) - Объект, обеспечивающий взаимодействие двух других
объектов, один из которых использует, а другой предоставляет несовместимый с
первым интерфейс.
- Мост (Bridge) - Структура, позволяющая изменять интерфейс обращения и
интерфейс реализа ции класса независимо.
- Компоновщик (Composite) - Объект, который объединяет в себе объекты,
подобные ему самому.
- Декоратор (Decorator) - Класс, расширяющий функциональность другого класса
без использования наследования.
- Фасад (Facade) - Объект, который абстрагирует работу с несколькими
классами, объединяя их в единое целое.
- Приспособленец (Flyweight) - обьект, представляющий себя как уникальный
экземпляр в разных местах программы, но по факту не являющийся таковым.
- Заместитель (Proxy) - Объект, который является посредником между двумя
другими объектами, и который реализует/ограничивает доступ к объекту, к
которому обращаются через него.
/*************************************************************************************************************************/

116. Приведите примеры поведенческих шаблонов проектирования


- Цепочка обязанностей (Chain of responsibility) - Предназначен для организации
в системе уровней ответственности.

89
- Команда (Command) - Представляет действие. Объект команды заключает в
себе само действии и его параметры.
- Интерпретатор (Interpreter) - Решает часто встречающуюся, но подверженную
изменениям, задачу.
- Итератор (Iterator) - Представляет собой объект, позволяющий получить
последовательныйдоступ к элементам объекта-агрегата без использования
описаний каждого из объектов, входящих в состав агрегации.
- Посредник (Mediator) - Обеспечивает взаимодействие множества объектов,
формируя при этом слабую связанность и избавляя объекты от необходимости
явно ссылаться друг на друга.
- Хранитель (Memento) - не нарушая инкапсуляцию фиксирует и сохраняет
состояния объекта так, чтобы позднее восстановить его в этих состояниях.
- Наблюдатель (Observer) - Определяет зависимость типа ≪один ко многим≫
между объектами таким образом, что при изменении состояния одного объекта
все зависящие от него оповещаются об этом событии.
- Состояние (State) - Используется, когда во время выполнения программы
объект должен менять своё поведение в зависимости от своего состояния.
- Стратегия (Strategy) - Предназначен для определения семейства алгоритмов,
инкапсуляции каждого из них и обеспечения их взаимозаменяемости.
- Шаблонный метод (Template method) - Определяет основу алгоритма и
позволяет наследникам переопределять некоторые шаги алгоритма, не изменяя
его структуру в целом.
- Посетитель (Visitor) - Описывает операцию, которая выполняется над
объектами других классов. При изменении класса Visitor нет необходимости
изменять обслуживаемые классы.

90
Основы WEB
117. Какие существуют уровни модели OSI?
# Уровень (layer) Тип данных (PDU) Функции Примеры
7 Прикладной (application) - Доступ к сетевым службам HTTP, FTP.
6 Представительский (presentation) - Представление и шифрование данных
ASCII, JPEG
5 Сеансовый(session) - Управление сеансом связи RPC, PAP
4 Транспортный (transport) Сегменты (segment)/ Дейтаграммы (datagram)
Прямая связь между конечными пунктами и надёжность TCP, UDP
3 Сетевой (network) Пакеты (packet) Определение маршрута и логическая
адресация IP, AppleTalk
2 Канальный (data link) Биты (bit)/Кадры (frame) Физическая адресация Ethernet,
IEEE 802.2, L2TP
1 Физический (physical) Биты (bit) Работа со средой передачи, сигналами и
двоичными данными USB, витая пара.

118. Что такое TCP/IP?


TCP/IP - это два основных сетевых протокола Internet. Часто это название
используют и для обозначения сетей, работающих на их основе.
IP (Internet Protocol) - маршрутизируемый протокол, отвечающий за IP-
адресацию, маршрутизацию, фрагментацию и восстановление пакетов. В его
задачу входит продвижение пакета между сетями – от одного маршрутизатора до
другого и тех пор, пока пакет не попадет в сеть назначения. В отличие от
протоколов прикладного и транспортного уровней, протокол IP разворачивается
не только на хостах, но и на всех маршрутизаторах. Этот протокол работает без
установления соединения и без гарантированной доставки. В настоящее время
используются следующие 2 версии протокола IP:
IPv6 — IP-адрес имеет разрядность 128 бит и записывается в виде восьми 16-
битных полей, с использованием шестнадцатеричной системы счисления и с
возможностью сокращения двух и более последовательных нулевых полей до ::,
например: 2001:db8:42::1337:cafe
IPv4 —IP-адрес имеет разрядность 32 бита и записывается в виде четырех
десятичных чисел в диапазоне 0 … 255 через точку, например: [Link].
TCP (Transfer Control Protocol) - протокол, обеспечивающий надежную,
требующую логического соединения связь между двумя компьютерами. Отвечает
за установление соединения, упорядочивание посылаемых пакетов и
восстановление пакетов, потерянных в процессе передачи.
Стек протоколов TCP/IP включает в себя четыре уровня:
- канальный уровень (link layer) - например Ethernet, IEEE 802.11 Wireless
Ethernet, физическая среда и принципы кодирования информации.

91
- сетевой уровень (Internet layer) - например IP
- транспортный уровень (transport layer) - например TCP, UDP
- прикладной уровень (application layer) - например HTTP, FTP, DNS
TCP-соединение двух узлов начинается с handshake (рукопожатия):
- Узел A посылает узлу B специальный пакет SYN —приглашение к соединению
- B отвечает пакетом SYN-ACK —согласием об установлении соединения
- A посылает пакет ACK —подтверждение, что согласие получено
После этого TCP соединение считается установленным и приложения,
работающие в этих узлах, могут посылать друг другу пакеты с данными.
В заголовке TCP/IP пакета указывается: IP-адрес отправителя, IP-адрес
получателя, Номер порта.

119. Что такое UDP?

UDP, User Datagram Protocol (Протокол пользовательских датаграмм) —протокол,


который обеспечивает доставку без требований соединения с удаленным
модулем UDP и обязательного подтверждения получения. К заголовку IP-пакета
UDP добавляет всего четыре поля по 2 байта каждое:
- поле порта источника (source port)
- поле порта пункта назначения (destination port)
- поле длины (length)
- поле контрольной суммы (checksum)
Поля «порт источника» и «контрольная сумма» не являются обязательными для
использования в IPv4. В IPv6 необязательно только поле «порт отправителя».
UDP используется DNS, SNMP, DHCP и другими приложениями.

120. Чем отличаются TCP и UDP?

TCP — ориентированный на соединение протокол, что означает необходимость


"рукопожатия" для установки соединения между двумя хостами. Как только
соединение установлено, пользователи могут отправлять данные в обоих
направлениях.
Надёжность — TCP управляет подтверждением, повторной передачей и тайм-
аутом сообщений. Производятся многочисленные попытки доставить сообщение.
Если оно потеряется на пути, сервер вновь запросит потерянную часть. В TCP
нет ни пропавших данных, ни (в случае многочисленных тайм-аутов)
разорванных соединений.
Упорядоченность — если два сообщения последовательно отправлены, первое
сообщение достигнет приложения-получателя первым. Если участки данных
приходят в неверном порядке, TCP отправляет неупорядоченные данные в
буфер до тех пор, пока все данные не могут быть упорядочены и переданы
приложению.

92
Тяжеловесность — TCP необходимо три пакета для установки соединения
перед тем, как отправить данные. TCP следит за надёжностью и перегрузками.
Потоковость —данные читаются как поток байтов, не передается никаких
особых обозначений для границ сообщения или сегментов.
UDP — более простой, основанный на сообщениях протокол без установления
соединения. Протоколы такого типа не устанавливают выделенного соединения
между двумя хостами. Связь достигается путём передачи информации в одном
направлении от источника к получателю без проверки готовности или состояния
получателя.
Ненадёжность — когда сообщение посылается, неизвестно, достигнет ли оно
своего назначения — оно может потеряться по пути. Нет таких понятий как
подтверждение, повторная передача, тайм-аут.
Неупорядоченность — если два сообщения отправлены одному получателю, то
порядок их достижения цели не может быть предугадан.
Легковесность — никакого упорядочивания сообщений, никакого отслеживания
соединений и т.д. Это лишь транспортный уровень.
Датаграммы — пакеты посылаются по отдельности и проверяются на
целостность только еслиони прибыли. Пакеты имеют определенные границы,
которые соблюдаются после получения, то есть операция чтения на получателе
выдаст сообщение таким, каким оно было изначально послано.
Отсутствие контроля перегрузок — для приложений с большой пропускной
способностью существует шанс вызвать коллапс перегрузок, если только они не
реализуют меры контроля на прикладном уровне.

121. Что такое Web server?


Web server (Веб-сервер) —сервер, принимающий HTTP-запросы от клиентов и
выдающий им HTTP-ответы. Так называют как программное обеспечение,
выполняющее функции web-сервера, так и непосредственно компьютер, на
котором это программное обеспечение работает.
Web-серверы могут иметь различные дополнительные функции, например:
- автоматизация работы web-страниц;
- ведение журнала обращений пользователей к ресурсам;
- аутентификация и авторизация пользователей;
- поддержка динамически генерируемых страниц;
- поддержка HTTPS для защищённых соединений с клиентами.
Наиболее известные web-серверы: Apache, Microsoft IIS, nginx.

122. Что такое Web application?


Web application (Веб-приложение) - клиент-серверное приложение, в котором
клиентом выступает браузер, а сервером —web-сервер. Логика web application
распределена между сервером и клиентом, хранение данных осуществляется,
преимущественно, на сервере, а обмен информацией происходит по сети. Одним

93
из преимуществ такого подхода является тот факт, что клиенты не зависят от
конкретной операционной системы пользователя, поэтому web application
является кроссплатформенным сервисом.

123. Что такое Application server?

Application Server (Сервер приложений) —программа, представляющая собой


сервер, который занимается системной поддержкой приложений и обеспечивает
их жизненный цикл в соответствии с правилами, определёнными в
спецификациях. Может работать как полноценный самостоятельный web-сервер
или быть поставщиком страниц для другого web-сервера. Обеспечивает обмен
данными между приложениями и клиентами, берёт на себя выполнение таких
функций, как создание программной среды для функционирующего приложения,
идентификацию и авторизацию клиентов, организацию сессии для каждого из
них. Наиболее известные серверы приложений Java: Apache Tomcat, Jetty, JBoss,
GlassFish, IBM WebSphere, Oracle Weblogic.

124. Чем отличаются Web server и Application server?


Понятие web server относится скорее к способу передачи данных (конкретно, по
протоколу HTTP), в то время как понятие Application server относится к способу
выполнения этих самых приложений (конкретно, удаленная обработка запросов
клиентов при помощи каких-то программ, размещенных на сервере). Эти понятия
нельзя ставить в один ряд. Они обозначают разные признаки программы. Какие-
то программы удовлетворяют только одному признаку, какие-то - нескольким
сразу. Apache Tomcat умеет выполнять приложения? Да, значит он является
application server. Apache Tomcat умеет отдавать данные по HTTP? - Да.
Следовательно он является web server.
Возьмите какую-нибудь базу данных, в которой на хранимых процедурах описана
сложная логика и можно в ответ на SQL-запросы отправлять даже sms. Такую
базу данных можно назвать application server, но web server - уже нет, потому что
все это не работает с клиентом по HTTP протоколу. Возьмите чистый Apache, в
котором не включены никакие модули для поддержки языков программирования.
Он умеет отдавать только статичные файлы и картинки по протоколу HTTP. Это
web server, но не application server. Включите модуль для поддержки PHP и
разместите там программу на PHP, которая делает запросы к базе данных и
динамически формирует страницы. Теперь Apache стал и application server.

94
Spring
125. Что такое Spring Framework.

Spring Framework (или коротко Spring) — универсальный фреймворк с


открытым исходным кодом для Java-платформы. Несмотря на то, что Spring
Framework не обеспечивает какую-либо конкретную модель программирования,
он стал широко распространённым в Java-сообществе главным образом как
альтернатива и замена модели Enterprise JavaBeans. Spring Framework
предоставляет бо́льшую свободу Java-разработчикам в проектировании; кроме
того, он предоставляет хорошо документированные и лёгкие в использовании
средства решения проблем, возникающих при создании приложений
корпоративного масштаба. Обычно Spring описывают как облегченную
платформу для построения Java-приложений, но с этим утверждением связаны
два интересных момента. Во-первых, Spring можно использовать для построения
любого приложения на языке Java (т.е. автономных, веб приложений,
приложений JEE и т.д.), что отличает Spring от многих других платформ, таких как
Apache Struts, которая ограничена только веб-приложениями. Во-вторых,
характеристика “облегченная” в действительности не имеет никакого отношения к
количеству классов или размеру дистрибутива; напротив, она определяет
принцип всей философии Spring — минимальное воздействие. Платформа Spring
является облегченной в том смысле, что для использования ядра Spring вы
должны вносить минимальные (если вообще какие-либо) изменения в код своего
приложения, а если в какой-то момент вы решите больше не пользоваться
Spring, то и это сделать очень просто. Обратите внимание, что речь идет только
о ядре Spring — многие дополнительные компоненты Spring, такие как доступ к
данным, требуют более тесной привязки к Spring Framework.

126. Какие некоторые из важных особенностей и преимуществ Spring


Framework?

Spring Framework обеспечивает решения многих задач, с которыми сталкиваются


Java-разработчики и организации, которые хотят создать информационную
систему, основанную на платформе Java. Из-за широкой функциональности
трудно определить наиболее значимые структурные элементы, из которых он
состоит. Spring Framework не всецело связан с платформой Java Enterprise,
несмотря на его масштабную интеграцию с ней, что является важной причиной
его популярности.
Spring Framework, вероятно, наиболее известен как источник расширений
(features), нужных для эффективной разработки сложных бизнес-приложений вне

95
тяжеловесных программных моделей, которые исторически были
доминирующими в промышленности. Ещё одно его достоинство в том, что он
ввел ранее неиспользуемые функциональные возможности в сегодняшние
господствующие методы разработки, даже вне платформы Java. Этот фреймворк
предлагает последовательную модель и делает её применимой к большинству
типов приложений, которые уже созданы на основе платформы Java. Считается,
что Spring Framework реализует модель разработки, основанную на лучших
стандартах индустрии, и делает её доступной во многих областях Java. Таким
образом к достоинствам Spring можно отнести:
Относительная легкость в изучении и применении фреймворка в
разработке и поддержке приложения.
Внедрение зависимостей (DI) и инверсия управления (IoC) позволяют
писать независимые друг от друга компоненты, что дает преимущества в
командной разработке, переносимости модулей и т.д..
Spring IoC контейнер управляет жизненным циклом Spring Bean и
настраивается наподобие JNDI lookup (поиска).
Проект Spring содержит в себе множество подпроектов, которые
затрагивают важные части создания софта, такие как вебсервисы, веб
программирование, работа с базами данных, загрузка файлов, обработка
ошибок и многое другое. Всё это настраивается в едином формате и
упрощает поддержку приложения.

127. Что вы понимаете под Dependency Injection (DI)?

Внедрение зависимости (Dependency injection, DI) — процесс предоставления


внешней зависимости программному компоненту. Является специфичной
формой «инверсии управления» ( Inversion of control, IoC), когда она применяется
к управлению зависимостями. В полном соответствии с принципом единой
обязанности объект отдаёт заботу о построении требуемых ему зависимостей
внешнему, специально предназначенному для этого общему механизму. К
достоинствам применения DI можно отнести:
• Сокращение объема связующего кода. Одним из самых больших плюсов DI
является возможность значительного сокращения объема кода, который должен
быть написан для связывания вместе различных компонентов приложения.
Зачастую этот код очень прост — при создании зависимости должен создаваться
новый экземпляр соответствующего объекта.
• Упрощенная конфигурация приложения. За счет применения DI процесс
конфигурирования приложения значительно упрощается. Для конфигурирования
классов, которые могут быть внедрены в другие классы, можно использовать
аннотации или XML-файлы.
• Возможность управления общими зависимостями в единственном репозитории.
При традиционном подходе к управлению зависимостями в общих службах, к

96
которым относятся, например, подключение к источнику данных, транзакция,
удаленные службы и т.п., вы создаете экземпляры (или получаете их из
определенных фабричных классов) зависимостей там, где они нужны — внутри
зависимого класса. Это приводит к распространению зависимостей по множеству
классов в приложении, что может затруднить их изменение. В случае
использования DI вся информация об общих зависимостях содержится в
единственном репозитории (в Spring есть возможность хранить эту информацию
в XML-файлах или Java классах), что существенно упрощает управление
зависимостями и снижает количество возможных ошибок.
• Улучшенная возможность тестирования. Когда классы проектируются для DI,
становится возможной простая замена зависимостей. Это особенно полезно при
тестировании приложения.
• Стимулирование качественных проектных решений для приложений. Вообще
говоря, проектирование для DI означает проектирование с использованием
интерфейсов. Используя Spring, вы получаете в свое распоряжение целый ряд
средств DI и можете сосредоточиться на построении логики приложения, а не на
поддерживающей DI платформе.

128. Как реализуется DI в Spring Framework?

Реализация DI в Spring основана на двух ключевых концепциях Java —


компонентах JavaBean и интерфейсах. При использовании Spring в качестве
поставщика DI вы получаете гибкость определения конфигурации зависимостей
внутри своих приложений разнообразными путями (т.е. внешне в XML-файлах, с
помощью конфигурационных Java классов Spring или посредством аннотаций
Java в коде). Компоненты JavaBean (также называемые POJO (Plain Old Java
Object — простой старый объект Java)) предоставляют стандартный механизм
для создания ресурсов Java, которые являются конфигурируемыми множеством
способов. За счет применения DI объем кода, который необходим при
проектировании приложения на основе интерфейсов, снижается почти до нуля.
Кроме того, с помощью интерфейсов можно получить максимальную отдачу от
DI, потому что бины могут использовать любую реализацию интерфейса для
удовлетворения их зависимости.
К типам реализации внедрения зависимостей в Spring относят:
Constructor Dependency Injection — это тип внедрения зависимостей, при
котором зависимости компонента предоставляются ему в его конструкторе (или
конструкторах).
public class ConstructorInjection {
private Dependency dependency;
public ConstructorInjection(Dependency dependency) {
[Link] = dependency;
}

97
}
Setter Dependency Injection — контейнер IoC внедряет зависимости компонента
в компонент через методы установки в стиле JavaBean.

public class SetterInjection {


private Dependency dependency;
public void setDependency(Dependency dependency) {
[Link] = dependency;
}
}

129. Какие преимущества использования Spring Tool Suite?

Для упрощения процесса разработки основанных на Spring приложений в Eclipse


(наиболее часто используемая бесплатная IDE-среда для разработки Java-
приложений), в рамках Spring создан проект Spring IDE. Проект бесплатный. Он
интегрирован в Eclipse IDE, Spring IDE, Mylyn (среда разработки в Eclipse,
основанная на задачах), Maven for Eclipse, AspectJ Development Tool.

130. Приведите названия некоторых важных Spring модулей.

• Inversion of Control-контейнер: конфигурирование компонентов приложений и


управление жизненным циклом Java-объектов.
• Фреймворк аспектно-ориентированного программирования: работает с
функциональностью, которая не может быть реализована возможностями
объектно-ориентированного программирования на Java без потерь.
• Фреймворк доступа к данным: работает с системами управления
реляционными базами данных на Java-платформе, используя JDBC- и ORM-
средства и обеспечивая решения задач, которые повторяются в большом числе
Java-based environments.
• Фреймворк управления транзакциями: координация различных API управления
транзакциями и инструментарий настраиваемого управления транзакциями для
объектов Java.
• Фреймворк MVC: каркас, основанный на HTTP и сервлетах, предоставляющий
множество возможностей для расширения и настройки (customization). •
Фреймворк удалённого доступа: конфигурируемая передача Java-объектов через
сеть в стиле RPC, поддерживающая RMI, CORBA,HTTP-based протоколы,
включая web-сервисы (SOAP).
• Фреймворк аутентификации и авторизации: конфигурируемый инструментарий
процессов аутентификации и авторизации, поддерживающий много популярных и
ставших индустриальными стандартами протоколов, инструментов, практик через
дочерний проект Spring Security (ранее известный как Acegi).

98
• Фреймворк удалённого управления: конфигурируемое представление и
управление Java-объектами для локальной или удалённой конфигурации с
помощью JMX.
• Фреймворк работы с сообщениями: конфигурируемая регистрация объектов-
слушателей сообщений для прозрачной обработки сообщений из очереди
сообщений с помощью JMS, улучшенная отправка сообщений по стандарту JMS
API.
• Тестирование: каркас, поддерживающий классы для написания модульных и
интеграционных тестов.

99

Вам также может понравиться