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

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

✍️По всем вопросам: @Pascal4eg
Download Telegram
Функциональное программирование (ФП) - это парадигма программирования, которая базируется на работе с функциями как основными строительными блоками программ. В ФП данные рассматриваются как неизменяемые и функции рассматриваются как математические функции, которые принимают входные данные и возвращают результат без видимых побочных эффектов. Вот основные концепции функционального программирования:

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
PrintStream является классом, предназначенным для удобного вывода различных типов данных в поток вывода. Он является подклассом OutputStream и предоставляет методы для вывода данных различных примитивных типов, строк и объектов в удобном для чтения текстовом формате.

В этом примере PrintStream используется для вывода данных как в консоль, так и в файл. Он обеспечивает удобный способ форматирования и вывода данных различных типов. Класс PrintStream также автоматически преобразует различные типы данных в их текстовое представление перед выводом.
👍9👏1
😁23🤣2🔥1💯1
SOLIDпринципы объектно‑ориентированного программирования

SOLID — это аббревиатура пяти основных принципов проектирования в объектно‑ориентированном программировании — Single responsibility, Open-closed, Liskov substitution, Interface segregation и Dependency inversion.

В переводе на русский: принципы единственной ответственности, открытости / закрытости, подстановки Барбары Лисков, разделения интерфейса и инверсии зависимостей.

Аббревиатура SOLID была предложена Робертом Мартином, автором нескольких книг, широко известным в сообществе разработчиков. Следование принципам позволяет строить на базе ООП масштабируемые и сопровождаемые программные продукты с понятной бизнес‑логикой. Код, который написан с соблюдением принципов SOLID, проще понимать, поддерживать, расширять или изменять его функциональность.

Принцип единственной обязанности / ответственности (single responsibility principle / SRP) обозначает, что каждый объект должен иметь одну обязанность и эта обязанность должна быть полностью инкапсулирована в класс. Все его сервисы должны быть направлены исключительно на обеспечение этой обязанности.

Принцип открытости / закрытости (open-closed principle / OCP) декларирует, что программные сущности (классы, модули, функции и т. п.) должны быть открыты для расширения, но закрыты для изменения. Это означает, что эти сущности могут менять свое поведение без изменения их исходного кода.

Принцип подстановки Барбары Лисков (Liskov substitution principle / LSP) в формулировке Роберта Мартина: «функции, которые используют базовый тип, должны иметь возможность использовать подтипы базового типа не зная об этом».

Принцип разделения интерфейса (interface segregation principle / ISP) в формулировке Роберта Мартина: «клиенты не должны зависеть от методов, которые они не используют». Принцип разделения интерфейсов говорит о том, что слишком «толстые» интерфейсы необходимо разделять на более маленькие и специфические, чтобы клиенты маленьких интерфейсов знали только о методах, которые необходимы им в работе. В итоге, при изменении метода интерфейса не должны меняться клиенты, которые этот метод не используют.

Принцип инверсии зависимостей (dependency inversion principle / DIP) — модули верхних уровней не должны зависеть от модулей нижних уровней, а оба типа модулей должны зависеть от абстракций; сами абстракции не должны зависеть от деталей, а вот детали должны зависеть от абстракций.
👍17❤‍🔥1🔥1
public class Quest {
public static void main(String[] args) {
try {
System.out.println("Hello World");
} catch (IOException e) {
System.out.println("Error");
}
}
}