ru
Feedback
Секреты Java

Секреты Java

Открыть в Telegram

Обсуждаем Java, архитектуру, фреймворки и всё, о чем должны говорить разработчики. Владелец: @Alexey_Morl Реклама на бирже: https://telega.in/c/java_secrets

Больше
7 991
Подписчики
Нет данных24 часа
Нет данных7 дней
Нет данных30 день
Привлечение подписчиков
декабрь '25
декабрь '250
в 0 каналах
ноябрь '250
в 0 каналах
Get PRO
октябрь '250
в 0 каналах
Get PRO
сентябрь '250
в 0 каналах
Get PRO
август '250
в 0 каналах
Get PRO
июль '250
в 0 каналах
Get PRO
июнь '250
в 0 каналах
Get PRO
май '250
в 0 каналах
Get PRO
апрель '250
в 0 каналах
Get PRO
март '250
в 0 каналах
Get PRO
февраль '250
в 0 каналах
Get PRO
январь '25
+1
в 0 каналах
Get PRO
декабрь '240
в 0 каналах
Get PRO
ноябрь '24
+4
в 1 каналах
Get PRO
октябрь '24
+927
в 52 каналах
Get PRO
сентябрь '24
+581
в 7 каналах
Get PRO
август '24
+748
в 11 каналах
Get PRO
июль '24
+800
в 34 каналах
Get PRO
июнь '24
+729
в 15 каналах
Get PRO
май '24
+2 085
в 29 каналах
Get PRO
апрель '24
+1 083
в 14 каналах
Get PRO
март '24
+1 115
в 13 каналах
Get PRO
февраль '24
+2 427
в 8 каналах
Get PRO
январь '24
+285
в 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.

2
Можно ли поймать Error? Технически, Error как и любой другой Throwable можно поймать в блок catch. Такой код абсолютно валидный и скомпилируется без проблем. На практике, согласно спецификации, значение исключения типа Error – необрабатываемая ошибка, ловить которую нет смысла. Более того, выброс исключения подтипа VirtualMachineError означает, что JVM находится в сломанном состоянии. Дальнейшая работа непредсказуема: OutOfMemoryError приводит к невозможности создания новых объектов, StackOverflowError теряет фреймы стека вызова, и так далее. Так как ловить Error не нужно, объявление её в секции throws сигнатуры метода необязательно. Как и RuntimeException, Error – разновидность unchecked exception.
2 546
3
Java - компилируемый или интерпретируемый язык? Java - это язык программирования, который обычно описывают как "компилируемый и интерпретируемый". Давайте разберемся, что это означает и как это влияет на процесс создания и запуска Java программ. Компиляция: ▫️Когда вы пишете Java код, вы его пишите в файл с расширением .java. ▫️Этот файл с исходным кодом передается компилятору Java (например, javac идущий в JDK), который преобразует его в байт-код. ▫️В результате компиляции создаются файлы классов с расширением .class, содержащие байт-код, который представляет собой набор инструкций, понятных JVM. Пример компиляции Java кода: javac MyClass.java Программа еще не запущена! Компиляция - это все еще процесс разработки. Интерпретация: ▫️Когда вы запускаете программу на Java, созданный байт-код передается JVM. ▫️JVM пошагово (построчно, пооператорно), в реальном времени, преобразует байт-код в машинный код и выполняет его. ▫️В процессе выполнения JVM может также проводить оптимизацию и JIT (Just-In-Time) компиляцию некоторых частей кода в машинный код для повышения производительности (JIT компиляция - отдельная тема). Пример запуска Java программы: java MyClass Вот теперь мы запустили программу. Вывод: Java использует смешанный подход, объединяя в себе компиляцию и интерпретацию. Это позволяет создавать эффективные и переносимые программы, которые могут быть запущены на различных платформах без необходимости перекомпиляции исходного кода.
2 252
4
Приведите примеры использования fork/join framework Как следует из названий связанных классов, ForkJoinPool используется для рекурсивных задач. Это такие задачи, которые можно делить на порции, подзадачи. Отделение подзадачи – это операция fork, финальная агрегация результатов подзадач – join. Реализация fork/join для самых популярных общих случаев уже есть в стандартной библиотеке, работать непосредственно с классом ForkJoinPool не потребуется. Метод parallelSetAll из класса Arrays применяет fork/join для генерации элементов массива; parallelPrefix для модификации; parallelSort для сортировки. Фреймворк неявно работает и в параллельных стримах. В этом случае логику fork определяет его сплитератор, а join выполняют потоковые операции. Классический пример: Arrays.stream(new int[]{1, 2, 3, 4}).parallel().sum(); Существуют целые категории частных задач, решения которых хорошо параллелизуются: векторные операции, работа с графами, поиск данных. Для специфичных задач придется реализовывать собственные RecursiveTask, RecursiveAction, или Spliterator.
2 094
5
Сколько вам лет?
775
6
Сколько вам лет?
532
7
Что такое и как создать daemon thread? Демон в широком значении – фоновая программа. В Java потоки-демоны имеют схожий смысл: это потоки для фоновых действий по обслуживанию основных потоков. Потоки не-демоны называются пользовательскими (user thread). Тред создается демоном, если его родитель демон. Свойство Java-треда isDaemon можно переключать в любой момент до старта потока. По сравнению с пользовательскими потоками демоны имеют меньший приоритет выполнения. Когда все пользовательские треды завершились, JVM завершает работу. Демоны не выполняют самостоятельных задач, поэтому не препятствуют остановке, программа завершается не дожидаясь окончания их работы. Daemon thread может быть полезен для таких действий, как инвалидация кэша, периодическая актуализация значений из внешних источников, освобождение неиспользуемых пользовательских ресурсов.
2 134
8
Зачем выбирать ReentrantLock вместо synchronized? Объект класса ReentrantLock решает те же задачи, что и блок synchronized. Поток висит на вызове метода lock() в ожидании своей очереди занять этот объект. Владеть локом, как и находиться внутри блока synchronized может только один поток одновременно. unlock(), подобно выходу из блока синхронизации, освобождает объект-монитор для других потоков. В отличие от блока синхронизации, ReentrantLock дает расширенный интерфейс для получения информации о состоянии блокировки. Методы лока позволяют еще до блокировки узнать, занят ли он сейчас, сколько потоков ждут его в очереди, сколько раз подряд текущий поток завладел им. Шире и возможные режимы блокировки. Кроме обычного ожидающего lock(), вариант tryLock() с параметром ожидает своей очереди только заданное время, а без параметра – вообще не ждет, а только захватывает свободный лок. Еще одно отличие – свойство fair. Лок с этим свойством обеспечивает «справедливость» очереди: пришедший раньше поток захватывает объект раньше. Блок synchronized не дает никаких гарантий порядка.
2 016
9
Вызов конструктора из другого конструктора После одного из тестов были вопросы по поводу вызова одного конструктора из другого. Давайте пройдемся по этой теме. Для чего нам вообще вызывать один конструктор из другого? Это позволяет нам избежать дублирования кода, когда несколько конструкторов имеют общую инициализацию. При вызове одного конструктора из другого используется ключевое слово this и за ним следует список аргументов, передаваемых в вызываемый конструктор. Вы можете использовать вызов только одного конструктора и это должно быть первым оператором в теле вашего конструктора! Рассмотрим пример: public class MyClass { private int x; // Конструктор по умолчанию public MyClass() { this(0); // Вызов конструктора с одним аргументом } // Конструктор с одним аргументом public MyClass(int x) { this.x = x; //может быть еще какая-то логика } } В примере выше MyClass имеет два конструктора: 1. Конструктор по умолчанию вызывает конструктор с одним аргументом, передавая ему значение по умолчанию. 2. Конструктор с одним аргументом инициализирует поле класса значением, переданным в аргументе. Если у нас есть дочерний класс: class ChildClass extends MyClass { public ChildClass() { super(1); } } Мы точно так же можем вызвать конструктор родительского класса, но с помощью ключевого слова super. Правила все те же, что и для ключевого слова this! Обратите внимание, что можно записать и таким образом: class ChildClass extends MyClass { public ChildClass() { } } Тогда неявно вызовется конструктор родительского класса по умолчанию. Помните правило - конструктор родительского класса вызывается всегда! Если его не вызвали вы явно, его вызовет компилятор (при наличии конструктора по умолчанию родительского класса) или он же сообщит, что нужно вызвать явно (при отсутствии конструктора по умолчанию родительского класса).
1 877
10
Что такое Snapshot версия у артефактов Maven? Наверняка вы встречали такие зависимости у которых в версии стоит слово SNAPSHOT. Пример: <dependency> <groupId>com.example</groupId> <artifactId>my-library</artifactId> <version>1.0.0-SNAPSHOT</version> </dependency> Что это значит? SNAPSHOT версия в Maven - это версия, которая все еще разрабатывается и может меняться. Она удобна для тестирования и обмена обновлениями на стадии разработки. Release версия (без суффикса SNAPSHOT)- это окончательная, стабильная версия проекта, которая готова к использованию и меняться уже не будет (только от версии к версии). Если ваше приложение использует какую-либо библиотеку с версией SNAPSHOT, это значит ее автор может доработать/удалить функционал и залить новый артефакт не поменяв версию. Совесть автора чиста, а ваше приложение после обновления зависимостей может не собраться.
1 831
11
Casting Casting используется для преобразования переменной одного типа данных в другой. Существуют два типа преобразования: р+1
Casting Casting используется для преобразования переменной одного типа данных в другой. Существуют два типа преобразования: расширяющее (Widening Casting) и сужающее (Narrowing Casting). Расширяющее преобразование (Widening Casting): Процесс преобразования данных меньшего типа в больший тип называется расширяющим преобразованием. Это преобразование выполняется автоматически. (изображение 1) Сужающее преобразование (Narrowing Casting): Преобразование данных большего типа в меньший тип называется сужающим преобразованием. Это преобразование должно выполняться вручную. (изображение 2)
1 838
12
Unit тесты с Hamcrest В посте хочу рассказать про довольно полезную либу. Hamcrest - это часто используемая библиотека для написания модульных тестов в Java в помощь JUnit. В замен использования многочисленных методов assert JUnit, Hamcrest предоставляет единственный оператор assertThat и стандартный набор сопоставителей (Matcher), которые закрывают большую часть необходимых кейсов. Примеры тестирования строковых значений: String str = "Hello"; assertThat(str, equalToIgnoringCase("hello")); assertThat(str, startsWith("H")); assertThat(str, containsString("elo")); В принципе из названий методов явно понятно, что они проверяют. Этот фреймворк поддерживает работу со всеми типами данных и коллекций. Но основное его преимущество это возможность написания лаконичных проверок для произвольных Java объектов. Помимо проверки определенных полей, мы можем сравнивать объекты целиком. Вам не нужно писать пошаговое сравнение всех полей объектов, за вас это сделает Hamcrest: public class Book { String author; String title; ... } @Test public void test() { Book book1 = new Book("Robert Martin", "Clean Code"); Book book2 = new Book("Joshua Bloch", "Effective Java"); assertThat(book1, instanceOf(Book.class)); assertThat(book1, hasProperty("title")); assertThat(book1, samePropertyValuesAs(book2)); } Третий assertThat провалит проверку, т.к. значения полей у объектов book1 и book2 разные, при этом он покажет детализированную информацию об ошибках: Expected: same property values as Book [author: "Joshua Bloch", title: "Effective Java"] but: author was "Robert Martin" Используя подобные механизмы вы значительно сократите время написания тестов, а так же улучшите их читаемость. upd: Либа достоточно устаревшая, и не поддерживается, как написали в комментах. В других постах рассмотрим аналоги.
1 916
13
Unit тесты с Hamcrest В посте хочу рассказать про довольно полезную либу. Hamcrest - это часто используемая библиотека для на
Unit тесты с Hamcrest В посте хочу рассказать про довольно полезную либу. Hamcrest - это часто используемая библиотека для написания модульных тестов в Java в помощь JUnit. В замен использования многочисленных методов assert JUnit, Hamcrest предоставляет единственный оператор assertThat и стандартный набор сопоставителей (Matcher), которые закрывают большую часть необходимых кейсов. Примеры тестирования строковых значений: String str = "Hello"; assertThat(str, equalToIgnoringCase("hello")); assertThat(str, startsWith("H")); assertThat(str, containsString("elo")); В принципе из названий методов явно понятно, что они проверяют. Этот фреймворк поддерживает работу со всеми типами данных и коллекций. Но основное его преимущество это возможность написания лаконичных проверок для произвольных Java объектов. Помимо проверки определенных полей, мы можем сравнивать объекты целиком. Вам не нужно писать пошаговое сравнение всех полей объектов, за вас это сделает Hamcrest: public class Book { String author; String title; ... } @Test public void test() { Book book1 = new Book("Robert Martin", "Clean Code"); Book book2 = new Book("Joshua Bloch", "Effective Java"); assertThat(book1, instanceOf(Book.class)); assertThat(book1, hasProperty("title")); assertThat(book1, samePropertyValuesAs(book2)); } Третий assertThat провалит проверку, т.к. значения полей у объектов book1 и book2 разные, при этом он покажет детализированную информацию об ошибках: Expected: same property values as Book [author: "Joshua Bloch", title: "Effective Java"] but: author was "Robert Martin" Используя подобные механизмы вы значительно сократите время написания тестов, а так же улучшите их читаемость. upd: Либа достоточно устаревшая, и не поддерживается, как написали в комментах. В других постах рассмотрим аналоги.
1
14
Collection только для чтения В Java есть полезные методы, позволяющие сделать класс Collection доступным только для чтения. М
Collection только для чтения В Java есть полезные методы, позволяющие сделать класс Collection доступным только для чтения. Мы можем сделать коллекцию только для чтения, используя один из следующих методов.
1 806
15
Правила переопределения метода Object.equals(). 1. Использование оператора == для проверки, является ли аргумент ссылкой на указанный объект. Если является, возвращается true. Если сравниваемый объект == null, должно вернуться false. 2. Использование оператор instanceof и вызова метода getClass() для проверки, имеет ли аргумент правильный тип. Если не имеет, возвращается false. 3. Приведение аргумента к правильному типу. Поскольку эта операция следует за проверкой instanceof она гарантированно будет выполнена. 4. Обход всех значимых полей класса и проверка того, что значение поля в текущем объекте и значение того же поля в проверяемом на эквивалентность аргументе соответствуют друг другу. Если проверки для всех полей прошли успешно, возвращается результат true, в противном случае - false. По окончанию переопределения метода equals() следует проверить: является ли порождаемое отношение эквивалентности рефлексивным, симметричным, транзитивным и непротиворечивым? Если ответ отрицательный, метод подлежит соответствующей правке.
2 147
16
Что значит «усыпить» поток? Это значит приостановить его на определенный промежуток времени, вызвав в ходе его выполнения статический метод Thread.sleep() и передав в качестве параметра необходимое количество времени в миллисекундах. До истечения этого времени поток может быть выведен из состояния ожидания вызовом interrupt() с выбрасыванием InterruptedException.
2 078
17
Использование метода Map.computeIfAbsent() для ленивой инициализации значений Метод computeIfAbsent() представляет собой мощн
Использование метода Map.computeIfAbsent() для ленивой инициализации значений Метод computeIfAbsent() представляет собой мощный инструмент для ленивой инициализации значений в Map в Java. Он улучшает читаемость и эффективность кода, позволяя избежать лишних проверок и упрощая добавление значений в коллекцию. Эта фишка особенно полезна при работе с динамическими данными, где необходимо эффективно управлять созданием и обновлением значений в Map.
2 414
18
Fluent Interface Fluent Interface (Текучий интерфейс) — это шаблон проектирования, который позволяет создавать код, читающийс
Fluent Interface Fluent Interface (Текучий интерфейс) — это шаблон проектирования, который позволяет создавать код, читающийся как текст на естественном языке. Он достигается путем создания методов, которые возвращают ссылку на this объект, позволяя вызывать их в цепочке.
2 236
19
Какие существуют unchecked exception? Наиболее часто встречающиеся: ArithmeticException, ClassCastException, ConcurrentModificationException, IllegalArgumentException, IllegalStateException, IndexOutOfBoundsException, NoSuchElementException, NullPointerException, UnsupportedOperationException.
2 067
20
Spring аннотация @Lazy Как известно Spring создает бины сразу при поднятии контекста и со скоупом Singletone. Но бывают случаи, когда мы не хотим создавать его сразу. И Spring дает нам такую возможность с аннотацией @org.springframework.context.annotation.Lazy. Эта аннотация используется в Spring Framework для отложенной инициализации бина. Это означает, что бин будет создан только при первом обращении к нему. Мы можем использовать @Lazy над классом конфигурации: @Lazy @Configuration public class MyConfiguration { @Bean public MyBean myBean() { return new MyBean(); } ... } Это значит что все бины создаваемые в этом классе будут инициализироваться при первом обращении к ним. Либо мы можем повесить @Lazy на метод с @Bean, чтобы указать только один бин для ленивой загрузки. Рассмотрим пример с @Autowired: @Lazy @ToString @Component public class Apple { public Apple() { System.out.println( "Apple bean initialized"); } } У нас есть класс с аннотацией @Lazy, который так же помечен как @Component. И есть класс куда мы инжектим этот бин: @Component public class Bag { public Apple apple; @Autowired public Bag(@Lazy Apple apple) { this.apple = apple; System.out.println( "Bag bean initialized"); } } Обратите внимание, что @Lazy так же и над аргументом конструктора (если инжектим через него), это обязательно! Мы можем поднять контекст и посмотреть, что происходит: @SpringBootApplication public class Application { public static void main(String[] args) { var context = SpringApplication .run(Application.class, args); System.out.println("Context is up"); Bag bag = context.getBean(Bag.class); System.out.println("Bag is gotten"); System.out.println(bag.apple); } } Вывод будет следующим: Bag bean initialized Context is up Bag is gotten Apple bean initialized Apple() Как мы видим - сначала создастся бин класса Bag и поднимется контекст, и только после обращения к бину Apple через Bag, бин Apple проинициализируется. Использование аннотации @Lazy особенно полезно в больших приложениях, где не все бины нужны сразу и можно сэкономить ресурсы, инициализируя их только по мере необходимости.
2 455