Java | Фишки и трюки


Channel's geo and language: Russia, Russian
Category: Technologies


Java: примеры кода, интересные фишки и полезные трюки
Купить рекламу: https://telega.in/c/java_tips_and_tricks
✍️По всем вопросам: @Pascal4eg

Related channels  |  Similar channels

Channel's geo and language
Russia, Russian
Statistics
Posts filter


⌨️ Несколько способов для избежания NullPointerException:

👉 Используйте Optional из пакета java.util.

👉 При сравнении константы с переменной пришедшей из вне, нужно вызывать equals() на константе.

👉 А при сравнении двух переменных используйте java.util.Objects#equals.

👉 Используйте библиотеки с null-безопасностью, например, Apache Commons StringUtils.

👉 Вызывайте методы equals() и equalsIgnoreCase() на известных объектах.

👉 Применяйте valueOf() вместо toString().

👉 Воспользуйтесь аннотациями @NotNull и @Nullable на основе IDE.

👉 При возврате из метода коллекций, не стоит возвращать null, лучше пустую коллекцию (java.util.Collections#emptyList).


⚡️⚡️Строки кода стремятся подешеветь до цены токена. Что остается ценным?

Про это — трек «Замысел» 15 октября. Во второй день Joker, посвященный разработке будущего. О том, как человек понимает, почему он называет что-то проблемой, и формулирует свою цель, а не ту, что навязана ему извне. Теперь это общая работа для всех ролей, от разработчика до CEO: шаблонными решениями и готовыми схемами думает модель.

В карточках к посту рассказываем, почему так получилось, и показываем доклады из трека: спикеры расскажут, о чем нужно подумать еще до постановки задачи, и чем будет заниматься новый тип инженера — Context Engineer.

К дискуссиям после докладов — собираем ваши кейсы. Напишите в комментариях поста, если у вас бывало такое:
— агент час работал не над той задачей, потому что задача была сформулирована плохо;
— ускорение одного этапа не сдвинуло общий срок;
— узким местом оказалась не модель или инфраструктура, а знание, которое никто не записал.

Joker 2026 — 14–15 октября, Санкт-Петербург и онлайн.

🌟По промокоду javatipsandtricks персональные билеты дешевле

🔜🔜Исследуйте разработку будущего — на сайте Joker.

Реклама. ООО «Джуг Ру Груп». ИНН 7801341446


Метод retainAll()

Метод retainAll() интерфейса Collection, удаляет из коллекции все элементы, не содержащиеся в переданной коллекции. По сути оставляет в коллекции только пересекающиеся элементы из двух коллекций.


boolean retainAll(Collection c);


Параметры:
c – коллекция, содержащая элементы, которые следует сохранить в коллекции.

Возврат:
true, если коллекция изменилась в результате вызова


Метод putIfAbsent() интерфейса Map. Если указанный ключ еще не связан со значением (или сопоставлен со значением null), он связывает его с заданным значением и возвращает значение null, в противном случае возвращается текущее значение.


default V putIfAbsent(K key, V value) {
V v = get(key);
if (v == null) {
v = put(key, value);
}

return v;
}

Параметры:
key – ключ, с которым должно быть связано указанное значение. value – значение, которое должно быть связано с указанным ключом.

Возврат:
предыдущее значение, связанное с указанным ключом, или значение null, если для ключа не было сопоставления. Возврат null может также указывать на то, что мапа ранее ассоциировала с ключом значение null, если реализация поддерживает значения null.


⌨️ Можно ли объявить метод абстрактным и статическим одновременно?

Нет. В таком случае компилятор выдаст ошибку: "Illegal combination of modifiers: ‘abstract’ and ‘static’".

Модификатор abstract говорит, что метод будет реализован в другом классе, а static наоборот указывает, что этот метод будет доступен по имени класса.


⌨️ Где и для чего используется модификатор abstract?

Класс, помеченный модификатором abstract, называется абстрактным классом. Такие классы могут выступать только предками для других классов. Создавать экземпляры самого абстрактного класса не разрешается. При этом наследниками абстрактного класса могут быть как другие абстрактные классы, так и классы, допускающие создание объектов.

Метод, помеченный ключевым словом abstract - абстрактный метод, т.е. метод, который не имеет реализации. Если в классе присутствует хотя бы один абстрактный метод, то весь класс должен быть объявлен абстрактным.

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


⌨️ void и Void

В языке программирования Java ключевое слово void используется для указания, что метод не возвращает никакого значения. Метод с возвращаемым типом void выполняет определенные действия, но не производит результат, который можно было бы использовать в вызывающей части программы.

Для void еще есть класс обёртка Void. Класс Void — это класс-заполнитель который нельзя создать и который хранит ссылку на объект Class, представляющий ключевое слово Java void.

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


interface I {
T doStuff();
}

class A implements I {
@Override
public Void doStuff() {
// ничего другого вернуть нельзя
return null;
}
}


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


⌨️ Что такое generics?

Generics - это технический термин, обозначающий набор свойств языка позволяющих определять и использовать обобщенные типы и методы. Обобщенные типы или методы отличаются от обычных тем, что имеют типизированные параметры.

Примером использования обобщенных типов может служить Java Collection Framework. Так, класс LinkedList - типичный обобщенный тип. Он содержит параметр E, который представляет тип элементов, которые будут храниться в коллекции. Создание объектов обобщенных типов происходит посредством замены параметризированных типов реальными типами данных. Вместо того, чтобы просто использовать LinkedList, ничего не говоря о типе элемента в списке, предлагается использовать точное указание типа LinkedList, LinkedList и т.п.


⌨️ Почему char[] предпочтительнее String для хранения пароля?

С момента создания строка остаётся в пуле, до тех пор, пока не будет удалена сборщиком мусора. Поэтому, даже после окончания использования пароля, он некоторое время продолжает оставаться доступным в памяти и способа избежать этого не существует. Это представляет определённый риск для безопасности, поскольку кто-либо, имеющий доступ к памяти сможет найти пароль в виде текста. В случае использования массива символов для хранения пароля имеется возможность очистить его сразу по окончанию работы с паролем, позволяя избежать риска безопасности, свойственного строке.


Сезон поиска работы только стартанул, а тут такие шишки: СТО, IT-директора и техлиды сами пришли и разместили вакансии.

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

→ CTO Альфа-Банка Кирилл Сафаров ищет руководителя портфеля цифровых продуктов на MLM-проект «Свой в Альфе»;

→ IT-директор Сергей Чернуха из «Филип Моррис Интернэшнл» в России в поиске сразу двух управляющих: IT-решениями для бизнеса и IT-инфраструктурой;

→ Руководитель направления инженерии и анализа данных в Контуре Юлия Лунёва приглашает продуктового аналитика-сеньора в направление веб-сервисов;

→ Техлид команды тестирования в Альфа-Банкее Егор Шохин ищет автотестировщика на C# уровня мидл+;

→ CTO МТС Банка Кирилл Золотарев ищет технического лидера в команду платежей;

→ Коммерческий директор Skillaz Андрей Синякин опубликовал вакансию директора
по продукту


Метод Arrays.fill() для инициализации массива

Класс java.util.Arrays имеет несколько методов с именем fill(), которые принимают разные типы аргументов и заполняют весь массив одним и тем же значением:
long array[] = new long[5];
Arrays.fill(array, 30);
У метода также есть несколько альтернатив, которые устанавливают диапазон массива в определенное значение:
int array[] = new int[5];
Arrays.fill(array, 0, 3, -50);
Обратите внимание, что метод принимает массив, индекс первого элемента, индекс последнего элемента (не включительно) и значение.


⌨️ Чем абстрактный класс отличается от интерфейса? В каких случаях следует использовать абстрактный класс, а в каких интерфейс?

В Java абстрактный класс и интерфейс представляют два различных механизма для достижения абстракции и обеспечения множественного наследования. Вот основные различия между ними:

1️⃣ Абстрактный класс (Abstract
Class):

👉 Может содержать как абстрактные методы (методы без тела), так и обычные методы с реализацией.
👉 Может иметь переменные экземпляра (поля).
👉 Может содержать конструкторы.
👉 Поддерживает одиночное наследование: подкласс можно наследовать только от одного абстрактного класса.


abstract class Animal {
String name;

abstract void makeSound();

void eat() {
System.out.println(name + " is eating.");
}
}

2️⃣ Интерфейс (Interface):

👉 Содержит только абстрактные методы (в Java 8 и выше может содержать методы с реализацией, помеченные ключевым словом default).
👉 Не может иметь переменных экземпляра, кроме переменных, объявленных как static final.
👉 Не содержит конструкторов.
👉 Поддерживает множественное наследование: класс может реализовывать несколько интерфейсов.


interface Animal {
void makeSound();
void eat();
}

Когда использовать:

👉 Используйте абстрактные классы, когда у вас есть общая реализация для нескольких классов и вы хотите предоставить базовый функционал.

👉 Используйте интерфейсы, когда вам нужна поддержка множественного наследования, или когда классы, возможно, уже наследуют от других классов.

Обычно считается хорошей практикой использовать интерфейсы для определения контракта и абстрактные классы для предоставления базовой реализации. В Java 8 и более новых версиях с появлением интерфейсов с методами по умолчанию (default methods), различия стали менее явными, и выбор может зависеть от конкретной ситуации.


Методы и конструкторы в enum

Перечисления (enum) — это классы Java, определяющие наборы констант. Самый простой enum выглядит так:


public enum Vehicle { CAR, BUS, BICYCLE, SCOOTER }


Но на самом деле, enum в Java обладают гораздо более широким функционалом. Они поддерживают поля, методы, интерфейсы и т.д. Они также Comparable и Serializable и могут имплементировать все методы объекта.
Для наглядности давайте создадим enum Animal и присвоим каждому элементу цвет:

public enum Animal {
DOG("black"),
CAT("white"),
RAT("gray");

private final String color;
Animal(String color){
this.color = color;
}
public String getColor(){
return color;
}
}

Теперь мы можем выполнить перебор элементов массива с помощью статического метода values():

public class Main {
public static void main(String[] args) {
for (Animal a : Animal.values()) {
System.out.println(a.name() + " - " + a.getColor());
}
}
}

Мы получим следующий результат:

DOG - black
CAT - white
RAT - gray


Каждой константе можно присвоить разное поведение при выполнении определенного метода. Например, можно сделать основной метод абстрактным и переопределить его в каждой константе:

public enum Operation {
PLUS { double evaluate(double x, double y) { return x + y; } },
MINUS { double evaluate(double x, double y) { return x - y; } },
abstract double evaluate(double x, double y);
}

Всё это обеспечивает удобную работу с перечислениями в Java.


JSON – невероятно удобный и полезный формат для хранения и обмена данными. Java полностью поддерживает его.

Сериализовать данные можно так:

JSONObject author = new JSONObject();
author.put("name", "J. K. Rowling");
author.put("numberOfBooks", 22);

JSONArray books = new JSONArray();
books.put("Harry Potter and the Philosopher's Stone");
books.put("Harry Potter and the Chamber of Secrets");
books.put("Harry Potter and the Prisoner of Azkaban");

author.put("books", books);
System.out.print(author);

Получится вот такая JSON-строка:

{"books":["Harry Potter and the Philosopher's Stone","Harry Potter and the Chamber of Secrets","Harry Potter and the Prisoner of Azkaban"],"name":"J. K. Rowling","numberOfBooks":22}

Десериализация на Java выглядит примерно так:

JSONObject author = new JSONObject(serializedString);
System.out.println("name: " + author.get("name"));
System.out.println("numberOfBooks: " + author.get("numberOfBooks"));

JSONArray books = (JSONArray) author.get("books");
Iterator i = books.iterator();
System.out.println("books:");
while (i.hasNext()) {
System.out.println(i.next());
}

Нужно добавить зависимость:


org.json
json
20231013


⌨️ Фишка: Создание итератора для пользовательского класса.

Код:
import java.util.Iterator;

public class CustomClass implements Iterable {
private String[] elements;

public CustomClass(String[] elements) {
this.elements = elements;
}

@Override
public Iterator iterator() {
return new CustomIterator();
}

private class CustomIterator implements Iterator {
private int index = 0;

@Override
public boolean hasNext() {
return index < elements.length;
}

@Override
public String next() {
if (this.hasNext()) {
return elements[index++];
} else {
throw new NoSuchElementException();
}
}
}

public static void main(String[] args) {
String[] arr = {"one", "two", "three"};
CustomClass customClass = new CustomClass(arr);

for (String element : customClass) {
System.out.println(element);
}
}
}


💁‍♂️ Этот код позволяет создать итератор для пользовательского класса CustomClass. Итератор позволяет перебирать элементы массива elements и использовать их в цикле for-each.


⌨️ Параллельное выполнение задач с использованием ExecutorService

ExecutorService - это интерфейс, который предоставляет удобный способ для управления параллельным выполнением задач. Он предоставляет пул потоков для выполнения задач и управляет их жизненным циклом:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {
public static void main(String[] args) {
// Создание пула потоков
ExecutorService executor = Executors.newFixedThreadPool(5);

// Подача задач на выполнение
for (int i = 0; i < 10; i++) {
executor.execute(new Task(i));
}

// Остановка пула потоков после выполнения задач
executor.shutdown();
}

static class Task implements Runnable {
private int taskId;

public Task(int taskId) {
this.taskId = taskId;
}

@Override
public void run() {
System.out.println("Выполнение задачи " + taskId + " в потоке " +
Thread.currentThread().getName());
}
}
}


🧑‍💻Этот код создает пул из 5 потоков и подает на выполнение 10 задач. Каждая задача выводит свой идентификатор и имя потока, в котором она выполняется. После выполнения всех задач пул потоков останавливается.


⌨️ Метод для глубокого копирования объектов. Это часто используется, когда нужно создать копию объекта, сохраняя его данные, но не связи с другими объектами.

📌 Вот пример метода для глубокого копирования:

import java.io.*;

public class DeepCopyExample {
public static void main(String[] args) {
Person person = new Person("John", 30);
Person deepCopy = deepCopy(person);

System.out.println("Original: " + person.getName() + ", " + person.getAge());
System.out.println("Deep copy: " + deepCopy.getName() + ", " + deepCopy.getAge());
}

public static T deepCopy(T object) {
try {
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteOutputStream);
objectOutputStream.writeObject(object);

ByteArrayInputStream byteInputStream = new ByteArrayInputStream(byteOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteInputStream);
T copy = (T) objectInputStream.readObject();

return copy;
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
}

class Person implements Serializable {
private String name;
private int age;

public Person(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public int getAge() {
return age;
}
}

✅ В этом примере мы создаем класс Person, который реализует интерфейс Serializable, чтобы объекты этого класса можно было сериализовать и десериализовать. Затем мы создаем метод deepCopy, который принимает объект и возвращает его глубокую копию.

Мы используем ByteArrayOutputStream и ObjectOutputStream, чтобы записать объект в байтовый массив, а затем ByteArrayInputStream и ObjectInputStream, чтобы прочитать копию объекта из массива.

В методе main мы создаем объект Person, затем создаем его глубокую копию с помощью метода deepCopy и выводим на экран значения оригинального и скопированного объекта.


⌨️ Использования анонимных классов:

Анонимный класс - это класс без имени, который объявляется и создается внутри другого класса или метода. Он удобен, когда требуется определить класс, который будет использоваться только один раз, и нет необходимости создавать отдельный класс.

public class Main {
interface Greeting {
void sayHello();
}

public static void main(String[] args) {
Greeting greeting = new Greeting() { // объявление и создание анонимного класса
@Override
public void sayHello() {
System.out.println("Привет, мир!");
}
};

greeting.sayHello(); // вызов метода sayHello у анонимного класса
}
}

✅В данном примере мы определили интерфейс Greeting, содержащий метод sayHello(). Затем, в методе main(), мы создали анонимный класс, реализующий интерфейс Greeting и переопределивший метод sayHello(). Затем мы вызываем метод sayHello() у объекта анонимного класса.

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


⌨️ Использование класса java.time.Duration для рассчета разницы во времени между двумя моментами.

Код для расчета разницы во времени:




import java.time.Duration;
import java.time.LocalDateTime;

public class TimeDifferenceCalculator {
public static void main(String[] args) {
LocalDateTime startTime = LocalDateTime.of(2022, 1, 1, 10, 0, 0);
LocalDateTime endTime = LocalDateTime.of(2022, 1, 1, 12, 30, 0);

Duration duration = Duration.between(startTime, endTime);

long hours = duration.toHours();
long minutes = duration.toMinutes() % 60;
long seconds = duration.getSeconds() % 60;

System.out.println("Time difference: " + hours + " hours, " + minutes + " minutes, " + seconds + " seconds.");
}
}

В этом примере мы создаем два объекта LocalDateTime - startTime и endTime, представляющие начальное и конечное время соответственно. Затем мы используем метод Duration.between() для расчета разницы во времени между startTime и endTime.

Далее, мы используем методы toHours(), toMinutes() и getSeconds() для получения часов, минут и секунд в полученной разнице времени. Наконец, мы выводим результаты на экран.

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


⌨️ Использование аннотации @FunctionalInterface для определения функциональных интерфейсов.

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

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

Ниже приведен пример кода, демонстрирующий использование аннотации @FunctionalInterface:

@FunctionalInterface
interface Calculator {
int calculate(int a, int b);
}

public class FunctionalInterfaceExample {
public static void main(String[] args) {
Calculator addition = (a, b) -> a + b;
System.out.println("Результат сложения: " + addition.calculate(5, 3));

Calculator subtraction = (a, b) -> a - b;
System.out.println("Результат вычитания: " + subtraction.calculate(5, 3));
}
}

В этом примере мы создаем функциональный интерфейс Calculator, содержащий метод calculate. Затем мы создаем экземпляры интерфейса, используя лямбда-выражения, и вызываем метод calculate для выполнения операций сложения и вычитания.

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

20 last posts shown.