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

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

✍️По всем вопросам: @Pascal4eg
Download Telegram
Динамическое программирование (Dynamic Programming, DP) - это метод решения сложных задач путем разбиения их на более простые подзадачи и решения каждой подзадачи только один раз, сохраняя результаты вычислений для избежания повторных вычислений. Затем эти результаты могут быть использованы для построения решения исходной задачи, что значительно сокращает время выполнения программы. Этот метод часто используется в оптимизации, оптимальном управлении и решении задач, где необходимо максимизировать или минимизировать какую-то целевую функцию.

Примером задачи, которая часто решается с использованием динамического программирования, является задача о рюкзаке (Knapsack problem), где необходимо выбрать определенное количество предметов с определенными весами и стоимостями так, чтобы сумма весов не превышала заданную вместимость рюкзака, а сумма стоимостей была максимальной.
👍13🤩3
Ключевое слово instanceof — это бинарный оператор, и мы можем использовать его для проверки того, является ли определенный объект экземпляром заданного типа. Следовательно, результат операции либо истинен, либо ложен. Кроме того, ключевое слово instanceof — это наиболее распространенный и простой способ проверить, является ли объект подтипом другого типа.

Обратите внимание, если с помощью instanceof проверить переменную содержащую null, то результат всегда будет false.
👍11👎1🔥1
SequencedCollection

В Java 21 появилась новая группа интерфейсов коллекций, самым основным из которых является SequencedCollection. Он расширяет базовый интерфейс Collection, добавляя в него ряд полезных методов для манипуляций с первым и последним элементами, а также для инвертирования коллекции:

interface SequencedCollection<E> extends Collection<E> {
void addFirst(E e);
void addLast(E e);
E getFirst();
E getLast();
E removeFirst();
E removeLast();
SequencedCollection<E> reversed();
}


Две основных реализации интерфейса List (ArrayList и LinkedList) также поддерживают этот интерфейс.
👍20🔥3
Функциональное программирование (ФП) - это парадигма программирования, которая базируется на работе с функциями как основными строительными блоками программ. В ФП данные рассматриваются как неизменяемые и функции рассматриваются как математические функции, которые принимают входные данные и возвращают результат без видимых побочных эффектов. Вот основные концепции функционального программирования:

1⃣ Функции как объекты первого класса: В функциональном программировании функции рассматриваются как объекты первого класса, что означает, что они могут быть переданы как аргументы в другие функции, возвращены из других функций и сохранены в переменных.

2⃣ Неизменяемость: В ФП данные считаются неизменяемыми, что означает, что после создания объекта его нельзя изменить. Вместо этого создаются новые объекты с измененными значениями. Это способствует более предсказуемому и безопасному поведению программы.

3⃣ Функции без побочных эффектов: Функции в ФП должны быть без побочных эффектов, что означает, что они не должны изменять глобальное состояние программы или какие-либо внешние переменные. Это делает код более чистым и предсказуемым.

4⃣ Рекурсия: Рекурсия является важной концепцией в функциональном программировании. Вместо циклов, ФП часто использует рекурсивные функции для выполнения итераций.

5⃣ Функции высшего порядка: Функции высшего порядка - это функции, которые могут принимать другие функции в качестве аргументов или возвращать их в качестве результатов. Это позволяет абстрагировать общие операции и создавать более гибкий и модульный код.

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

Функциональное программирование часто используется в ситуациях, где требуется обработка и анализ данных, а также в многозадачных и распределенных системах. Языки программирования, поддерживающие ФП, включают Haskell, Lisp, Scala, Clojure, и функциональные возможности в языках, таких как Python, JavaScript и Java. ФП позволяет писать более декларативный и выразительный код, что может способствовать повышению производительности и упрощению разработки в некоторых случаях.
👍8💯21🔥1
Класс Console

В JDK 6 и более поздних версиях мы можем использовать класс Console из пакета java.io для чтения и записи в консоль.

Если нам нужно прочитать конфиденциальную информацию, например пароль, мы можем использовать метод readPassword(), чтобы запросить у пользователя пароль и прочитать пароль из консоли с отключенным эхом.
👍17🔥4
public class Increment {
public static void main(String[] args) {
int j = 0;
for (int i = 0; i < 100; i++) {
j = j++;
}
System.out.println(j);
}
}
3👍2
Что выведет код?
Anonymous Quiz
24%
0
49%
100
16%
101
10%
ошибка компиляции
🎉5
Стековая память

Для оптимального запуска приложения, JVM делит память на стек и кучу. Всякий раз, когда мы объявляем новые переменные и объекты, вызываем новый метод, объявляем строку или выполняем аналогичные операции, JVM выделяет память для этих операций либо из стековой памяти, либо из кучи.

Стековая память в Java используется для распределения статической памяти и выполнения потока - каждый поток исполнения в Java имеет свой собственный стек, создаваемый вместе с потоком. Этот стек содержит кадры стека, каждый из которых представляет вызов метода. Каждый кадр содержит примитивные значения, специфичные для метода (переданные в метод и объявленные в нем), и ссылки на объекты, находящиеся в куче.

Стековая память следует принципу LIFO. Последний метод, добавленный в стек при вызове, будет первым, который завершится (выйдет из стека) при завершении выполнения.

Когда метод завершает выполнение, соответствующий ему кадр стека очищается, поток возвращается к вызывающему методу, и место становится доступным для следующего метода.

Ключевые особенности стека:

👉 Он увеличивается и уменьшается по мере вызова и возврата новых методов соответственно.

👉 Переменные внутри стека существуют только до тех пор, пока работает метод, создавший их.

👉 Он автоматически выделяется и освобождается, когда метод завершает выполнение.

👉 Каждый рекурсивный вызов метода также создает новый кадр стека

👉 Если эта память заполнена, Java выдает java.lang.StackOverFlowError.

👉 Доступ к этой памяти происходит быстрее по сравнению с кучей.

👉 Эта память является потокобезопасной, поскольку каждый поток работает в своем собственном стеке.
👍171🔥1
DataInputStream и DataOutputStream предоставляют методы для чтения и записи примитивных данных из и в поток ввода/вывода. Эти классы добавляют функциональность к обычным потокам ввода/вывода, позволяя удобно считывать и записывать данные различных типов, таких как целые числа, числа с плавающей запятой, символы и т.д., без необходимости вручную преобразовывать их в байты.
👍11👏1
Чистый код: создание, анализ и рефакторинг
Автор: Роберт Мартин

Эту книгу должен прочитать каждый разработчик. А затем перечитывать каждый год! 😁 Плюс для джавистов в том что в книге все примеры написаны на Java.

"Чистый код" Роберта Мартина – это практическое руководство для разработчиков, нацеленное на улучшение качества программного кода. Автор предлагает читателям принципы и практики, которые помогут создавать код, который легко понимать, поддерживать и изменять. Книга рассматривает множество аспектов программирования, включая именование переменных, организацию функций, обработку ошибок, комментирование и многое другое. С использованием конкретных примеров и иллюстраций автор помогает программистам развивать навыки написания "чистого кода" для повышения эффективности и удовлетворения требований современной разработки программного обеспечения.
👍21🔥2
Память кучи

В Java память кучи (heap memory) является областью памяти, где хранятся объекты и массивы. Куча управляется сборщиком мусора (garbage collector), который автоматически освобождает память, занятую объектами, которые больше не используются.

Динамическое выделение
Объекты в Java создаются динамически во время выполнения программы. Куча предоставляет пространство для хранения этих объектов, и их размер может изменяться в процессе выполнения программы.

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

Поколения кучи
Память кучи в Java обычно делится на три поколения: молодое поколение (young generation), поколение средней жизни (tenured/old generation) и поколение постоянных объектов (permanent generation, до Java 7) или область метаспейса (metaspace, начиная с Java 8).

👉 Молодое поколение содержит новые объекты, которые создаются в программе. Сборка мусора часто выполняется в этом поколении.

👉 Поколение средней жизни содержит объекты, которые пережили несколько циклов сборки мусора в молодом поколении.

👉 Поколение постоянных объектов (или область метаспейса) содержит метаданные классов и постоянные строки. В Java 8 и более поздних версиях используется метаспейс вместо постоянного поколения.

Настройка кучи
Размеры и параметры работы сборщика мусора и памяти кучи можно настраивать с использованием опций командной строки при запуске Java-приложения. Например, можно указать максимальный и начальный размеры кучи, выбрать алгоритм сборки мусора и другие параметры.

Производительность и оптимизации
Память кучи и сборка мусора в Java подвергаются постоянным оптимизациям и улучшениям в новых версиях JDK. Производительность приложений может зависеть от эффективности работы сборщика мусора и правильного управления памятью.
🔥12👍62👌1
😁35👍2
Параметр fetch в аннотациях @ManyToOne, @ManyToMany, @OneToOne, @OneToMany определяет как будет загружаться связанная сущность: вместе с загрузкой родительской сущности или в момент обращения к аннотированному полю.

Этот параметр может принимать два значения:

EAGER: при загрузке родительской сущности будет загружена и дочерняя сущность. Это один SQL-запрос с выбором родительской сущности к которой присоединяется (LEFT JOIN) дочерняя.

LAZY: при загрузке родительской сущности, дочерняя сущность загружена не будет. Вместо нее будет создан proxy-объект. С помощью этого proxy-объекта Hibernate будет отслеживать обращение к этой дочерней сущности и при первом обращении загрузит ее в память.

Какой же использовать? Если сущности не большие и связей не много, а проект не сильно нагруженный, то можно использовать EAGER, иначе нагрузка на БД может быть большой и в результате приложение может стать задумчивым.

Но LAZY не панацея, например мы можем выбрать список сущностей и что то с ними делать в цикле обращаясь к полю LAZY, в результате мы получим дополнительный запрос на каждый элемент списка, что так же увеличит нагрузку на БД. В данном случае нужно выбрать это поле как EAGER.

Получается что у нас могут быть разные варианты использования выбранной сущности. Как же быть? Первое что приходит в голову это сделать набор сущностей которые смотрят на одну и ту же таблицу, но в которых параметр fetch задан по разному, в зависимости от использования сущности. Но такой способ выглядит криво: дублирование кода, проблемы с передачей сущности в методы (классы сущностей все разные). В следующих постах поговорим о том как этого избежать. Не переключайте канал.
👍17🔥5👏1
JPA Entity Graph - это механизм, предоставляемый JPA, который позволяет явно указывать, какие атрибуты сущности должны быть загружены или проигнорированы во время выполнения запросов к базе данных. Это может быть полезно для управления жадной загрузкой (eager loading) и ленивой загрузкой (lazy loading) связанных сущностей.

JPA Entity Graph предоставляет более гибкий и декларативный способ управления загрузкой связанных данных по сравнению с использованием параметра fetch в аннотациях связи с другими сущностями. Он также позволяет избежать проблем с N+1 запросами, когда множество связанных сущностей загружается в отдельных запросах.

В нашем примере мы создали именованный граф сущности (person-with-address), который включает атрибуты name из сущности Person и также включает атрибут address с дополнительным подграфом (address-subgraph), который включает атрибут city из сущности Address.

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

Entity Graph можно так же создавать динамически используя метод createEntityGraph вместо getEntityGraph.
👍13🔥2
Какое объявление i превращает этот цикл в бесконечный? while (i == i + 1) { }
Anonymous Quiz
29%
double i = 1.0 / 0.0;
17%
double i = 0.0 / 1.0;
18%
double i = Double.MAX_VALUE;
6%
double i = Double.MIN_VALUE;
30%
решения не существует
5🔥1🎉1
Чем отличаются эти два способа определения массива?

int[] array = new int[]{1,2,3}
int[] array = {1,2,3}

Ответ: ничем не отличается, эти два определения идентичны на уровне байт-кода.
👍36😁4🔥3
Проблема N+1 (N+1 Issue) в JPA (Java Persistence API) возникает, когда при получении списка сущностей с их связанными сущностями (например, при использовании отношения OneToMany или ManyToOne), для каждой основной сущности выполняется дополнительный запрос для загрузки связанной сущности. Это приводит к выполнению N+1 запросов к базе данных, где N - количество основных сущностей.

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

Использование управления загрузкой, такого как JOIN FETCH или использование JPA Entity Graphs, позволяет предотвратить проблему N+1 и повысить производительность при работе с базой данных через JPA.
👍17🔥1