Java | Фишки и трюки
6.92K subscribers
183 photos
33 videos
6 files
44 links
Java: примеры кода, интересные фишки и полезные трюки

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

✍️По всем вопросам: @Pascal4eg

Менеджер по рекламе: @shmyzna
Download Telegram
☕️Text Blocks в Java: читаемые строки без \n и слёз

До Java 13 работа с многострочными строками была болью. JSON? HTML? SQL? Только через "\n" + и кучу экранирования.
Но с Text Blocks всё стало проще, понятнее и читаемо.

Сравни:

До:
String html = "<html>\n" +
" <body>Hello</body>\n" +
"</html>";


После (Text Block):

String html = """
<html>
<body>Hello</body>
</html>
""";


➡️ Стало чище, короче и как в оригинале. Это обычная String, просто оформленная красиво.

✔️ Преимущества Text Blocks:

1. 👍 Читаемость на уровне "как есть"
Код больше не превращается в кашу. Особенно полезно для:

SQL-запросов:
     String query = """
SELECT id, name
FROM users
WHERE active = true
ORDER BY created_at DESC
""";


HTML/JSON шаблонов:

     String json = """
{
"name": "Alice",
"role": "admin"
}
""";


2. 👍 Меньше экранирования
Забудь про \", \\n, \\t — теперь можно писать почти как в блокноте.

3. 👍 Автоматическое выравнивание
Java сама уберёт начальные отступы на основе самой "узкой" строки.

Пример:
   String msg = """
Line 1
Line 2
Line 3
""";


➡️ Результат будет:
Line 1
Line 2
Line 3


4. 👍 Интеграция с .formatted()
Нельзя вставить переменные прямо в Text Block?
Используем .formatted() — коротко и читабельно:
String user = "Bob";
String template = """
{
"user": "%s",
"access": "granted"
}
""".formatted(user);
````
```java
String s = """
Hello""";
System.out.println(s.length()); // 6, а не 5 (есть \n)


3. 🟢 Нет интерполяции переменных
Нельзя писать так:
   String name = "Eve";
String wrong = """
Hello, $name!
"""; // не сработает


Вместо этого:
   String right = """
Hello, %s!
""".formatted(name);


🗣️ Запомни: Text Blocks делают строки в Java человечными. Это не просто сахар — это инструмент, чтобы твой код говорил с тобой на одном языке. Используй их для всего, что требует форматирования — и забудь про + "\n".
Please open Telegram to view this post
VIEW IN TELEGRAM
👍111🔥1
Java-приложение в Docker

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

Docker — это платформа для создания, развертывания и управления контейнерами. Контейнеры изолируют приложения и все их зависимости, что делает возможным запуск кода в любом окружении без конфликта с другими приложениями.

Шаг 1: Установка Docker

Перед тем как начать, убедитесь, что Docker установлен на вашем компьютере. Вы можете следовать [официальной документации](https://docs.docker.com/get-docker/) для установки на вашу операционную систему.

Шаг 2: Создание Java-приложения

Для примера создадим простое приложение. Предположим, у вас есть простая программа, которая выводит "Hello, World!".

Создайте файл HelloWorld.java:


public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}

Скомпилируйте её с помощью:


javac HelloWorld.java


Шаг 3: Создание Dockerfile

Dockerfile — это текстовый файл, позволяющий автоматически собирать образ Docker. Создайте файл с именем Dockerfile в каталоге вашего проекта:


# Используем официальный образ OpenJDK
FROM openjdk:17-jdk-slim

# Устанавливаем рабочий каталог
WORKDIR /app

# Копируем скомпилированный класс в контейнер
COPY HelloWorld.class ./

# Определяем команду запуска
CMD ["java", "HelloWorld"]


Шаг 4: Сборка Docker-образа

Теперь создайте Docker-образ, используя следующую команду в каталоге вашего проекта:


docker build -t helloworld .

Эта команда соберет образ и назовет его helloworld.

Шаг 5: Запуск контейнера

Чтобы запустить контейнер, выполните команду:


docker run --rm helloworld


Вы должны увидеть вывод:

Hello, World!

Флаг --rm автоматически удаляет контейнер после его остановки.

#java #docker
👍5👏1
⌨️ Захват переменных в лямбда-выражениях

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

1️⃣ Неизменяемыми (effectively final) – Переменная из внешнего контекста, используемая в лямбде, должна быть объявлена как final или фактически быть неизменяемой (то есть не изменяться после первого присваивания). Например:

int x = 10;
Runnable r = () -> System.out.println(x); // x захвачен в лямбде


2️⃣ Свободными от изменения в лямбде – Лямбда не может изменять захваченные переменные. Это ограничение гарантирует, что нет неоднозначного состояния, когда переменная изменяется из нескольких мест (например, из основного потока и из лямбда-функции одновременно).

3️⃣ Статическими или полями класса – В отличие от локальных переменных, статические поля класса или поля экземпляра могут свободно изменяться внутри лямбда-выражений, поскольку их значения хранятся в куче (heap) и доступны по ссылке.

Пример:

public class Main {
private static int staticVar = 20;

public static void main(String[] args) {
int localVar = 10;
Runnable r = () -> System.out.println(localVar + staticVar);
r.run();
}
}


В этом примере localVar захватывается, так как он effectively final, а staticVar доступен, так как это статическое поле.

#java #lambda #capturing
Please open Telegram to view this post
VIEW IN TELEGRAM
👍72
⌨️ Выполнение команды в командной строке с выводом результата


public class CommandLine {
public static void main(String[] args) throws IOException {
Runtime rt = Runtime.getRuntime();
String[] commands = {"ping", "-c 5", "google.com"};
Process proc = rt.exec(commands);

BufferedReader stdInput = new BufferedReader(new
InputStreamReader(proc.getInputStream()));

BufferedReader stdError = new BufferedReader(new
InputStreamReader(proc.getErrorStream()));

// Read the output from the command
System.out.println("Here is the standard output of the command:\n");
String s = null;
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
}

// Read any errors from the attempted command
System.out.println("Here is the standard error of the command (if any):\n");
while ((s = stdError.readLine()) != null) {
System.out.println(s);
}
}
}


Метод Runtime.getRuntime() возвращает объект, представляющий текущую среду выполнения Java. Этот объект позволяет запускать команды системы.

Метод exec() запускает системную команду и возвращает объект Process, который представляет запущенный процесс. Команда, которую нужно выполнить, передается как массив строк.

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

Вывод:

Here is the standard output of the command:

PING google.com (173.194.222.138): 56 data bytes
64 bytes from 173.194.222.138: icmp_seq=0 ttl=60 time=39.479 ms
64 bytes from 173.194.222.138: icmp_seq=1 ttl=60 time=39.753 ms
64 bytes from 173.194.222.138: icmp_seq=2 ttl=60 time=47.982 ms
64 bytes from 173.194.222.138: icmp_seq=3 ttl=60 time=39.569 ms
64 bytes from 173.194.222.138: icmp_seq=4 ttl=60 time=39.850 ms

--- google.com ping statistics ---
5 packets transmitted, 5 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 39.479/41.327/47.982/3.330 ms
Here is the standard error of the command (if any):


#java #Runtime #exec
Please open Telegram to view this post
VIEW IN TELEGRAM
👍71
⌨️ Как добавить элемент в середину массива?

Есть несколько способов добавить элемент в середину массива. Однако, поскольку массивы имеют фиксированную длину, напрямую добавить элемент в существующий массив нельзя — нужно создавать новый массив с увеличенной длиной и копировать данные. Рассмотрим несколько способов:

1️⃣ Копирование вручную

Суть в том что бы создать новый массив на 1 элемент больше оригинального и перенести элементы из старого, не забыв при этом вставить новый элемент.


int[] array = {1, 2, 4, 5};
int index = 2; // индекс, куда вставить новый элемент
int newElement = 3;

// Создаем новый массив на 1 больше
int[] newArray = new int[array.length + 1];

// Копируем элементы до позиции вставки
for (int i = 0; i < index; i++) {
newArray[i] = array[i];
}

// Вставляем новый элемент
newArray[index] = newElement;

// Копируем оставшиеся элементы
for (int i = index + 1; i < newArray.length; i++) {
newArray[i] = array[i - 1];
}

// Печатаем новый массив
System.out.println(Arrays.toString(newArray)); // [1, 2, 3, 4, 5]


2️⃣ Использование коллекций (например, ArrayList)

ArrayList динамический по размеру, и можно легко вставить элемент в любое место с помощью метода add(index, element).


Integer[] array = {1, 2, 4, 5};
int index = 2; // индекс, куда вставить новый элемент
int newElement = 3;

List<Integer> list = new ArrayList<>(Arrays.asList(array));

// Вставляем элемент
list.add(index, newElement);

// Создаем новый массив на 1 больше
Integer[] newArray = list.toArray(array);

// Печатаем новый массив
System.out.println(Arrays.toString(newArray)); // [1, 2, 3, 4, 5]


3️⃣ Использование метода System.arraycopy

Метод System.arraycopy позволяет эффективно копировать части массива.


int[] array = {1, 2, 4, 5};
int index = 2; // индекс, куда вставить новый элемент
int newElement = 3;

// Создаем новый массив на 1 больше
int[] newArray = new int[array.length + 1];

// Копируем элементы до позиции вставки
System.arraycopy(array, 0, newArray, 0, index);

// Вставляем новый элемент
newArray[index] = newElement;

// Копируем оставшиеся элементы
System.arraycopy(array, index, newArray, index + 1, array.length - index);

// Печатаем новый массив
System.out.println(Arrays.toString(newArray)); // [1, 2, 3, 4, 5]


#java #array
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
🔄 Бесконечные потоки

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

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

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

📌 При работе с бесконечными потоками, крайне важно вызвать метод limit() перед вызовом терминальной операции, иначе наша программа будет работать бесконечно.
👍21
⌨️ POJO (Plain Old Java Object) — это простой Java-объект, не зависящий от каких-либо специфичных библиотек, фреймворков или технологий. Такой объект обычно содержит только поля (атрибуты) и методы доступа (геттеры и сеттеры), без дополнительной логики, аннотаций или наследования от специфических классов.

Пример POJO:

public class Person {
private String name;
private int age;

// Конструктор
public Person(String name, int age) {
this.name = name;
this.age = age;
}

// Геттеры и сеттеры
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}


POJO используется для создания простых объектов без привязки к какой-либо специфической архитектуре или фреймворку. Например, в JPA объекты-сущности часто являются POJO, что позволяет их использовать независимо от платформы.

#java #pojo
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9
⌨️ Stream.reduce() vs Collectors.joining()

Когда требуется конкатенировать строки с использованием Stream, можно выбрать один из двух методов: Stream.reduce() или Stream.collect(Collectors.joining()).

Пример с Stream.reduce():
List<String> list = List.of("Str1", "Str2", "Str3");
String result = list.stream().reduce("", (a, b) -> a + b);
System.out.println(result); // Str1Str2Str3


Пример с Collectors.joining():
List<String> list = List.of("Str1", "Str2", "Str3");
String result = list.stream().collect(Collectors.joining());
System.out.println(result); // Str1Str2Str3


Использование reduce() для конкатенации строк не является оптимальным с точки зрения производительности. При каждом вызове операции +, создается новая строка, так как строки в Java неизменяемы. Это приводит к увеличению нагрузки на память из-за создания множества промежуточных объектов.

В свою очередь, метод Collectors.joining() использует StringBuilder для сборки строк, что значительно эффективнее. Он избегает создания лишних объектов и снижает потребление памяти.

#java #Stream #reduce #joining
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
⌨️ ForkJoinPool — это специальная реализация пула потоков, предназначенная для выполнения параллельных задач с использованием алгоритма разделяй и властвуй (divide and conquer). Этот пул потоков был представлен в Java 7 как часть библиотеки java.util.concurrent.

Использует подход "разделяй и властвуй", где задача разбивается на подзадачи до тех пор, пока они не станут достаточно маленькими для последовательного решения. Для этого используются классы RecursiveTask<V> (возвращает результат) и RecursiveAction (без результата).

ForkJoinPool динамически управляет количеством потоков, при необходимости создавая новые. Обычно это количество соответствует числу процессоров, доступных в системе.

Использует технику work-stealing, где потоки, завершившие свои задачи, могут "красть" задачи у других потоков, чтобы эффективно использовать ресурсы процессора.

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

Основные методы:

invoke(): синхронно запускает задачу и ждет её завершения.

submit(): запускает задачу асинхронно.

execute(): также запускает задачу асинхронно, но не возвращает результат.

Пример использования:

import java.util.concurrent.RecursiveTask;
import java.util.concurrent.ForkJoinPool;

class SumTask extends RecursiveTask<Integer> {
private final int[] array;
private final int start, end;
private final int threshold = 10;

public SumTask(int[] array, int start, int end) {
this.array = array;
this.start = start;
this.end = end;
}

@Override
protected Integer compute() {
if (end - start <= threshold) {
// Базовый случай: небольшая задача
int sum = 0;
for (int i = start; i < end; i++) {
sum += array[i];
}
return sum;
} else {
// Разделяем задачу
int mid = (start + end) / 2;
SumTask leftTask = new SumTask(array, start, mid);
SumTask rightTask = new SumTask(array, mid, end);

leftTask.fork(); // Асинхронно запускаем левую подзадачу
int rightResult = rightTask.compute(); // Синхронно вычисляем правую подзадачу
int leftResult = leftTask.join(); // Ждем завершения левой подзадачи

return leftResult + rightResult;
}
}
}

public class ForkJoinExample {
public static void main(String[] args) {
ForkJoinPool pool = new ForkJoinPool();
int[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
SumTask task = new SumTask(array, 0, array.length);
int result = pool.invoke(task);
System.out.println("Сумма: " + result);
}
}

Этот код создает задачу для суммирования массива, разбивая его на подзадачи.

#java #ForkJoinPool
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
⌨️ Параллельные и последовательные потоки (Stream)

Последовательные потоки (Sequential Streams)

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

✔️ Используют основной поток программы (main thread) для выполнения операций.

✔️ Метод stream() создаёт последовательный поток.

Пример последовательного потока:

List<String> list = List.of("Hello ", "w", "o", "r", "l", "d!");
list.stream().forEach(System.out::print);
// Hello world!


Параллельные потоки (Parallel Streams)

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

✔️ Используют ForkJoinPool для распределения задач между потоками.

✔️ Эффективны для больших наборов данных, поскольку могут улучшить производительность на многоядерных процессорах.

✔️ Параллельный поток можно создать, вызвав метод parallelStream() или применив метод parallel() к уже существующему потоку.

Пример параллельного потока:

List<String> list = List.of("Hello ", "w", "o", "r", "l", "d!");
list.parallelStream().forEach(System.out::print);
// rlHello d!wo


#java #Stream #parallelStream
Please open Telegram to view this post
VIEW IN TELEGRAM
👍31
🔍 ТОП-5 ошибок на алгоритмической сессии

Привет, на связи Таня Коровкина из ШОРТКАТ. Ментор по алгоритмам и backend-разработчик

Каждый месяц тысячи разработчиков совершают одни и те же ошибки на алгоритмических интервью 🚩

И продолжают готовиться... не к тому.

6 июля(понедельник) в 19:00 (МСК) проведу вебинар и покажу, что на самом деле оценивает интервьюер и какие ошибки чаще всего приводят к отказу

• дам практические советы, которые можно использовать уже на следующем собеседовании
• расскажу про специфику российского BigTech

🤘 Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для разработчиков, которые хотят повысить свой грейд, ЗП и прокачать скиллы.

Переходи в нашего бота, чтобы получить ссылку на эфир →
@shortcut_sh_bot

Реклама.
О рекламодателе.
Please open Telegram to view this post
VIEW IN TELEGRAM
2
⌨️ Полезные стримы. Группировка

Дан список людей с именем и городом проживания. Нужно сгруппировать их по городам.

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

record Person(String name, String city) {}

public class StreamExample {
public static void main(String[] args) {
List<Person> people = List.of(
new Person("Alice", "New York"),
new Person("Bob", "Los Angeles"),
new Person("Charlie", "New York"),
new Person("David", "Los Angeles"),
new Person("Edward", "San Francisco")
);

Map<String, List<Person>> peopleByCity = people.stream()
.collect(Collectors.groupingBy(Person::city));

peopleByCity.forEach((city, peopleInCity) -> {
System.out.println(city + ": " + peopleInCity.stream()
.map(Person::name)
.collect(Collectors.joining(", ")));
});
// Вывод:
// San Francisco: Edward
// New York: Alice, Charlie
// Los Angeles: Bob, David
}
}


#java #stream #grouping
Please open Telegram to view this post
VIEW IN TELEGRAM
👍51