Java | Фишки и трюки
7.21K subscribers
182 photos
29 videos
6 files
40 links
Java: примеры кода, интересные фишки и полезные трюки

Купить рекламу: https://telega.in/c/java_tips_and_tricks

✍️По всем вопросам: @Pascal4eg
Download Telegram
🚩 Double braces инициализация - это создание и инициализация объекта в одном java выражении. Чаще всего используется с коллекциями.

На самом деле в этот момент происходит создание анонимного внутреннего класса, расширяющего ArrayList и определение в нём блока инициализации экземпляра.

Плюсы: код выглядит проще, красивее и понятнее.

Минусы:
📍 малоизвестный способ инициализации
📍 каждый раз создается дополнительный класс
📍 не работает, если класс, который мы пытаемся расширить, помечен как final
📍 содержит скрытую ссылку на внешний экземпляр, что может привести к утечкам памяти

Именно из за этих минусов данная инициализация является антипаттерном.

В современных версиях java есть более красивые и правильные варианты создания и инициализации коллекций.
Начиная с Java 8 это Stream API, а начиная с java 9 конструкция List.of().
👍62
Sentry for Java — это программный инструмент, призванный помочь разработчикам отслеживать и управлять ошибками, исключениями и сбоями в приложениях Java.

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

Ошибки которые не обрабатываются, Sentry соберёт автоматически, а те которые вы обработали, можно отправить вручную:

} catch (Exception e) {
Sentry.addBreadcrumb("readFile");
Sentry.captureException(e);
}


Подключение: добавить зависимость sentry-spring-boot-starter и прописать в конфиге sentry.dsn
🥰4👍2
📝 Запись (record) — это класс, объявленный с ключевым словом record вместо ключевого слова class. Запись служит контейнером неизменяемых данных и предназначена для лаконичного описания DTO (Data Transfer Object).

Класс, который компилятор создает для вас при создании записи, является окончательным (final).

Этот класс расширяет класс java.lang.Record. Таким образом, ваша запись не может расширять какой-либо класс.

Запись, как и класс, может реализовывать любое количество интерфейсов.

Блок, который следует сразу за именем записи, — (int x, int y) объявляет компоненты записи. Для каждого компонента записи компилятор создает private final поле и метод доступа к нему с тем же именем, что и у этого компонента. В записи может быть объявлено любое количество компонентов.

Компилятор сам создаёт конструктор со всеми перечисленными компонентами записи, а так же реализует методы toString(), equals() и hashCode() с реализацией по умолчанию.

record появился в Java 16.
🔥3
Защита от повторного запроса с помощью БД

Если вы разрабатываете Веб-приложение или REST-сервис, то рано или поздно столкнётесь с повторными запросами. Что имеется в виду? Объясню на примере Веб-страницы с кнопкой. По нажатию на кнопку, на бэкенд отправляется запрос. Запрос, соответственно, синхронный и пока серверная часть делает какую-то работу, браузер клиента показывает, что загружает страницу. Если это происходит продолжительное время, клиент может подумать, что его запрос завис и нажать кнопку ещё раз. Также повторное нажатие может произойти случайно.

Какая тут может произойти проблема? Если это, например, какой-то запрос данных, то в общем-то проблемы и нет, но если это действие, которое должно отработать только один раз, то тут могут быть весьма неприятные последствия. Для примера в интернет-магазине собрана корзина и создан заказ на оплату (статус REGISTERED), далее по нажатию кнопки "оплатить" с клиента списывают деньги и переводят заказ в статус оплачен (PAID). И если в этом процессе произойдёт двойной запрос, то с клиента могут списать деньги за заказ два раза.

Читать статью
👏62👍1
🔄 Бесконечные потоки

Интерфейс Stream имеет два статических метода для генерации бесконечных потоков: iterate() и generate().

iterate(final T seed, final UnaryOperator<T> f) возвращает бесконечный последовательный упорядоченный поток, созданный путем итеративного применения функции f к исходному элементу начального значения, создавая поток, состоящий из начального числа, f(начальное число), f(f(начальное число)) и т. д.

generate(Supplier<? extends T> s) возвращает бесконечный последовательный неупорядоченный поток, в котором каждый элемент создается предоставленным поставщиком (Supplier). Это подходит для генерации константных потоков, потоков случайных элементов и т. д.

📌 При работе с бесконечными потоками, крайне важно вызвать метод limit() перед вызовом терминальной операции, иначе наша программа будет работать бесконечно.
😁3👍1
Varargs (Variable Arguments List, изменяющийся список аргументов) — это способ создания методов, которые могут принимать произвольное количество аргументов одного типа (от нуля и более). Данная возможность появилась в JDK 5.

Запись вида Object... args и есть varargs.

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

Альтернативой varargs является перегрузка методов или передача в метод массива значений.

Varargs был создан с целью упрощения работы программиста, удобства и краткости кода.

📌 В качестве ограничения любой метод может использовать varargs только в единственном числе и строго последним аргументом.
❤‍🔥7👍3🔥2
Унарный оператор - это оператор, который принимает на вход один аргумент и возвращает некоторое значение.

К унарным операторам относятся: +, -, !
А так же, пре-унарный оператор и пост-унарный оператор.

- (оператор смены знака)
Собственно это всё что он делает - меняет знак переданного аргумента. Ещё есть побочный эффект от применения этого оператора, это повышение типа до int, в случае если аргумент имеет тип byte, short или char.

! - оператор логической инверсии. Применяется только к переменным типа boolean и превращает значение из true в false и наоборот.

Операторы инкремента (++) и декремента (--) применяются к целочисленным переменным и обладают двумя вариациям:
пост-инкремент/декремент (i++ и i--)
пре-инкремент/декремент (++i и --i)
Разница между вариациями в том, что ++i увеличивает переменную и возвращает новое значение, а i++ возвращает старое значение, а только затем увеличивает переменную.

Что же делает унарный + ? Да ничего не делает, только имеет такой же побочный эффект как и унарный -.
🔥7
Какой будет результат выполнения кода на картинке выше?
Anonymous Quiz
29%
x=5 y=9
29%
x=4 y=9
24%
x=4 y=10
18%
Ошибка компиляции
🤩7👍2
🚀🧵 Гармония в параллельном мире Java: Thread-Safe

В мире Java, параллельное выполнение потоков - это норма. Однако без должных мер предосторожности, оно может привести к состоянию гонки (race condition) и ошибкам. Вот почему важно понимать и применять концепцию Thread-Safe.

Thread-Safe означает, что ваш код или структуры данных могут безопасно использоваться из множества потоков, не вызывая нежелательных конфликтов. Как добиться Thread-Safety:

Синхронизация: Используйте synchronized или объекты блокировки (Locks), чтобы гарантировать, что только один поток обращается к ресурсу в определенный момент.

Использование неизменяемых (immutable) объектов: Объекты, которые нельзя изменить после их создания, являются потокобезопасными по определению.

Используйте потокобезопасные структуры данных: Java предоставляет такие структуры, как ConcurrentHashMap, которые спроектированы для безопасного параллельного доступа.

Атомарные операции: Используйте java.util.concurrent.atomic для атомарных операций над переменными.

Грамотное планирование потоков: Учтите порядок выполнения и жизненный цикл потоков.

Тестирование: Пишите тесты для обнаружения потенциальных проблем в параллельном выполнении.

Thread-Safe код - это залог безопасности и эффективности в параллельной Java. Помните об этом, разрабатывая многопоточные приложения.
#ThreadSafety
🔥91👍1
Weak Reference: Слабая связь с объектами

В мире Java, WeakReference - это мощный инструмент для управления памятью. Он позволяет создавать ссылки на объекты, которые могут быть автоматически удалены сборщиком мусора, если на них больше нет сильных ссылок (обычная ссылка на объект).

Чем это полезно? Это помогает избежать утечек памяти в приложениях, где объекты могут оставаться неиспользуемыми, но все еще иметь сильные ссылки.

Пример использования WeakReference:
WeakReference<MyObject> weakRef = new WeakReference<>(new MyObject());
Когда не будет сильных ссылок на MyObject, сборщик мусора автоматически удалит его, освобождая память. Это помогает оптимизировать использование ресурсов и снижать риск утечек памяти.

WeakReference - это мощный инструмент для управления памятью в Java, и его следует использовать там, где это необходимо для обеспечения эффективного управления ресурсами.
#WeakReference #УправлениеПамятью
9🥰2
🛠️ Системы сборки в мире Java — это незаменимые инструменты, которые делают процесс разработки проще и эффективнее.

Что бы ваш код стал работающим приложением, его нужно собрать в файл который можно запустить. Это может быть .war или .jar файл.

1️⃣ Apache Ant: Ant — это классическая система сборки Java, основанная на XML. Она обеспечивает гибкость и контроль над процессом сборки, но требует более подробной настройки в сравнении с Maven и Gradle. Считается устаревшей и практически не используется в современных проектах.

2️⃣ Apache Maven: Эта система сборки известна своей конфигурацией на основе управляемых проектов (POM), что упрощает управление зависимостями и сборку проектов. Maven также предоставляет обширную базу плагинов, которые облегчают автоматизацию различных задач. Конфигурация происходит в pom.xml файлах, соответственно в xml формате. На данный момент является наиболее распространенной системой сборки.

3️⃣ Gradle: Gradle предоставляет гибкую и мощную систему сборки с использованием Groovy DSL. Он позволяет разработчикам определять собственные задачи сборки и легко интегрировать их в проект. Gradle также поддерживает инкрементную сборку, что ускоряет разработку. Считается более современной системой по сравнении с Apache Maven и новые проекты часто создаются с использованием этой системы сборки.

Выбор системы сборки зависит от ваших потребностей и предпочтений. Независимо от того, какую вы выберете, они все помогут упростить и ускорить ваш процесс разработки на Java.
👍14🐳3
⌨️ Nashorn 🦏 — это движок JavaScript, разработанный на языке программирования Java первоначально Oracle, а затем сообществом OpenJDK. Nashorn был включен в JDK в версии Java 8 и до Java 14. С Java 15 его нужно подключать явно.

Исполнение JavaScript кода внутри Java приложения может быть полезным по нескольким причинам:

1⃣ Расширяемость: Вы можете использовать JavaScript для динамического настраиваемого поведения вашего Java приложения. Это позволяет легко добавлять и изменять функциональность без необходимости перекомпилировать и пересобирать код Java.

2⃣ Переиспользование кода: Если у вас уже есть существующий JavaScript код, его можно легко интегрировать в Java приложение, чтобы не создавать аналогичную функциональность на Java.

3⃣ Использование библиотек: JavaScript имеет богатую экосистему библиотек и фреймворков, которые могут быть полезными для решения определенных задач. Вы можете использовать их внутри Java приложения, чтобы ускорить разработку и добавить новые возможности.

Из кода Java в JavaScript можно передать данные, а так же получить обратно. Так же в JavaScript можно использовать Java классы.

📌 Однако следует помнить, что интеграция JavaScript и Java может вызвать сложности с обработкой ошибок, безопасностью и отладкой. Также, перед использованием JavaScript в Java приложении, стоит оценить, действительно ли это необходимо, и рассмотреть альтернативные способы решения задачи.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🦄4
Optional представляет собой класс, введенный в Java 8, который предназначен для борьбы с проблемами, связанными с отсутствием значения (null) и обеспечивает более безопасную и чистую обработку возможных отсутствующих значений.

Вот некоторые основные концепции и преимущества, связанные с Optional:

1⃣ Избавление от NullPointerException: Одной из основных задач Optional является предотвращение NullPointerException, которые могут возникнуть, когда мы пытаемся обратиться к методам или полям объекта, который оказывается null.

2⃣ Явное указание на отсутствие значения: Optional позволяет явно указать, что значение может быть отсутствующим, и предоставляет методы для проверки и обработки этого случая.

3⃣ Безопасные методы доступа к данным: Вместо того чтобы напрямую получать доступ к данным внутри Optional, мы используем методы, такие как isPresent(), ifPresent(), orElse(), orElseGet(), orElseThrow(), которые позволяют выполнить определенные действия, если значение присутствует, или вернуть альтернативное значение, если оно отсутствует.

Использование Optional способствует более чистому и читаемому коду, а также снижает вероятность ошибок, связанных с нулевыми значениями. Однако следует помнить, что Optional не всегда является наилучшим выбором, и его следует использовать там, где это имеет смысл с точки зрения бизнес-логики вашего приложения.
👍63
volatile - это ключевое слово, которое может быть применено к переменным. Ключевое слово volatile играет важную роль в управлении видимостью и синхронизацией данных между потоками. Когда переменная объявлена как volatile, это означает, что операции чтения и записи этой переменной будут синхронизированы между потоками.

Гарантированная видимость
Когда переменная помечена как volatile, она гарантированно обеспечивает видимость ее значения между потоками. Это означает, что если один поток изменяет значение volatile переменной, другие потоки увидят это изменение. Это особенно полезно в многопоточных сценариях, когда несколько потоков читают и записывают в одну и ту же переменную.

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

📌 volatile является мощным механизмом для обеспечения видимости изменений переменных между потоками без использования блокировок. Однако его следует использовать с осторожностью и только тогда, когда это действительно необходимо, так как в большинстве случаев синхронизация с использованием synchronized или java.util.concurrent библиотеки может быть более подходящим вариантом.
💯6👍3
⌨️ Инициализация это процесс задания начальных значений переменным и объектам перед их использованием. Этот процесс необходим для того, чтобы обеспечить корректное начальное состояние переменных и объектов в программе.

Порядок инициализации

1⃣ Статические переменные: если в классе есть статические переменные, то они будут инициализированы в первую очередь. Переменные будут инициализированы в том порядке в котором идут в коде.

2⃣ Статический блок инициализации: далее сработает блок инициализации статических переменных.

3⃣ Переменные экземпляра

4⃣ Блок инициализации переменных экземпляра

5⃣ Конструктор

Вывод кода:
Static Variables initialized!
Static Initialization Block initialized!
Instance Variables initialized!
Instance Initialization Block initialized!
Constructor initialized!


Инициализация в Java является важным аспектом, который гарантирует, что объекты и переменные находятся в правильном состоянии перед их использованием. Понимание порядка инициализации помогает избегать ошибок и создавать надежные программы на Java.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11👨‍💻2
Интернирование строк (string intern) - это процесс хранения одной и той же строки только в одном экземпляре в пуле строк (string pool) для оптимизации использования памяти и ускорения сравнения строк.

Вместо того чтобы создавать новый объект строки каждый раз, когда вы используете литерал строки (например, "Hello, World!"), Java проверяет, существует ли уже такая строка в пуле строк. Если она там есть, Java использует существующий экземпляр строки, иначе создает новый и добавляет его в пул строк.

Интернирование строк выполняется с использованием метода intern(), доступного для объектов класса String. Вызов этого метода на строке приводит к тому, что Java проверяет пул строк на наличие строки с таким же содержанием. Если строка уже существует в пуле строк, метод возвращает ссылку на существующий экземпляр. В противном случае он добавляет текущую строку в пул и возвращает ссылку на нее.

Интернирование строк имеет смысл использовать, если у вас есть множество одинаковых строк, и вы хотите оптимизировать использование памяти или ускорить сравнение строк. Однако не стоит интернировать каждую строку, так как это может привести к излишней нагрузке на пул строк и неэффективному использованию памяти.
😱5👍2😁2