Собеседование Backend-Java-разработчика: вопросы и где искать ответы. Часть 2

Публикую продолжение сборника вопросов-ответов с собеседований на Backend-Java-разработчика. В первой части мы прошлись по Java и Spring. А в этой поговрим о Hibernate, базах данных, паттернах и практиках разработки, об одной популярной библиотеке, поддержке и сопровождении наших приложений, а также посмотрим на альтернативные шпаргалки и подведём итоги.

TL;DR

GitHub-репозиторий с полной шпаргалкой тут, а Хабр всё ещё торт.

Вопросы

Hibernate

28. Какие есть кэши в Hibernate и какие работают по умолчанию?

3 уровня кеширования:

  • Кеш первого уровня (First-level cache). По умолчанию включен.
  • Кеш второго уровня (Second-level cache). По умолчанию отключен.
  • Кеш запросов (Query cache). По умолчанию отключен.

Подробнее:

Углубиться в Hibernate нам всегда поможет Vlad Mihalcea:

29. Чем отличается Lazy от Eager в Hibernate?

  • Eager Loading — стратегия загрузки, при которой подгрузка связанных сущностей происходит сразу. Для применения необходимо в аннотацию отношения (@OneToOne, @ManyToOne, @OneToMany, @ManyToMany) передать fetch = FetchType.EAGER. Используется по умолчанию для отношений @OneToOne и @ManyToOne.
  • Lazy Loading — стратегия загрузки, при которой подгрузка связанных сущностей откладывается как можно дольше. Чтобы задать такое поведение, нужно в аннотацию отношения (@OneToOne, @ManyToOne, @OneToMany, @ManyToMany) передать fetch = FetchType.LAZY. Используется по умолчанию для отношений @OneToMany, @ManyToMany. До момента загрузки используется proxy-объект, вместо реального. Если обратиться к такому LAZY-полю после закрытия сессии Hibernate, то получим LazyInitializationException.

Вопрос также связан с проблемой "N+1" и может плавно перетечь в её обсуждение.

Почитать подробнее и с примерами можно в блоге Vlad Mihalcea: раз, два и про LazyInitializationException три.

30. Что такое 'проблема N+1 запроса' при использовании Hibernate? Когда возникает? Как решить? Как обнаружить?

Проблема N+1 может возникнуть не только при использовании Hibernate, но и других библиотек и фреймворков для доступа к данным.

В общем случае говорят о проблеме N+1 запроса, когда фреймворк выполняет N дополнительных запросов выборки данных, когда можно было обойтись всего одним. Соответственно от размера N зависит влияние проблемы на время ответа нашего приложения. Эту ситуацию нельзя обнаружить с помощью slow query log, ибо сами по себе запросы могут выполняться быстро, но их количество окажется большим или даже огромным.

На такое можно нарваться даже при использовании plain sql (jdbc, JOOQ), когда у нас одна сущность (и соответственно таблица) связана с другой. И вот мы подгрузили одним запросом просто список из первых, а потом пошли и в цикле для каждой подгрузили связанную по одному запросу. "Да как вы это допустили!?". Да просто по запарке кто-то в цикле начал вызывать метод, у которого в глубине где-то делается запрос и привет. Как исправить? Использовать JOIN со связанной таблицей при чтении списка. Тогда понадобиться лишь один запрос.

Теперь к Hibernate. Если на странице документации поискать "N+1", то можно обнаружить несколько упоминаний данной проблемы. Тут опишу самые явные и распространённые.

Например, возьмём стратегию выборки FetchType.EAGER. Она склонна к порождению N+1. А в отношении @ManyToOne по умолчанию используется именно она. Забыли в своём JPQL запросе заиспользовать JOIN FETCH и привет. А если нам и не нужны были связанные сущности, то тогда стоит задать стратегию FetchType.LAZY.

Если уж упомянули FetchType.LAZY, то сразу стоит сказать, что одно её наличие не гарантирует отсутствие проблемы N+1. При выборке списка сущностей, связанные автоматически не подгрузились. А мы потом пошли в цикле по загруженному списку и стали обращаться к полям связанной сущности — и снова здравствуйте. Всё тот же JOIN FETCH нас спасёт и в этой ситуации.

Но JOIN FETCH во многих случаях нас может привести к декартовому произведению, и тогда будет совсем bonjour. Для отношения @OneToMany это можно решить с помощью FetchMode.SUBSELECT — будет 2 запроса, но во втором запросе на получение списка связанных сущностей в условии выборки будет подзапрос на получение идентификаторов родительских сущностей. Т.е. запрос практически повторяется и он может быть тяжеловесным.

Есть вариант лучше — вычитывать связанные сущности пачками. Мы можем добавить аннотацию @BatchSize и указать размер подгружаемой пачки записей в одном запросе.

Ещё варианты:

Чтобы обнаружить проблему N+1, нужно писать тесты с использованием библиотеки db-util от Vlad Mihalcea. Подробнее можно прочитать у него же в блоге.

А вот JOOQ умеет обнаруживать N+1 автоматически, послушать об этом можно в 17-м эпизоде (01:16:36) подкаста Паша+Слава. Подробнее в документации JOOQ.

Углубиться в проблему можно:

31. Как описать составной ключ при использовании Hibernate?

На всякий случай: составной ключ — первичный ключ, состоящий из двух и более атрибутов. Вообще про ключи есть большая статья на Хабре с ценными комментариями.

Чтобы описать составной ключ при использовании Hibernate, нам необходимо создать под этот ключ отдельный класс с необходимыми полями и добавить ему аннотацию @Embeddable. Кроме того, он должен быть Serializable и иметь реализацию equals и hashcode.

В самой же сущности, для которой мы описываем составной ключ, добавляем поле только что созданного класса ключа и вешаем на него аннотацию @EmbeddedId.

Посмотреть примеры и углубиться в тему можно в статье Vlad Mihalcea или в документации Hibernate.

32. Как можно отобразить наследование на БД с помощью JPA (Hibernate)?

Есть 4 способа отобразить наследование на БД с помощью JPA (Hibernate):

  • MappedSuperclass — поля родителя содержатся в каждой таблице для каждого дочернего класса. Базовый класс отдельной таблицы не имеет. На базовый класс навешиваем @MappedSuperClass, а вот на дочерние @Entity. Если в таблице потомка поле родителя называется не так, как указано в родительском классе, то его нужно смаппить с помощью аннотации @AttributeOverride в классе этого потомка. Родитель не может участвовать в ассоциации. При полиморфных запросах у нас будут отдельные запросы для каждой таблицы.
  • Single table — вся иерархия классов в одной таблице. Чтобы различать классы, необходимо добавить колонку-дискриминатор. В данной стратегии на родительский @Entity-класс навешивается @Inheritance(strategy = InheritanceType.SINGLE_TABLE) и @DiscriminatorColumn(name = "YOUR_DISCRIMINATOR_COLUMN_NAME") (по умолчанию имя колонки DTYPE и тип VARCHAR). В каждом подклассе указываем @DiscriminatorValue("ThisChildName") со значением, которое будет храниться в колонке-дискриминаторе для данного класса. Если нет возможности добавить колонку, то можно использовать аннотацию @DiscriminatorFormula, в которой указать выражение CASE...WHEN — это не по JPA, фишка Hibernate. Денормализация. Простые запросы к одной таблице. Возможное нарушение целостности — столбцы подклассов могут содержать NULL.
  • Joined table — отдельные таблицы для всех классов иерархии, включая родителя. В каждой таблице только свои поля, а в дочерних добавляется внешний (он же первичный) ключ для связи с родительской таблицей. В @Entity-класс родителя добавляем @Inheritance(strategy = InheritanceType.JOINED). Для полиморфных запросов используются JOIN, а также выражение CASE...WHEN, вычисляющее значение поля _clazz, которое заполняется литералами (0 (родитель), 1, 2 и т.д.) и помогает Hibernate определить какого класса будет экземпляр.
  • Table per class — также как и в MappedSuperclass, имеем отдельные таблицы для каждого подкласса. Базовый класс отдельной таблицы не имеет. По спецификации JPA 2.2 (раздел 2.12) данная стратегия является опциональной, но в Hibernate реализована, поэтому продолжим. В данном случае на базовый класс мы навешиваем @Entity и @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS). Поле первичного ключа (@Id) обязательно для родительского класса. Также аннотация @AttributeOverride в этой стратегии не работает — называйте родительские поля в таблицах сразу единообразно. Полиморфный запрос будет использовать UNION для объединения таблиц. Чтобы различить при создании экземпляров подклассы, Hibernate добавляет поле _clazz в запросы, содержащие литералы (1, 2 и т.д.). А одинаковый набор столбцов для объединения добирается как NULL AS some_field. Родитель может участвовать в ассоциации с другими сущностями.

Почитать ещё по теме с примерами можно:

Кроме того, можно заглянуть в JavaDoc с аннотациями:

Или почитать спецификацию JPA 2.2. Ещё есть книжка с толкованием данного pdf.

Базы данных

33. В чем разница между where и having в SQL?

Есть исчерпывающий ответ на этот вопрос в документации к Postgresql. Вот его перевод с сайта Postgres Pro:

Основное отличие WHERE от HAVING заключается в том, что WHERE сначала выбирает строки, а затем группирует их и вычисляет агрегатные функции (таким образом, она отбирает строки для вычисления агрегатов), тогда как HAVING отбирает строки групп после группировки и вычисления агрегатных функций. Как следствие, предложение WHERE не должно содержать агрегатных функций; не имеет смысла использовать агрегатные функции для определения строк для вычисления агрегатных функций. Предложение HAVING, напротив, всегда содержит агрегатные функции. (Строго говоря, вы можете написать предложение HAVING, не используя агрегаты, но это редко бывает полезно. То же самое условие может работать более эффективно на стадии WHERE.)

Но вас также может заинтересовать:

34. ACID — расшифровать и немного раскрыть

  • A — Атомарность (Atomicity). Гарантирует, что никакая транзакция не будет зафиксирована в системе частично.
  • C — Согласованность (Consistency). Каждая успешная транзакция фиксирует только допустимые результаты.
  • I — Изолированность (Isolation). Во время выполнения транзакции параллельные транзакции не должны оказывать влияния на её результат.
  • D — Стойкость (Durability). Подтверждённые изменения не будут отменены (например, из-за сбоя).

35. Уровни изолированности транзакции. Какую проблему решает каждый уровень? Какой по умолчанию в большинстве реляционных БД? Приходилось ли изменять уровень изолированности транзакции и для чего?

Краткий конспект статьи из Википедии:

  • Read uncommitted (чтение незафиксированных данных). Он гарантирует только отсутствие потерянных обновлений (ситуация, когда при одновременном изменении одного блока данных разными транзакциями одно из изменений теряется).
  • Read committed (чтение фиксированных данных). Именно он чаще всего является уровнем по умолчанию. Обеспечивает защиту от «грязного» чтения (чтение данных, добавленных или изменённых транзакцией, которая впоследствии откатится).
  • Repeatable read (повторяемость чтения). Решает проблему неповторяющегося чтения (ситуация, когда при повторном чтении в рамках одной транзакции ранее прочитанные данные оказываются изменёнными).
  • Serializable (упорядочиваемость). Транзакции полностью изолируются друг от друга, каждая выполняется так, как будто параллельных транзакций не существует — защищает от фантомного чтения (ситуация, когда при повторном чтении в рамках одной транзакции одна и та же выборка дает разные множества строк).

На Хабре есть несколько статей по теме на любой вкус:

36. Какие бывают типы индексов в БД и как они устроены под капотом? Что такое индекс вообще?

Индекс БД — это структура данных, которая повышает скорость операций поиска данных в таблице БД за счет дополнительных операций записи и хранения для поддержания структуры данных индекса. Индексы используются для быстрого поиска данных без необходимости поиска каждой строки в таблице базы данных каждый раз при обращении к таблице БД. Ключом индекса выступает значение индексируемого столбца/столбцов. Некоторые БД позволяют строить индексы по выражениям.

В основе индексов чаще всего лежат следующие структуры данных:

Классификацию индексов можно разделить по различным критериям. Следует уточнить или предложить интервьюверу какой именно критерий имеется в виду.
Например, по порядку сортировки:

  • Упорядоченные — индексы, в которых записи упорядочены по возрастанию или убыванию. Реализуются на B-деревьях.
  • Неупорядоченные — индексы, в которых записи неупорядоченны. Реализуются на хешах.
    Или по воздействию на источник данных:
  • Некластерные — такие индексы организуют ссылки на соответствующие строки и значения. Ссылка содержит информацию об ID файла, в котором хранится строка; ID страницы соответствующих данных; номер искомой строки на соответствующей странице; содержимое столбца. В таблице БД может быть несколько некластерных индексов.
  • Кластерные — такие индексы представляют собой древовидную структуру данных, при которой значения индекса хранятся вместе с соответствующими им данными. Физическое расположение данных перестраивается в соответствии с порядком индекса, поэтому может быть только один такой индекс на таблицу БД.

Подробнее:

37. В чем разница между оптимистической и пессимистической блокировками?

Блокировка (lock) в СУБД — отметка о захвате объекта транзакцией в ограниченный или исключительный доступ с целью предотвращения коллизий и поддержания целостности данных.

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

  • shared (read) — блокирует писателей, но позволяет читателям продолжить работу. Иными словами, разрешает другим транзакциям захватывать блокировку на чтение, но запрещает блокировку на запись.
  • exclusive (write) — блокирует и писателей, и читателей. Делает все операции записи последовательными. Иными словами, запрещает другим транзакциям захватывать блокировки на чтение и на запись.

Оптимистическая блокировка — не ограничивает модификацию обрабатываемых данных сторонними сессиями, однако перед началом предполагаемой модификации запрашивает значение некоторого выделенного атрибута каждой из строк данных (обычно используется наименование VERSION и целочисленный тип с начальным значением 0). Перед записью модификаций в базу данных перепроверяется значение выделенного атрибута, и если оно изменилось, то транзакция откатывается или применяются различные схемы разрешения коллизий. Если значение выделенного атрибута не изменилось — производится фиксация модификаций с одновременным изменением значения выделенного атрибута (например, инкрементом) для сигнализации другим сессиям, что данные изменились.

Основное отличие в том, что оптимистическая блокировка приводит к накладным расходам только в случае конфликта, в то время как пессимистическая блокировка уменьшает накладные расходы на конфликт. Поэтому оптимистическая блокировка лучше всего подходит в случае, когда большинство транзакций не конфликтуют.

Много статей про блокировки с кодом и картинками можно прочитать в блоге Vlad Mihalcea:

Подробнее на Хабре:

Подробнее в других источниках:

Принципы, паттерны, практики и подходы разработки ПО

38. SOLID

Обычно просто просят расшифровать, что каждая буква означает с минимальным пояснением. Никто из моих интервьюеров не стал размусоливать или обсуждать. Один раз спросили, зачем оно вообще надо. Но, я думаю, чем больше сеньорности в вакансии, тем глубже и интереснее дискуссия может возникнуть.

  • S — Принцип единственной ответственности (The Single Responsibility Principle). Каждый класс должен иметь одну и только одну причину для изменений.
  • O — Принцип открытости/закрытости (The Open Closed Principle). Сущности должны быть открыты для расширения, но закрыты для модификации.
  • L — Принцип подстановки Барбары Лисков (The Liskov Substitution Principle). Объекты должны быть заменяемыми на экземпляры их подтипов без изменения правильности выполнения программы. Наследующий класс должен дополнять, а не изменять базовый.
  • I — Принцип разделения интерфейса (The Interface Segregation Principle). Много специальных интерфейсов лучше, чем один универсальный.
  • D — Принцип инверсии зависимостей (The Dependency Inversion Principle). Зависеть нужно от абстракций, а не от реализаций.

Первоисточником, сформулировавшим данные принципы, является дядюшка "Свет наш чистый код" Боб Мартин, а акроним придумал один из его читателей. Хотите погрузиться глубже? Тогда читайте часть 3 его книги "Чистая архитектура". Книжка годная, читается легко, не пожалеете. Однако пропускайте всё через свой опыт и оценивайте критически, мало ли что 🙂

Сама по себе тема богатая и холиворная, ибо многие начинают трактовать эти принципы по-своему. На этой почве может случиться недопонимание с собеседующим, будьте аккуратны и конструктивны. Достаточно поискать статьи на Хабре по SOLID или каким-нибудь принципам в отдельности и заглянуть в комментарии. Кому-то SOLID вообще не по душе.

Также, могу посоветовать, самостоятельно разобрать каждый принцип на практическом примере, ибо за это тоже могут спросить. Или заглянуть на Baeldung.

39. Паттерны. Какие знаете? Каких видов бывают? Расскажите парочку (желательно разных видов)? Какие применяли на практике или встречались где-то? Чем декоратор отличается от прокси, а абстрактная фабрика от обычной?

Обычно советуют и в вакансиях пишут про книжку GoF. Но, на мой взгляд, сейчас есть вариант даже лучше — refactoring.guru. Там и сравнение, и на родной мове (русский, украинский, английский, китайский), и примеры на вашем любимом ЯП, прекрасные иллюстрации и диаграммы. Там также можно купить книжку по паттернам. Автору большой-большой респект.

40. Что такое coupling и cohesion? Что из них (или оба) должно быть сильным (высоким) и/или слабым (низким)?

Coupling (Зацепление) — степень зависимости между программными компонентами. Должна быть низкой.

Cohesion (Связность) — степень сфокусированности методов класса. Должна быть высокой.

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

Тема периодически затрагивается на Хабре в контексте построения хорошей архитектуры и написания качественного кода. Как и всегда, особую ценность составляют комментарии:

Также coupling и cohesion часто упоминаются или ведут к обсуждению GRASP. Об этом можно почитать на Хабре: раз и два.

41. Что такое куб масштабирования?

Куб масштабирования (scale cube, из книги The Art of Scalability) является наглядным изображением трёх ортогональных способов увеличения производительности приложения: sharding, mirrorring и microservices.

  • Sharding (data partioning) — разбиение и размещение однотипных данных по разным узлам.
  • Mirroring (horizontal duplication) — дублирование или клонирование данных для уменьшения времени отклика.
  • Microservices — архитекутрный подход, при котором функциональность системы разбивается на отдельные сервисы по бизнес-задачам.

Наглядно:

Почитать подробнее:

42. Какие вы знаете паттерны микросервисов?

Как и в случае с ООП-паттернами, существует книжка с одноимённым названием. Переведена на русский. На Хабре опубликована статья-затравочка от ИД "Питер".

И есть другая книжка — Microservice Patterns and Best Practices. На Medium имеется на неё обзор.

На microservices.io можно найти наглядные и интерактивные схемы паттернов микросервисной архитектуры: тут или здесь. Каждый блок на схемах — это ссылка на статью по данному паттерну. Строго рекомендую.

Также на Хабре есть статья по теме. Первым же комментарием к ней советуют тоже интересную книжку — Enterprise Integration Patterns и список паттернов из неё.

43. Расскажите о CAP-теореме. Как MongoDB (например) удовлетворяет CAP-теореме?

Теорема CAP — эвристическая теорема, утверждающая, что любая распределённая система может удовлетворять не более двум из трёх свойств:

  • Consistency (согласованность) — во всех вычислительных узлах в один момент времени данные не противоречат друг другу — каждое чтение вернёт самую последнюю запись. Подразумевает под собой линеаризуемость — если произошла какая-либо операция, то её результат доступен сразу после того, как был получен ответ о её выполнении.
  • Availability (доступность) — любой запрос получает успешный ответ без гарантии, что ответы со всех узлов системы совпадают.
  • Partition tolerance (устойчивость к разделению) — система продолжает функционировать, несмотря на потерю (или задержку) произвольного числа сообщений между узлами из-за сетевых проблем (асинхронная сеть).

MongoDB, по мнению большинства (например), это — CP, но вот есть статья… Держим в уме, что теорема эвристическая и носит упрощательный характер. Также вызывает много споров.

Про большинство БД можно найти ответы в статьях по дополнительным ссылкам ниже.

Подробности:

44. Чем сервисная архитектура отличается от микросервисной?

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

  • представляет бизнес-логику с определённым результатом (много ответственностей, связанных единой бизнес-областью, целью, смыслом и т.п.);
  • может состоять из других сервисов или зависеть от них;
  • является "чёрным ящиком" для своих клиентов.
    Как и все серьёзные подходы к чему-либо имеет свой манифест
    Микросервисная архитектура (MSA) — частный случай SOA, побуждающий строить взаимодействие насколько это возможно и необходимо небольших, слабосвязанных, легко изменяемых и взаимозаменяемых компонентов (микросервисов). MSA ориентирована, в первую очередь, на структуру и компоненты отдельного приложения.
    Микросервис:
  • выполняет только одну достаточно элементарную и определённую функцию (Unix way — единственная ответственность);
  • деплоится и разрабатывается независимо от других микросервисов;
  • является независимым от других микросервисов (в том числе максимальная минимизация общего кода);
  • является "чёрным ящиком" для своих клиентов.

Можно найти несколько источников, где MSA представляется как нечто новое и хорошее, а SOA — старое и умирающее. Часто это ещё и подаётся под соусом технологий. ИМХО — это больше маркетинговая туфта, чем хорошее сравнение. MSA и SOA не завязаны на технологии и не ограничивают в их использовании. Более того, оба подхода можно совмещать с пользой для бизнеса.

Подробнее:

45. Какие есть ограничения у микросервисов? Назовите достоинства и недостатки микросервисной архитектуры

Достоинства:

  • Меньше кода на программный компонент (микросервис) -> проще и быстрее разрабатывать, поддерживать, тестировать, понимать, выбросить и переписать, реализовывать с помощью наиболее подходящих технологий и инструментов (в том числе на новье)
  • Меньше команда -> меньше проблем и конфликтов при мёрже и совместной разработке
  • Проще горизонтально масштабировать
  • Быстрее и проще как внедрять, так и откатывать

Недостатки:

  • Увеличение накладных расходов на взаимодействие между программными компонентами (микросервисами) — обмен между ними выполняется по сети со всем вытекающим (сериализация/десериализация, задержки и т.д.)
  • Возможно появление необходимости распределённых транзакций
  • Увеличение объёмов логирования и мониторинга
  • Дублирование кода
  • Сложнее дебажить и отлавливать ошибки
  • Нужно быть готовым и способным продолжить работать, когда любой из микросервисов отвалится
  • Общая сложность растёт с количеством микросервисов

Подробнее:

Посмотреть:

46. Чем композиция отличается от агрегации?

Композиция (composition) — отношение "является частью" (HAS-A Relationship), при котором целое явно контролирует время жизни своей составной части.

Агрегация (aggregation) — отношение "является частью" (HAS-A Relationship), при котором целое хоть и содержит свою составную часть, время их жизни не связано.

Подробнее:

47. Какие бывают тестовые объекты (заглушки)? Чем стаб отличается от мока?

Тестовый объект (Test Double) — объекты, которые необходимы в тестах для подмены внешних зависимостей тестируемого кода
Типы тестовых объектов:

  • Dummy — объекты, которые передаются в методы, но не используются. Например: заполнение списка параметров, часто это просто null
  • Fake — заглушка, являющаяся рабочей имплементацией, но с урезанной функциональностью и неприменима в production-окружении. Например: in-memory БД (fake database)
  • Stub — заглушка с жестко заданными ответами на вызовы со стороны тестируемого объекта (system under test — SUT) во время теста
  • Spy — это разновидность Stub, которая записывает информацию о произошедшем с ней, какие вызовы её методов были выполнены и сколько раз, как изменилось состояние и т.п.
  • Mock — заглушка с ожиданиями определённого набора вызовов, которые будут на ней выполнены в ходе теста

Из данного набора заглушек только Mock используется для верификации поведения, остальные — для верификации состояния тестируемого объекта .

Подробнее:

48. Дайте определение полиморфизму

В общем виде определение полиморфизма приводит Бенджамин Пирс в своей книге Типы в языках программирования:

Термин “полиморфизм” обозначает семейство различных механизмов, позволяющих использовать один и тот же участок программы с различными типами в различных контекстах.

Полиморфизм бывает следующих видов:

  1. Универсальный полиморфизм. Он подразделяется на:
    • Параметрический полиморфизм — описывает вычисления в общем виде, абстрагируясь от конкретных типов, которые будут использованы. Параметрически полиморфные функции также называются обобщенными (Generic).
    • Полиморфизм включений (inclusive) — описывает вычисления не только для конкретного типа, но для и всех его возможных подтипов. Отражает принцип подстановки Барбары Лисков.
  2. Специальный полиморфизм (или ad-hoc) — диспетчеризация (перенаправление) к одной или нескольким функциям для конкретного типа аргумента. Из него выделяют подтипы:
    • Перегрузка (overloading) позволяет объявлять функции с одним и тем же именем, но с разными типами аргументов и их количеством (арностью).
    • Неявное приведение типов — преобразование одного типа в другой по определённым правилам, описанным в стандарте языка, и выполняемое компилятором.

Безотносительно к Java полиморфизм хорошо разобран в статье Полиморфизм простыми словами.
В других источниках:

49. Что такое REST? Какое API является RESTful? Чем REST отличается от SOAP?

REST – это архитектурный стиль, который можно применить для реализации взаимодействия web-сервисов.
Вот несколько признаков REST, которые хотят услышать большинство интервьюеров:

  • Концентрация на ресурсах
  • Ресурс имеет уникальный URI
  • Формат передачи — обычно JSON, но можно XML
  • Транспортный протокол — HTTP. Строго говоря — это необязательно, но по факту и в большинстве случаев так. Следующие пункты тоже.
  • Использование различных методов HTTP в соответствии с их семантикой и предназначением. GET: получить информацию о ресурсе; POST: создать новый ресурс; PUT: обновить существующий ресурс; DELETE: удалить ресурс. Иногда спорят об обновлении — PUT vs PATCH vs POST.
  • Использование кодов ответа HTTP в соответствии с их семантикой и предназначением. Хорошая картинка по определению HTTP-кода ответа.
  • HATEOAS обычно не используют и не спрашивают, но это тоже относится к REST, о чём можно упомянуть

Чаще всего, на моей практике, спрашивают какой HTTP-метод для чего должен использоваться или как назвать URI, чтобы API было RESTful. Однако, никакой спецификации или стандарта под это нет. И вообще, строго говоря, изначально REST — это архитектурный стиль, непривязанный ни к каким протоколам. Тема раскрыта в этой статье или в этой на Хабре — комментарии сочные.

Вот принципы REST-архитектуры по Филдингу:

  • Модель клиент-сервер (Client–server)
  • Отсутствие состояния (Stateless) — у клиента и сервера нет необходимости отслеживать состояние друг друга
  • Кэширование (Cacheable) — ответы сервера должны помечаться как кэшируемы и некэшируемые
  • Единообразие интерфейса (Uniform interface) — идентификация ресурсов, манипуляция ресурсами через представление, "самодостаточные" сообщения, HATEOAS
  • Слои (Layered system) — в системе может быть больше одного слоя, но компоненты системы видит и взаимодействует только с соседним слоем. Кроме клиента и сервера может быть ещё прокси и шлюз
  • Код по требованию (необязательное ограничение) (Code on demand (optional)) — отправка сервером исполняемого кода клиенту

SOAP — стандартизованный протокол обмена данными.
Основные характеристики SOAP:

  • Формат сообщений — XML (SOAP-XML — Envelope, Header, Body, Fault)
  • SOAP-сервис должен иметь описание на языке WSDL(тоже XML)
  • SOAP поддерживает множество протоколов — (TCP, UDP, HTTP, SMTP, FTP и т.д.)
  • При использовании HTTP, поддерживаются методы GET и POST

Тема горячая, интересная всем web-разработчикам, поэтому на Хабре имеется уйма статей на любой вкус.
Теоритическо-практические:

Холиварно-дискусионные:

Популярные библиотеки

50. Стоит ли использовать Lombok?

Осторожно, холиворный вопрос! Возможны ожоги и прочие неприятности!

На Хабре найдётся много больше одной статьи демонстрирующей народную любовь к Lombok — ведь он знаменитый победитель бойлерплейтов, сократитель кода, ускорятель кодонабора, любимый сундучок исцеляющих костылей и прочее, и прочее. О прелести Lombok вам расскажут из любого утюга.
Но как там с подводными камнями? Что с надводными вилами? Этот вопрос на собеседовании о том, как хорошо вы знаете недостатки своего любимого инструмента. И на сдачу — тест на совместимость.

А ответ на вопрос стоит начать с переворота стрелочки — с хитрым ленинским прищуром спросите у интервьюера: "А как в вашем стайл-гайде написано?". Заодно узнаете о его отсутствии наличии. Ибо холиварные вопросы решить нужно однажды на сходе Архитектурного Синода и на весь простор компании сии решения распространить, увековечив их в стайл-гайде, дабы раскола избежать и не соблазнять неокрепшие умы неофитов спорами на рабочем месте посреди спринта горящего.

Самая лайтовая и, возможно, притянутая за уши причина не использовать Lombok — плагин постоянно отваливается с выходом новой версии IDEA. А если отваливается плагин, то работа летит коту под хвост. Но это уже уходит в прошлое! С версии 2020.3 Lombok-плагин будет поставляться вместе с IDEA.

Большинство из проблем с Lombok связаны с особенностями и нюансами реализации как самих аннотаций библиотеки, так и сторонних фреймворков и библиотек. Например, эксперты по Hibernate советуют (например тут) избегать использования Lombok в entity-классах. Хотя бы исключить использование @EqualsAndHashCode, @ToString и @Data, потому что генерируемый ими код для методов equals, hashCode и toString может либо отработать неверно, либо привести к потере производительности или исключениям в некоторых определённых случаях для entities. А как нужно правильно реализовывать данные методы для entities написано тут или тут.

Интересная дискуссия, затянувшаяся на два дня, была по данному вопросу в чатике подкаста "Паша+Слава" 7-8 марта — начало тут. В ней asm0dey задаёт каверзные вопросы по использованию Lombok и рассказывает почему он забанил Lombok как тимлид. Например: "Что произойдёт если библиотека, которую ты используешь, использует @SneakyThrows и ты её вызываешь в рамках транзакции?" (об этом можно почитать тут), "Тебе наверное нравится аннотация @Data. Как она строит equals для коллекций и для массивов?", "Знаешь ли ты что @EqualsAndHashCode может вызвать StackOverflowException и тебе даже нечего будет дебажить?".

Главная проблема Lombok — простота хуже воровства — его волшебная лёгкость провоцирует не читать документацию, что потом может повлечь за собой горе и страдания. Но может и не повлечь — тут как задача в джиру ляжет. Не даром не заросла к Lombok народная тропа — можно ограничиться использованием некоторого минимального джентельменского набора из Lombok, следить за собой. Но всё-таки мойте руки, предохраняйтесь читайте документацию перед активным использованием ваших инструментов, ведь они могут иметь неочевидные нюансы и сюрпризы в реализации.

P.S. У Lombok есть занятная экспериментальная фича onX — у неё интересный синтаксис (@__ правда только для Java 7) и его описание в документации. А вот тут рассказывается зачем оно вам может понадобиться, например в Spring-приложении.

Подробнее:

Прочее

51. Чем поток отличается от процесса?

Ответ на этот вопрос писали в интернете ещё тогда, когда я балду в школе пинал (нулевые), поэтому у читателя есть широкий выбор.
Мне импонирует заметка на OpenNET — кратко и чётко, суше Сахары.
Для любителей смотреть в окна есть статейка на MSDN.
Вишенка на торте — олдовая статья с Хабра.

52. Какие инструменты для нагрузочного тестирования вы знаете?

Благодаря нагрузочному тестированию появляется возможность оценить производительность приложения при различных нагрузках от действий определённого количества пользователей приложения.
Существует несколько решений для проведения подобного рода тестирования:

  • Apache JMeter — самый известный, opensource, поддерживает работу с различными сетевыми протоколами разных уровней HTTP, HTTPS, FTP, LDAP, SOAP, TCP, почтовых протоколов и shell-скриптов. Он, как java-based инструмент, предоставляет возможности по работе с JDBC, Message-oriented middleware (MOM) через JMS и Java Objects.
  • LoadRunner — это сложный и универсальный инструмент для нагрузочного тестирования различных платформ. Платный, ориентирован в первую очередь на Enterprise.
  • LoadNinja — платный, позволяет пользователям создавать нагрузочные тесты без использования каких-либо скриптов. Средствами браузера собирает метрики, которые позволяют оценить производительность приложения.
  • WebLOAD — коммерческий комплексный инструмент. Предоставляет пользователям расширенные функции, такие как анализ производительности и интеллектуальная аналитика, а также интеграцию с рядом инструментов, начиная от мониторинга производительности до конвейеров CI/CD.
  • LoadUI Pro — коммерческий инструмент от создателей SOAP UI, тесты которого можно использовать как сценарии нагрузочного тестирования. Предназначен для веб-сервисов и позволяет пользователям оценивать масштабируемость, скорость и производительность API.
  • K6 — opensource инструмент, ориентированный на разработчиков и тестирования производительности сайтов. Скрипты пишутся на JavaScript. Запуск тестов происходит в консольном режиме, результаты тестирования по умолчанию также выводятся в консоль, однако доступна поддержка таких плагинов для вывода результатов, как Kafka, Datadog, InfluxDB, JSON и StatsD. Доступна интеграция с CI-инструментами.
  • Яндекс.Танк — инструмент от Яндекс с модульной архитектурой, которая позволяет использовать различные генераторы нагрузок — высокопроизводительный асинхронный hit-based-генератор HTTP-запросов Phantom и сценарный инструмент jMeter. Встроенный мониторинг ресурсов, автоматическая остановка теста по заданным условиям, вывод результатов в консоль и в виде графиков, подключение пользовательских модулей.
  • Gatling — opensource инструмент на Scala с использованием Netty и Akka. Скрипты пишутся на Scala, есть возможность интеграции с Jenkins.

Подробнее:

53. Чем докер отличается от виртуалки? Что лежит в его основе? Как работает Docker?

Виртуальная машина (Virtual Machine — VM) — программная и/или аппаратная система, эмулирующая аппаратное обеспечение и исполняющее приложения некоторой целевой платформы на платформе хоста. Иными словами — это виртуальный компьютер с виртуальными устройствами и независимой операционной системой, управлением памятью и другими компонентами. Т.о. на одном реальном компьютере может существовать несколько виртуальных, изолированных друг от друга, со своими ОС. Запускаемые внутри виртуальной машины приложения не имеют понятия о её виртуальности и взаимодействуют с ней как с реальной. Хотя VM изолирована от реального хоста, она может иметь доступ к его диску и периферийным устройствам. Существует возможность сделать backup’ы VM в виде текущего состояния системы и содержимого дисков для возможности последующего восстановления системы в исходное состояние.
Каждая VM несёт дополнительные расходы на эмуляцию оборудования и работу своей ОС.
Управляет виртуальными машинами на реальном железе гипервизор — это программное и/или аппаратное решение, процесс, который отделяет операционную систему компьютера и приложения от базового физического оборудования.
Самими популярными решениями виртуализации являются KVM с QEMU, XEN, решения от VMWare и Hyper-V.

Docker — программное решение, использующее виртуализацию на уровне операционной системы для доставки, развёртывания, изоляции и упаковки приложения со всеми его зависимостями в т.н. контейнер.
Контейнеры расходуют меньше места на диске и ресурсов хоста, потому что переиспользуют большее количество общих ресурсов хоста и обеспечивает виртуализацию на уровне ОС. Каждый из контейнеров работает как отдельный процесс основной ОС, у которого есть своё собственное виртуальное адресное пространство. Изоляция контейнеров достигается с помощью linux namespaces. Ограничить потребление системных ресурсов контейнером можно через cgroups.
Контейнер создаётся из образа, который в свою очередь основан на Dockerfile проекта, и представляет собой набор файловых систем (слоёв) наслоённых друг на друга и сгруппированных вместе, доступных только для чтения. Контейнеры всегда создаются из образов, добавляя свой собственный доступный для записи верхний слой, в который вносятся необходимые для контейнера в процессе работы изменения. Это позволяет нескольким контейнерам переиспользовать один образ.
Docker-контейнеры используются различными системами оркестрации и CI/CD.
Для существует большой общедоступный репозиторий Docker-образов — Docker-Hub
Кроме Docker, существуют и другие контейнеры.

На Хабре есть множество статей на тему Docker:

А также на тему виртуализации:

Кроме того, можно углубиться в других источниках:

54. Чем контейнер сервлетов отличается от сервера приложений? Какие знаете?

Контейнер сервлетов — это компонент веб-сервера для взаимодействия с Java-сервлетами — обеспечивает их жизненный цикл, сопоставление URL-адреса к определённому сервлету и имеет ли отправитель запроса по данному URL необходимые права доступа и т.п.
Примеры (также являются самостоятельными веб-серверами, но НЕ серверами приложений (реализуют неполный стек Java EE)):

Исчерпывающий ответ на вопрос "Зачем нужны сервера приложений, если есть Tomcat" можно найти в одноимённой статье.

Аналогичные источники вопросов-ответов и прочие полезности

Итого

Собеседование Backend-Java-разработчика охватывает широкий диапазон используемых в работе практик и инструментов. Судя по дополнительным источникам, которые я приводил в ответах, на Хабре можно отыскать статьи по каждому вопросу, однако много источников уровня "Нетленная классика" лежит за его пределами. Например, в докладах с конференций JUG.RU.

Если у вас есть интересный вопрос с интерьвю, которым вы хотите поделиться, то вы можете написать его в комментариях или открыть PR в репозитории на GitHub с данной шпаргалкой.

Внимание! Будьте бдительны и осторожны! Во всех подобных статьях (моя не исключение) и сборниках могут прятаться ошибки. Помните, что лучшие источники знаний — это исходный код и документация.

Кроме того, зазубривание ответов на вопросы выше не даёт гарантий успешного трудоустройства, хорошой работы и шелковистости волос.

ссылка на оригинал статьи https://habr.com/ru/post/529214/

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *