Java Basic Questions V 4.1
Java Basic Questions V 4.1
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
/*************************************************************************************************************************/
7
/*************************************************************************************************************************/
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) во время выполнения.
/*************************************************************************************************************************/
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
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 может применяться к переменным, параметрам методов,
полям и методам класса или самим классам.
- Класс не может иметь наследников;
- Метод не может быть переопределен в классах наследниках;
- Поле не может изменить свое значение после инициализации;
- Параметры методов не могут изменять своё значение внутри метода;
- Локальные переменные не могут быть изменены после присвоения значения.
/*************************************************************************************************************************/
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
/*************************************************************************************************************************/
16
- для статических блоков будет выброшено [Link];
- для нестатических блоков будет проброшено исключение-источник.
Если исключение - наследник Error, то в обоих случаях будет [Link].
Исключение: [Link] - смерть потока, для него исключения нет.
/*************************************************************************************************************************/
17
Доступ к переопределенным методам родительского класса. С помощью
ключевого слова super можно обратиться к любому члену родительского класса -
методу или полю, если они не определены как private. [Link]();
/*************************************************************************************************************************/
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 неприменим.
/*************************************************************************************************************************/
19
31. Что такое локальный класс, каковы его особенности
Local inner class (Локальный класс) - это вложенный класс, который может
быть декларирован в любом блоке, в котором разрешается декларировать
переменные. Как и простые внутренние классы (Member inner class) локальные
классы имеют имена и могут использоваться многократно. Как и анонимные
классы, они имеют окружающий их экземпляр только в нестатическом контексте.
Локальные классы имеют следующие особенности:
- Видны только в пределах блока, в котором объявлены;
- Не могут быть объявлены как private/public/protected или static;
- Не могут иметь внутри себя статических объявлений методов и классов, но
могут иметь финальные статические поля, проинициализированные константой;
- Имеют доступ к полям и методам обрамляющего класса;
- Могут обращаться к локальным переменным и параметрам метода, если они
объявлены с модификатором final.
/*************************************************************************************************************************/
/*************************************************************************************************************************/
20
полю внешнего класса напрямую. В случае, если у вложенного класса уже
существует поле с таким же литералом, то обращаться к такому полю следует
через ссылку на его экземляр. Например: [Link].
/*************************************************************************************************************************/
/*************************************************************************************************************************/
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) хранится
в куче.
/*************************************************************************************************************************/
/*************************************************************************************************************************/
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, который позволяет использовать разные
алгоритмы для разных этапов сборки мусора. Этот подход опирается на том, что:
- большинство создаваемых объектов быстро становятся мусором;
- существует мало связей между объектами, которые были созданы в прошлом и
только что созданными объектами.
/*************************************************************************************************************************/
24
ссылки, т.е. ссылки, непосредственно существующей в выполняемом коде.
Память освобождается сборщиком мусора по его собственному ≪усмотрению≫.
Программа может успешно завершить работу, не исчерпав ресурсов свободной
памяти или даже не приблизившись к этой черте и поэтому ей так и не
потребуются ≪услуги≫ сборщика мусора.
Мусор собирается системой автоматически, без вмешательства пользователя
или программиста, но это не значит, что этот процесс не требует внимания вовсе.
Необходимость создания и удаления большого количества объектов
существенным образом сказывается на производительности приложений и если
быстродействие программы является важным фактором, следует тщательно
обдумывать решения, связанные с созданием объектов, — это, в свою очередь,
уменьшит и объем мусора, подлежащего утилизации.
/*************************************************************************************************************************/
/*************************************************************************************************************************/
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≫
может вызвать длительные паузы в работе приложения при использовании
больших объёмов памяти. Кроме того, последовательный алгоритм работы не
позволяет оптимально использовать вычислительные ресурсы компьютера, и
последовательный сборщик мусора может стать узким местом при работе
приложения на многопроцессорных машинах.
/*************************************************************************************************************************/
/*************************************************************************************************************************/
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
/*************************************************************************************************************************/
28
статической секции при загрузке класса Finalizer). Методы finalize() вызываются
последовательно в том порядке, в котором были добавлены в список сборщиком
мусора. Соответственно, если какой-то finalize() зависнет, он подвесит поток
Finalizer, но не сборщик мусора. Это в частности означает, что объекты, не
имеющие метода finalize(), будут исправно удаляться, а вот имеющие будут
добавляться в очередь, пока поток Finalizer не освободится, не завершится
приложение или не кончится память. То же самое применимо и выброшенным в
процессе finalize() исключениям: метод runFinalizer() у потока Finalizer игнорирует
все исключения выброшенные в момент выполнения finalize(). Таким образом
возникновение исключительной ситуации никак не скажется на
работоспособности сборщика мусора.
/*************************************************************************************************************************/
Результат: !! 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) - исключение, которое будет
выброшено при ошибке приведения типа.
/*************************************************************************************************************************/
/*************************************************************************************************************************/
31
47. Какие есть особенности класса String?
Это неизменяемый (immutable) и финализированный тип данных;
Все объекты класса String JVM хранит в пуле строк;
Объект класса String можно получить, используя двойные кавычки;
Можно использовать оператор + для конкатенации строк;
Начиная с Java 7, строки можно использовать в конструкции switch.
32
Что делает метод intern() в классе String? Он нужен для сохранения строки в
пуле строк или получения ссылки, если такая строка уже находится в пуле.
Можно ли использовать строки в конструкции switch? Да, начиная с Java 7 в
операторе switch можно использовать строки, ранние версии Java не
поддерживают этого. При этом: участвующие строки чувствительны к регистру;
используется метод equals() для сравнения полученного значения со значениями
case, поэтому во избежание NullPointerException стоит предусмотреть проверку
на null. согласно документации, Java 7 для строк в switch, компилятор Java
формирует более эффективный байткод для строк в конструкции switch, чем для
сцепленных условий if-else.
/*************************************************************************************************************************/
/*************************************************************************************************************************/
33
/*************************************************************************************************************************/
/*************************************************************************************************************************/
34
- Системный загрузчик (System/application). Загружает классы, пути к которым
указаны в переменной окружения CLASSPATH или пути, которые указаны в
командной строке запуска JVM после ключей -classpath или -cp. Системный
загрузчик реализован классом [Link]. Launcher$AppClassLoader.
Загрузчики классов являются иерархическими: каждый из них (кроме базового)
имеет родительский загрузчик и в большинстве случаев, перед тем как
попробовать загрузить класс самостоятельно, он посылает вначале запрос
родительскому загрузчику загрузить указанный класс. Такое делегирование
позволяет загружать классы тем загрузчиком, который находится ближе всего к
базовому в иерархии делегирования. Как следствие поиск классов будет
происходить в источниках в порядке их доверия: сначала в библиотеке Core API,
потом в папке расширений, потом в локальных файлах CLASSPATH.
35
- Множественные пространства имен. Каждый загрузчик имеет своё
пространство имён для создаваемых классов. Соответственно, классы,
загруженные двумя различными загрузчиками на основе общего байт-кода, в
системе будут различаться.
/*************************************************************************************************************************/
/*************************************************************************************************************************/
36
4. Непротиворечивость: для любых ссылок на значения х и у, если несколько
раз вызвать х.equals(y), постоянно будет возвращаться значение true либо
постоянно будет возвращаться значение false при условии, что никакая
информация, используемая при сравнении объектов, не поменялась.
5. Для любой ненулевой ссылки на значение х выражение х.equals(null) должно
возвращать false.
/*************************************************************************************************************************/
37
Могут ли у разных объектов (ref0 != ref1) быть [Link](ref1) == true? Да,
могут. Для этого в классе этих объектов должен быть переопределен метод
equals(). Если используется метод [Link](), то для двух ссылок x и y метод
вернет true тогда и только тогда, когда обе ссылки указывают на один и тот же
объект (т.е. x == y возвращает true).
Могут ли у разных ссылок на один объект(ref0 == ref1) быть
[Link](ref1) == false? В общем случае - могут, если метод equals()
реализован некорректно и не выполняет свойство рефлексивности: для любых
ненулевых ссылок x метод [Link](x) должен возвращать true.
/*************************************************************************************************************************/
38
уникальные значения для разных объектов (его вычисление основано на
использовании адреса объекта в памяти).
Есть класс Point {int x,y;}. Почему хэшкод вида 31*x+y предпочтительнее,
чем x + y? Множитель создает зависимость значения хэш кода от очередности
обработки полей, что в итоге порождает лучшую хэш функцию.
/*************************************************************************************************************************/
39
реализует интерфейс Cloneable и переопределяет метод clone(). Так как, если
это будет иначе вызов метода невозможен из-за его недоступности. Отсюда
следует, что если класс имеет суперкласс, то для реализации механизма
клонирования текущего класса-потомка необходимо наличие корректной
реализации такого механизма в суперклассе. При этом следует отказаться от
использования объявлений final для полей объектных типов по причине
невозможности изменения их значений при реализации клонирования.
Помимо встроенного механизма клонирования в Java для клонирования
объекта можно использовать:
1. Специализированный конструктор копирования - в классе описывается
конструктор, который принимает объект этого же класса и инициализирует поля
создаваемого объекта значениями полей переданного.
2. Фабричный метод - (Factory method), который представляет собой
статический метод, возвращающий экземпляр своего класса.
3. Механизм сериализации - сохранение и последующее восстановление
объекта в/из потока байтов.
/*************************************************************************************************************************/
40
2. Поля для клонирования указываются явно;
3. Возможность клонировать даже final поля.
/*************************************************************************************************************************/
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.
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: Происходит,
когда нет возможности создавать новые потоки.
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 будет выполнен всегда, независимо от того, выброшено исключение или
нет.
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).
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.
/*************************************************************************************************************************/
48
Какая разница между fail-fast и fail-safe? В противоположность fail-fast,
итераторы fail-safe не вызывают никаких исключений при изменении структуры,
потому что они работают с клоном коллекции вместо оригинала.
Примеры итераторов, реализующих поведение fail-safe. Итератор
коллекции CopyOnWriteArrayList и итератор представления keySet коллекции
ConcurrentHashMap являются примерами итераторов fail-safe.
/*************************************************************************************************************************/
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.
/*************************************************************************************************************************/
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.
52
JAVA 8
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"; }}
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]() ); }
}
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); } }
55
interface IFunction { Triangle Function(int a, int b, int c); }
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](); // здесь ошибка, так как поток уже употреблен
58
В целом, этот механизм похож на конструирования SQL запросов, может
быть сколько угодно вложенных Select'ов и только один результат в итоге.
Например, в выражении [Link]().filter((s) -> [Link](«1»)).skip(2).findFirst() ,
filter и skip — конвейерные, а findFirst — терминальный, он возвращает объект
Optional и это заканчивает работу со stream'ом.
/*************************************************************************************************************************/
4. Создание стрима из
(строка файла
файла-элемент стрима)
[Link](путь_к_файлу) Stream<String> strFromFiles = [Link]([Link]("[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 Вернуть последовательный, если он уже, может вернуть самого себя.
61
Потоки ввода-вывода в JAVA
82. В чём заключается разница между IO и NIO?
62
Сериализация
83.Что такое сериализация
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().
64
Конкуренция —это способ одновременного решения множества задач.
Признаки:
Наличие нескольких потоков управления (например Thread в Java, корутина в
Kotlin), если поток управления один, то конкурентного выполнения быть не может
Недетерминированный результат выполнения. Результат зависит от случайных
событий, реализации и того как была проведена синхронизация. Даже если
каждый поток полностью детерминированный, итоговый результат будет
недетерминированным.
Параллелизм — это способ выполнения разных частей одной задачи.
Признаки:
Необязательно имеет несколько потоков управления
Может приводить к детерминированному результату, так, например, результат
умножения каждого элемента массива на число, не изменится, если умножать его
по частям параллельно.
Что такое кооперативная многозадачность, какой тип многозадачности
использует Java и почему
Кооперативная многозадачность - это способ деления процессорного времени
между потоками, при котором каждый поток обязан отдавать управление
следующему добровольно. Преимущества такого подхода - простота
реализации, меньшие накладные расходы на переключение контекста.
Недостатки - если один поток завис или ведет себя некорректно, то зависает
целиком вся система и другие потоки никогда не получат управление.
Java использует вытесняющую многозадачность, при которой решение о
переключении между потоками процесса принимает операционная система.
В отличие от кооперативной многозадачности управление операционной системе
передаётся вне зависимости от состояния работающих приложений, благодаря
чему, отдельные зависшие потоки процесса, как правило, не ≪подвешивают ≫
всю систему целиком. За счёт регулярного переключения между задачами также
улучшается отзывчивость приложения и повышается оперативность
освобождения ресурсов, которые больше не используются.
В реализации вытесняющая многозадачность отличается от кооперативной, в
частности, тем, что требует обработки системного прерывания от аппаратного
таймера.
65
Servlets, JSP, JSTL
86.Что такое сервлет
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] Описание проекта
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. Он гарантирует только отсутствие
потерянных обновлений. Если несколько транзакций одновременно пытались
изменять одну и ту же строку, то в окончательном варианте строка будет иметь
значение, определенное последней успешно выполненной транзакцией.
69
- поддержка языка определения данных и манипулирования ими.
Реляционная модель данных —это логическая модель данных и прикладная
теория построения
реляционных баз данных.
Реляционная модель данных включает в себя следующие компоненты:
Структурный аспект — данные представляют собой набор отношений.
Аспект целостности — отношения отвечают определенным условиям
целостности: уровня домена (типа данных), уровня отношения и уровня базы
данных.
Аспект обработки (манипулирования) —поддержка операторов манипулирования
отношениями (реляционная алгебра, реляционное исчисление).
Нормальная форма - свойство отношения в реляционной модели данных,
характеризующее его с точки зрения избыточности и определённое как
совокупность требований, которым должно удовлетворять отношение.
70
93.Что такое нормализация
71
следствием ограничений доменов и ограничений ключей, наложенных на данное
отношение.
72
SQL
94.Что такое SQL
73
Временная таблица - это объект базы данных, который хранится и управляется
системой базы данных на временной основе. Они могут быть локальными или
глобальными. Используется для сохранения результатов вызова хранимой
процедуры, уменьшение числа строк при соединениях, агрегирование данных из
различных источников или как замена курсоров и параметризованных
представлений.
Что такое «представление»(view) и для чего оно применяется
Представление, View - виртуальная таблица, представляющая данные одной или
более таблиц альтернативным образом. В действительности представление –
всего лишь результат выполнения оператора SELECT, который хранится в
структуре памяти, напоминающей SQL таблицу. Они работают в запросах и
операторах DML точно так же, как и основные таблицы, но не содержат никаких
собственных данных. Представления значительно расширяют возможности
управления данными. Это способ дать публичныйдоступ к некоторой (но не всей)
информации в таблице.
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, преимущества его использования.
76
Из каких частей состоит JDBC?
JDBC состоит из двух частей:
- JDBC API, который содержит набор классов и интерфейсов, определяющих
доступ к базам данных. Эти классы и методы объявлены в двух пакетах - [Link]
и [Link];
- JDBC-драйвер, компонент, специфичный для каждой базы данных.
JDBC превращает вызовы уровня API в ≪родные≫ команды того или иного
сервера баз данных.
1. Регистрация драйверов;
2. Установление соединения с базой данных;
3. Создание запроса(ов) к базе данных;
4. Выполнение запроса(ов) к базе данных;
5. Обработка результата(ов);
6. Закрытие соединения с базой данных.
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 - своеобразная ≪сессия≫, внутри контекста которой и будет
происходить дальнейшая работа с базой данных.
78
менения данных: разрешает грязное, непроверяющееся и фантомное чтения;
-TRANSACTION_READ_COMMITTED – любое изменение, сделанное в
транзакции, не видно
вне неё, пока она не сохранена: предотвращает грязное чтение, но разрешает
непроверяющееся и
фантомное;
- TRANSACTION_REPEATABLE_READ –запрещает грязное и непроверяющееся,
фантомное чтение разрешено;
- TRANSACTION_SERIALIZABLE –грязное, непроверяющееся и фантомное
чтения запрещены.
Важно: Сервер базы данных может не поддерживать все уровни изоляции.
Интерфейс [Link] предоставляет информацию об уровнях
изолированности транзакций, которые поддерживаются данной СУБД. Уровень
изоляции транзакции используемый СУБД можно задать с помощью метода
setTransactionIsolation() объекта [Link]. Получить информацию о
применяемом уровне изоляции поможет метод getTransactionIsolation().
/*************************************************************************************************************************/
79
или несколькими наборами параметров позволяет получить преимущества
заранее прекомпилированного и кэшированного запроса, помогая при этом
избежать SQL Injection.
/*************************************************************************************************************************/
80
Тестирование
104. Что такое модульное тестирование (Unit testing)
81
- реальный класс, работающий с web-сервисом, то это будет интеграционное
тестирование.
- заглушку, то это будет тестирование состояния.
- шпиона, а в конце теста проверить, что определенный метод зависимого
объекта действительно был вызван, то это будет тест взаимодействия.
/*************************************************************************************************************************/
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] -
настройка форматирования сообщения при помощи шаблона заданного
пользователем.
/*************************************************************************************************************************/
85
OFF — отсутствие журналирования; TRACE – трассировка всех сообщений;
DEBUG — детальная информация для отладки; INFO — информация;
WARN — предупреждение; ERROR — ошибка; FATAL — фатальная ошибка.
Между уровнями логирования установлен следующий порядок приоритетов:
OFF < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < ALL
/*************************************************************************************************************************/
86
Patterns. Шаблоны, паттерны проектирования
/*************************************************************************************************************************/
87
- Назначение назначение данного шаблона;
- Задача - задача, которую шаблон позволяет решить;
- Способ решения - способ, предлагаемый в шаблоне для решения задачи в том
контексте, где этот шаблон был найден;
- Участники - сущности, принимающие участие в решении задачи;
- Следствия - последствия от использования шаблона как результат действий,
выполняемых в шаблоне;
- Реализация - возможный вариант реализации шаблона.
Типы шаблонов проектирования:
- Основные (Fundamental) - основные строительные блоки других шаблонов.
Большинство других шаблонов использует эти шаблоны в той или иной форме.
- Порождающие шаблоны (Creational) — шаблоны, абстрагирующие процесс
создания экземпляра. Они позволяют сделать систему независимой от способа
создания, композиции и представления объектов. Шаблон, порождающий классы,
использует наследование, чтобы изменять созданный объект, а шаблон,
порождающий объекты, делегирует создание объектов другому объекту.
- Структурные шаблоны (Structural) определяют различные сложные структуры,
которые изменяют интерфейс уже существующих объектов или его реализацию,
позволяя облегчить разработку и оптимизировать программу.
- Поведенческие шаблоны (Behavioral) определяют взаимодействие между
объектами, увеличивая таким образом его гибкость.
/*************************************************************************************************************************/
88
или подписчика. Подписчик может получать опубликованные события от более
чем одной сущности, даже если он зарегистрирован только на одном канале.
/*************************************************************************************************************************/
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, витая пара.
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-адрес
получателя, Номер порта.
92
Тяжеловесность — TCP необходимо три пакета для установки соединения
перед тем, как отправить данные. TCP следит за надёжностью и перегрузками.
Потоковость —данные читаются как поток байтов, не передается никаких
особых обозначений для границ сообщения или сегментов.
UDP — более простой, основанный на сообщениях протокол без установления
соединения. Протоколы такого типа не устанавливают выделенного соединения
между двумя хостами. Связь достигается путём передачи информации в одном
направлении от источника к получателю без проверки готовности или состояния
получателя.
Ненадёжность — когда сообщение посылается, неизвестно, достигнет ли оно
своего назначения — оно может потеряться по пути. Нет таких понятий как
подтверждение, повторная передача, тайм-аут.
Неупорядоченность — если два сообщения отправлены одному получателю, то
порядок их достижения цели не может быть предугадан.
Легковесность — никакого упорядочивания сообщений, никакого отслеживания
соединений и т.д. Это лишь транспортный уровень.
Датаграммы — пакеты посылаются по отдельности и проверяются на
целостность только еслиони прибыли. Пакеты имеют определенные границы,
которые соблюдаются после получения, то есть операция чтения на получателе
выдаст сообщение таким, каким оно было изначально послано.
Отсутствие контроля перегрузок — для приложений с большой пропускной
способностью существует шанс вызвать коллапс перегрузок, если только они не
реализуют меры контроля на прикладном уровне.
93
из преимуществ такого подхода является тот факт, что клиенты не зависят от
конкретной операционной системы пользователя, поэтому web application
является кроссплатформенным сервисом.
94
Spring
125. Что такое Spring Framework.
95
тяжеловесных программных моделей, которые исторически были
доминирующими в промышленности. Ещё одно его достоинство в том, что он
ввел ранее неиспользуемые функциональные возможности в сегодняшние
господствующие методы разработки, даже вне платформы Java. Этот фреймворк
предлагает последовательную модель и делает её применимой к большинству
типов приложений, которые уже созданы на основе платформы Java. Считается,
что Spring Framework реализует модель разработки, основанную на лучших
стандартах индустрии, и делает её доступной во многих областях Java. Таким
образом к достоинствам Spring можно отнести:
Относительная легкость в изучении и применении фреймворка в
разработке и поддержке приложения.
Внедрение зависимостей (DI) и инверсия управления (IoC) позволяют
писать независимые друг от друга компоненты, что дает преимущества в
командной разработке, переносимости модулей и т.д..
Spring IoC контейнер управляет жизненным циклом Spring Bean и
настраивается наподобие JNDI lookup (поиска).
Проект Spring содержит в себе множество подпроектов, которые
затрагивают важные части создания софта, такие как вебсервисы, веб
программирование, работа с базами данных, загрузка файлов, обработка
ошибок и многое другое. Всё это настраивается в едином формате и
упрощает поддержку приложения.
96
которым относятся, например, подключение к источнику данных, транзакция,
удаленные службы и т.п., вы создаете экземпляры (или получаете их из
определенных фабричных классов) зависимостей там, где они нужны — внутри
зависимого класса. Это приводит к распространению зависимостей по множеству
классов в приложении, что может затруднить их изменение. В случае
использования DI вся информация об общих зависимостях содержится в
единственном репозитории (в Spring есть возможность хранить эту информацию
в XML-файлах или Java классах), что существенно упрощает управление
зависимостями и снижает количество возможных ошибок.
• Улучшенная возможность тестирования. Когда классы проектируются для DI,
становится возможной простая замена зависимостей. Это особенно полезно при
тестировании приложения.
• Стимулирование качественных проектных решений для приложений. Вообще
говоря, проектирование для DI означает проектирование с использованием
интерфейсов. Используя Spring, вы получаете в свое распоряжение целый ряд
средств DI и можете сосредоточиться на построении логики приложения, а не на
поддерживающей DI платформе.
97
}
Setter Dependency Injection — контейнер IoC внедряет зависимости компонента
в компонент через методы установки в стиле JavaBean.
98
• Фреймворк удалённого управления: конфигурируемое представление и
управление Java-объектами для локальной или удалённой конфигурации с
помощью JMX.
• Фреймворк работы с сообщениями: конфигурируемая регистрация объектов-
слушателей сообщений для прозрачной обработки сообщений из очереди
сообщений с помощью JMS, улучшенная отправка сообщений по стандарту JMS
API.
• Тестирование: каркас, поддерживающий классы для написания модульных и
интеграционных тестов.
99