Что произойдет в результате компиляции и выполнения кода?
Anonymous Quiz
8%
Вывод в консоль - 643;
30%
Пустой вывод в консоль;
30%
Вывод в консоль - 6;
32%
Ошибка компиляции.
👍15❤1
Как использовать JavaEE сервлет в Spring Framework?
Web-приложение на Spring MVC технически само по себе работает на сервлетах: всю обработку запросов берет на себя единый DispatcherServlet. С его помощью реализуется паттерн Front Controller.
Если вам нужно определить в программе полностью независимый от Spring-контекста сервлет или фильтр, ничего особенного для этого делать не нужно. Как обычно в Servlet API, нужно объявить класс, добавить его в web.xml как сервлет, добавить для сервлета маппинг.
Сервлет живет вне Spring-контекста, внедрение зависимостей в нём просто так не заработает. Чтобы использовать autowiring, на этапе инициализации сервлета вызывается статический SpringBeanAutowiringSupport.processInjectionBasedOnServletContext, с текущим сервлетом и его контекстом в аргументах. В этом же утилитарном классе есть ряд других средств для работы с контекстом извне.
Если программа построена на Spring Boot, создание бина типа ServletRegistrationBean поможет добавить сервлеты в рантайме. А для декларативного добавления на этапе компиляции, к классу конфигурации применяется @ServletComponentScan. С этой аннотацией стартер приложения просканирует и добавит в контекст все web-компоненты в стиле Servlet 3.0: классы с аннотациями @WebFilter, @WebListener и @WebServlet.
Web-приложение на Spring MVC технически само по себе работает на сервлетах: всю обработку запросов берет на себя единый DispatcherServlet. С его помощью реализуется паттерн Front Controller.
Если вам нужно определить в программе полностью независимый от Spring-контекста сервлет или фильтр, ничего особенного для этого делать не нужно. Как обычно в Servlet API, нужно объявить класс, добавить его в web.xml как сервлет, добавить для сервлета маппинг.
Сервлет живет вне Spring-контекста, внедрение зависимостей в нём просто так не заработает. Чтобы использовать autowiring, на этапе инициализации сервлета вызывается статический SpringBeanAutowiringSupport.processInjectionBasedOnServletContext, с текущим сервлетом и его контекстом в аргументах. В этом же утилитарном классе есть ряд других средств для работы с контекстом извне.
Если программа построена на Spring Boot, создание бина типа ServletRegistrationBean поможет добавить сервлеты в рантайме. А для декларативного добавления на этапе компиляции, к классу конфигурации применяется @ServletComponentScan. С этой аннотацией стартер приложения просканирует и добавит в контекст все web-компоненты в стиле Servlet 3.0: классы с аннотациями @WebFilter, @WebListener и @WebServlet.
👍22
Что произойдет в результате компиляции и выполнения кода?
Anonymous Quiz
31%
Вывод в консоль - "-1";
29%
Вывод в консоль - "1";
10%
Ошибка компиляции;
25%
Программа не завершит своего выполнения. В консоль ничего не будет выводиться;
5%
Вывод в консоль - "0".
👍14🍌4
В чём преимущества и недостатки Spring Boot?
Основные сущности фреймворка Spring Boot – это стартеры. Зависимости с названиями вида spring-boot-starter-xxx выполняют две основных задачи. Во-первых, они добавляют набор типичных сторонних библиотек-зависимостей; во-вторых, регистрируют типичные бины и их конфигурации. Кроме того, со Spring Boot в проекте появляется ряд таких полезностей, как embedded-сервер, конфигурация web-приложения без web.xml, метрики, properties вынесенные из кода во внешние файлы.
Например, spring-boot-starter-data-jpa даст вам готовый комплект всего необходимого для использования JPA: драйвер, совместимую с ним версию Hibernate, библиотеки Persistence API и Spring Data. В контексте приложения появятся все нужные для JPA репозиториев бины.
Таким образом Spring Boot ускоряет и упрощает разработку, дает возможность избавиться от boilerplate-кода в проекте и сфокусироваться на бизнес-задачах. Это бывает особенно важно в микросервисной архитектуре, когда создается большое количество приложений.
С другой стороны, такая избыточность естественно приводит к большей тяжеловесности и медлительности приложения.
Основные сущности фреймворка Spring Boot – это стартеры. Зависимости с названиями вида spring-boot-starter-xxx выполняют две основных задачи. Во-первых, они добавляют набор типичных сторонних библиотек-зависимостей; во-вторых, регистрируют типичные бины и их конфигурации. Кроме того, со Spring Boot в проекте появляется ряд таких полезностей, как embedded-сервер, конфигурация web-приложения без web.xml, метрики, properties вынесенные из кода во внешние файлы.
Например, spring-boot-starter-data-jpa даст вам готовый комплект всего необходимого для использования JPA: драйвер, совместимую с ним версию Hibernate, библиотеки Persistence API и Spring Data. В контексте приложения появятся все нужные для JPA репозиториев бины.
Таким образом Spring Boot ускоряет и упрощает разработку, дает возможность избавиться от boilerplate-кода в проекте и сфокусироваться на бизнес-задачах. Это бывает особенно важно в микросервисной архитектуре, когда создается большое количество приложений.
С другой стороны, такая избыточность естественно приводит к большей тяжеловесности и медлительности приложения.
👍29❤3
Что произойдет в результате компиляции и выполнения кода?
Anonymous Quiz
47%
Вывод в консоль - "[[[0, 0, 0], [0, 0, 0]]]"
2%
Вывод в консоль - "[[0, 0], [0, 0]]"
6%
Вывод в консоль - "[[[1, 2, 3], [1, 2, 3]]]"
22%
Вывод в консоль - "[1, 2, 3]"
23%
Ошибка компиляции
👍23🍌2🌚1
Как работает инъекция прототипа в синглтон?
Раньше мы уже рассматривали различия скоупов singleton и prototype в Spring Framework. Допустим ситуацию, когда в singleton-компонент внедряется зависимость со скоупом prototype – когда будет создан её объект?
Если просто добавить к определению бина аннотацию @Scope(SCOPE_PROTOTYPE), и использовать этот бин в синглтоне через аннотацию @Autowired – будет создан только один объект. Потому что синглтон создается только однажды, и обращение к прототипу случится тоже однажды при его создании (при внедрении зависимости).
Примитивный способ получать новый объект при каждом обращении – отказаться от @Autowired, и доставать его из контекста вручную. Для этого нужно вызывать context.getBean(MyPrototype.class).
Воспользоваться автоматическим внедрением зависимостей можно через внедрение метода (паттерн «Команда»). Автовайрится не сам объект, а производящий его метод.
Более красивый декларативный способ – правильно настроить определение бина. В аннотации @Scope кроме самого scopeName доступен второй параметр – proxyMode. По умолчанию его значение NO – прокси не создается. Но если указать INTERFACES или TARGET_CLASS, то под @Autowired будет внедряться не сам объект, а сгенерированный фреймворком прокси. И когда проксируемый бин имеет скоуп prototype, то объект внутри прокси будет пересоздаваться при каждом обращении.
Лучший способ разобраться со скоупами – прочитать официальный гайд с иллюстрациями, и поэкспериментировать на практике. Для начала попробуйте пример с изображения выше.
Раньше мы уже рассматривали различия скоупов singleton и prototype в Spring Framework. Допустим ситуацию, когда в singleton-компонент внедряется зависимость со скоупом prototype – когда будет создан её объект?
Если просто добавить к определению бина аннотацию @Scope(SCOPE_PROTOTYPE), и использовать этот бин в синглтоне через аннотацию @Autowired – будет создан только один объект. Потому что синглтон создается только однажды, и обращение к прототипу случится тоже однажды при его создании (при внедрении зависимости).
Примитивный способ получать новый объект при каждом обращении – отказаться от @Autowired, и доставать его из контекста вручную. Для этого нужно вызывать context.getBean(MyPrototype.class).
Воспользоваться автоматическим внедрением зависимостей можно через внедрение метода (паттерн «Команда»). Автовайрится не сам объект, а производящий его метод.
Более красивый декларативный способ – правильно настроить определение бина. В аннотации @Scope кроме самого scopeName доступен второй параметр – proxyMode. По умолчанию его значение NO – прокси не создается. Но если указать INTERFACES или TARGET_CLASS, то под @Autowired будет внедряться не сам объект, а сгенерированный фреймворком прокси. И когда проксируемый бин имеет скоуп prototype, то объект внутри прокси будет пересоздаваться при каждом обращении.
Лучший способ разобраться со скоупами – прочитать официальный гайд с иллюстрациями, и поэкспериментировать на практике. Для начала попробуйте пример с изображения выше.
👍27❤2
Чем отличается final finally finalize?
Тем, что это даже синтаксически разные вещи. Как и вопрос о методах Object, это способ начать разговор.
finalize – метод-финализатор из Object.
final – модификатор, который применяется к переменным, полям, методам и классам. Переменная или поле становится неизменяемым и требует инициализации. Финальный метод нельзя переопределить в наследниках. Финальный класс не может иметь наследников вообще. Используется для создания хорошего API по принципу наименьших привилегий.
Когда в методе используется локальная переменная внешней области видимости, она обязана быть effectively final. В этом случае ключевое слово final необязательно, но значение всё равно не должно меняться.
finally – часть языковой конструкции try-catch-finally.
Любое исключение, выброшенное из блока try переводит исполнение в самый соответствующий ему catch (при наличии). Этим продиктована необходимость располагать блоки catch в строгом порядке, от типа исключения-наследника, к родителю. В случае multicatch тот же порядок должен соблюдаться и внутри одного catch. Больше примеров про порядок.
После выполнится блок finally. Выполняется он в любом случае, было исключение или нет. Типичное использование – освобождение ресурсов, обязательные завершающие действия.
Для требующих финализации классов («ресурсов») добавляется интерфейс AutoCloseable, повторяющийся код блока final выносится в метод close и вызывается неявно в конце try-with-resources. Если в этой конструкции присутствует и явный final, он будет выполнен после.
Тем, что это даже синтаксически разные вещи. Как и вопрос о методах Object, это способ начать разговор.
finalize – метод-финализатор из Object.
final – модификатор, который применяется к переменным, полям, методам и классам. Переменная или поле становится неизменяемым и требует инициализации. Финальный метод нельзя переопределить в наследниках. Финальный класс не может иметь наследников вообще. Используется для создания хорошего API по принципу наименьших привилегий.
Когда в методе используется локальная переменная внешней области видимости, она обязана быть effectively final. В этом случае ключевое слово final необязательно, но значение всё равно не должно меняться.
finally – часть языковой конструкции try-catch-finally.
Любое исключение, выброшенное из блока try переводит исполнение в самый соответствующий ему catch (при наличии). Этим продиктована необходимость располагать блоки catch в строгом порядке, от типа исключения-наследника, к родителю. В случае multicatch тот же порядок должен соблюдаться и внутри одного catch. Больше примеров про порядок.
После выполнится блок finally. Выполняется он в любом случае, было исключение или нет. Типичное использование – освобождение ресурсов, обязательные завершающие действия.
Для требующих финализации классов («ресурсов») добавляется интерфейс AutoCloseable, повторяющийся код блока final выносится в метод close и вызывается неявно в конце try-with-resources. Если в этой конструкции присутствует и явный final, он будет выполнен после.
👍29
Что произойдет в результате компиляции и выполнения кода?
Anonymous Quiz
15%
Результат невозможно предугадать;
39%
Вывод в консоль - "0123456789";
31%
Ошибка - SyntaxError;
15%
Вывод в консоль - "0"
👍15🍌3🔥2❤🔥1
Какие существуют литералы?
Литерал – последовательность символов, обозначающая значение примитивного типа (или строки).
🔘 Целочисленные (Integer). Возможные форматы: 2019, 1__000_000 (с Java 7), 10048L (можно l, но будет путаться с 1), 0xfd12aa, 0b1011101, 07654321.
🔘 С плавающей точкой (floating-point). Возможные форматы: 123.4, 56.7e8, .07, 42F, 1.4D (избыточно, по умолчанию и так double).
🔘 Символы и строки. Символ (char) – в одинарных кавычках: 'R'. Спецсимволы пишутся с бэкслешем: '\n'. Любой символ можно представлять в виде escape-последовательности: '\u00F1'. Строковый литерал – последовательность символов в двойных кавычках: "Blabla". Для символов строки действуют те же правила.
🔘 Логические (boolean). true и false.
🔘 Специальный литерал null.
Все нюансы описаны в официальной документации.
Литерал – последовательность символов, обозначающая значение примитивного типа (или строки).
🔘 Целочисленные (Integer). Возможные форматы: 2019, 1__000_000 (с Java 7), 10048L (можно l, но будет путаться с 1), 0xfd12aa, 0b1011101, 07654321.
🔘 С плавающей точкой (floating-point). Возможные форматы: 123.4, 56.7e8, .07, 42F, 1.4D (избыточно, по умолчанию и так double).
🔘 Символы и строки. Символ (char) – в одинарных кавычках: 'R'. Спецсимволы пишутся с бэкслешем: '\n'. Любой символ можно представлять в виде escape-последовательности: '\u00F1'. Строковый литерал – последовательность символов в двойных кавычках: "Blabla". Для символов строки действуют те же правила.
🔘 Логические (boolean). true и false.
🔘 Специальный литерал null.
Все нюансы описаны в официальной документации.
👍18❤1
Что произойдет в результате компиляции и выполнения кода?
Anonymous Quiz
9%
Вывод в консоль - "true true"
47%
Вывод в консоль - "true false"
7%
Вывод в консоль - "false true"
31%
Вывод в консоль - "false false"
6%
Ошибка компиляции
👍18☃1
Чем отличаются checked и unchecked исключения?
Вопрос формулируют по-разному, суть вопроса – объяснение иерархии классов исключений. Подробно описано в документации.
Исключения бывают checked и unchecked. Checked требуется указывать в сигнатуре метода в разделе throws; перехватывать или добавлять в throws в вызывающем его методе. Unchecked можно добавить, но не обязательно, перехватывать не обязательно даже если указана в throws.
🔘 Throwable – базовый класс для всего что может быть использовано с оператором throw и в конструкции try-catch
🔘 RuntimeException – «нормальные» unchecked-исключения
🔘 Error – unchecked исключения, которые означают «серьезные проблемы» приложения. Не должны обрабатываться (хотя технически можно). Теоретически JVM может находиться в невалидном состоянии и не давать больше никаких гарантий
🔘 Exception (кроме RuntimeException) – checked исключения
Вопрос формулируют по-разному, суть вопроса – объяснение иерархии классов исключений. Подробно описано в документации.
Исключения бывают checked и unchecked. Checked требуется указывать в сигнатуре метода в разделе throws; перехватывать или добавлять в throws в вызывающем его методе. Unchecked можно добавить, но не обязательно, перехватывать не обязательно даже если указана в throws.
🔘 Throwable – базовый класс для всего что может быть использовано с оператором throw и в конструкции try-catch
🔘 RuntimeException – «нормальные» unchecked-исключения
🔘 Error – unchecked исключения, которые означают «серьезные проблемы» приложения. Не должны обрабатываться (хотя технически можно). Теоретически JVM может находиться в невалидном состоянии и не давать больше никаких гарантий
🔘 Exception (кроме RuntimeException) – checked исключения
👍18
Что произойдет в результате компиляции и выполнения кода? При условии что файл валиден и в процессе выполнения не выбросит FileNotFoundException
Anonymous Quiz
10%
Вывод в консоль символа H и выброс исключения IOException
21%
Вывод в консоль - HelloWorld!
12%
Вывод в консоль HelloWorld! и выброс исключения IOException в конце выполнения
20%
Пустой вывод в консоль
37%
Бесконечный вывод в консоль сообщения HelloWorld!
👍12
Что такое synchronized?
Можно применять как модификатор метода, и как самостоятельный оператор с блоком кода. Выполняет код при захваченном мониторе объекта. В виде оператора объект указывается явно. В виде модификатора нестатического метода используется this, статического – .class текущего класса.
Один из основных инструментов обеспечения потокобезопасности. Одновременно выполняется не более одного блока synchronized на одном и том же объекте. Такая блокировка называется intrinsic lock или monitor lock, подробно рассматривается в Java Concurrency in Practice 2.3.1.
Блок synchronized также необходим для использования методов wait, notify, notifyAll.
Можно применять как модификатор метода, и как самостоятельный оператор с блоком кода. Выполняет код при захваченном мониторе объекта. В виде оператора объект указывается явно. В виде модификатора нестатического метода используется this, статического – .class текущего класса.
Один из основных инструментов обеспечения потокобезопасности. Одновременно выполняется не более одного блока synchronized на одном и том же объекте. Такая блокировка называется intrinsic lock или monitor lock, подробно рассматривается в Java Concurrency in Practice 2.3.1.
Блок synchronized также необходим для использования методов wait, notify, notifyAll.
👍15
Что делает volatile?
volatile – ключевое слово для работы с многопоточностью. Не то же самое, что volatile в C++, не обязано делать что-либо с кэшем процессора. Оказывает на поле объекта ровно два эффекта.
Во-первых, чтение/запись такого поля становятся атомарными. Это применение актуально только для long и double, и не на всех платформах. Для остальных типов полей это верно и так.
Второй и самый интересный эффект – пара событий запись-чтение для такого поля являются synchronization actions. Значит, между ними существует отношение happens-before. Это значит, что существует гарантия, что произошедшее в памяти до записи будет видно после чтения. То есть будут успешно прочитаны значения, записанные в другие переменные.
Для полного понимания темы рекомендуется к просмотру доклад Алексея Шипилёва и документация. Лучше всего эффект volatile иллюстрирует задача из этого доклада, которую часто и дают в качестве этого вопроса. Вопрос – что выведет данный код:
Этот эффект используется для получения простой и дешевой адаптации программы к многопоточной среде без использования сложных и ошибкоопасных техник блокировок и синхронизаций.
volatile – ключевое слово для работы с многопоточностью. Не то же самое, что volatile в C++, не обязано делать что-либо с кэшем процессора. Оказывает на поле объекта ровно два эффекта.
Во-первых, чтение/запись такого поля становятся атомарными. Это применение актуально только для long и double, и не на всех платформах. Для остальных типов полей это верно и так.
Второй и самый интересный эффект – пара событий запись-чтение для такого поля являются synchronization actions. Значит, между ними существует отношение happens-before. Это значит, что существует гарантия, что произошедшее в памяти до записи будет видно после чтения. То есть будут успешно прочитаны значения, записанные в другие переменные.
Для полного понимания темы рекомендуется к просмотру доклад Алексея Шипилёва и документация. Лучше всего эффект volatile иллюстрирует задача из этого доклада, которую часто и дают в качестве этого вопроса. Вопрос – что выведет данный код:
int a; int b;Трюк в том, что помимо очевидных 21 (поток 2 отработал после 1), 00 (поток 2 отработал до 1, переменные еще не инициализированы) и 01 (поток 2 сработал между записями), может быть и неожиданные 20. Дело в том, что для операторов одного потока действует program order, он гарантирует хотя бы видимость правильной последовательности операций. Между потоками необходим «мост» из happens-before. Его даст применение модификатора volatile к переменной b, неожиданный результат 20 будет исключен.
// thread 1:
a = 1;
b = 2;
// thread 2:
System.out.print(b);
System.out.print(a);
Этот эффект используется для получения простой и дешевой адаптации программы к многопоточной среде без использования сложных и ошибкоопасных техник блокировок и синхронизаций.
👍28
Зачем нужно ключевое слово default?
Изначально (с Java 1.5) это слово использовалось для объявления дефолтного значения элементов аннотации.
В Java 8 вместе с лямбдами и стримами появилась острая необходимость дополнить стандартные интерфейсы новыми методами. Никто естественно не собирался ломать обратную совместимость, и было предложено добавить методы по умолчанию.
Теперь добавление ключевого слова default к методу интерфейса позволяет добавить ему тело. Все новые методы старых интерфейсов снабжаются дефолтной реализацией.
В реализации такого метода его дефолтный вариант вызывается тем же синтаксисом, что и внешний класс из вложенного: InterfaceName.super.methodName().
Методы по умолчанию подошли еще на шаг к введению в Java беспроблемной версии множественного наследования – примесям (mixin). Интерфейс не может иметь состояния, поэтому полноценные примеси всё ещё недоступны.
Изначально (с Java 1.5) это слово использовалось для объявления дефолтного значения элементов аннотации.
В Java 8 вместе с лямбдами и стримами появилась острая необходимость дополнить стандартные интерфейсы новыми методами. Никто естественно не собирался ломать обратную совместимость, и было предложено добавить методы по умолчанию.
Теперь добавление ключевого слова default к методу интерфейса позволяет добавить ему тело. Все новые методы старых интерфейсов снабжаются дефолтной реализацией.
В реализации такого метода его дефолтный вариант вызывается тем же синтаксисом, что и внешний класс из вложенного: InterfaceName.super.methodName().
Методы по умолчанию подошли еще на шаг к введению в Java беспроблемной версии множественного наследования – примесям (mixin). Интерфейс не может иметь состояния, поэтому полноценные примеси всё ещё недоступны.
👍16