Java | Вопросы собесов
11.5K subscribers
33 photos
2 videos
1.27K links
Download Telegram
🤔 В чём смысл ограничений?

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


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍9💊5
🤔 Каким образом можно заставить поток выполнится приоритет и есть ли такая возможность в Java?

В Java у каждого потока есть приоритет, но он не гарантирует порядок выполнения.

🟠Использование `setPriority(int priority)`
Java позволяет задавать приоритет потока с помощью метода setPriority(), который принимает значение от 1 до 10:
Thread.MIN_PRIORITY (1) – минимальный приоритет
Thread.NORM_PRIORITY (5) – стандартный приоритет (по умолчанию)
Thread.MAX_PRIORITY (10) – максимальный приоритет
class MyThread extends Thread {
public MyThread(String name, int priority) {
super(name);
setPriority(priority);
}

@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(getName() + " выполняется с приоритетом " + getPriority());
}
}
}

public class Main {
public static void main(String[] args) {
MyThread low = new MyThread("Low Priority", Thread.MIN_PRIORITY);
MyThread high = new MyThread("High Priority", Thread.MAX_PRIORITY);

low.start();
high.start();
}
}


🟠Почему `setPriority()` не даёт 100% контроль?
Приоритет – это всего лишь рекомендация для ОС.
Распределение процессорного времени зависит от планировщика ОС.
В Windows, Linux, macOS приоритеты работают по-разному, и Java не контролирует их на низком уровне.

🟠Как действительно заставить поток выполниться первым?
Реальные способы управления порядком выполнения:
а) Использование join()
Позволяет дождаться выполнения одного потока перед запуском другого.
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread high = new MyThread("High Priority", Thread.MAX_PRIORITY);
Thread low = new MyThread("Low Priority", Thread.MIN_PRIORITY);

high.start();
high.join(); // Ждём, пока high завершится

low.start(); // Теперь запускается low
}
}


б) Использование Executors.newSingleThreadExecutor()
Позволяет гарантированно выполнять потоки по очереди.
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {
public static void main(String[] args) {
ExecutorService executor = Executors.newSingleThreadExecutor();

executor.submit(() -> System.out.println("Задача 1"));
executor.submit(() -> System.out.println("Задача 2"));

executor.shutdown();
}
}


Ставь 👍 и забирай 📚 Базу знаний
👍5
🤔 В чем разница RestController и Controller?

-
@Controller — обрабатывает запросы и возвращает страницу (например, HTML через шаблонизатор).
-
@RestController — это @Controller + @ResponseBody, автоматически возвращает JSON/XML ответ прямо в тело HTTP-ответа.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍12🔥5💊2
🤔 Что такое heap, stack?

В контексте Java, Heap (куча) и Stack (стек) являются областями памяти, используемыми JVM для управления памятью, необходимой для выполнения программы. Каждая из этих областей имеет свои характеристики и используется для разных целей.

🚩Heap (Куча)

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

🟠Особенности
Куча разделена на поколения: молодое поколение (Young Generation) и старое поколение (Old Generation).
Молодое поколение включает в себя области Eden Space и Survivor Spaces (S0 и S1).
Старое поколение хранит долгоживущие объекты.

🟠Управление памятью
Куча управляется сборщиком мусора (Garbage Collector), который автоматически освобождает память, занятую объектами, которые больше не используются.

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

public class Example {
public static void main(String[] args) {
Example obj = new Example(); // obj создается в куче
}
}


🚩Stack (Стек)

Стек — это область памяти, используемая для управления вызовами методов и хранения локальных переменных, параметров методов и информации о возвратах.

🟠Особенности
Каждый поток имеет свой собственный стек.
Стек хранит кадры (frames) для каждого вызова метода. Каждый кадр содержит локальные переменные метода и информацию о вызовах.

🟠Управление памятью
Память в стеке автоматически управляется при вызове методов и выходе из них. Когда метод вызывается, создается новый кадр в стеке; когда метод завершает выполнение, его кадр удаляется из стека.

🟠Использование
Стек используется для хранения примитивных типов данных и ссылок на объекты, которые находятся в куче.
Локальные переменные методов и параметры методов хранятся в стеке.

public class Example {
public static void main(String[] args) {
int localVar = 10; // localVar хранится в стеке
Example obj = new Example(); // Ссылка на obj хранится в стеке, а сам объект — в куче
obj.method();
}

public void method() {
int anotherVar = 20; // anotherVar хранится в стеке
}
}


Ставь 👍 и забирай 📚 Базу знаний
👍10
🤔 Примеры итераторов, реализующих поведение fail-safe?

Fail-safe итераторы не выбрасывают ConcurrentModificationException и работают с копией коллекции. Примеры:
- CopyOnWriteArrayList
- ConcurrentHashMap.keySet().iterator()
- ConcurrentLinkedQueue


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🔥8👍4
🤔 Какие шаги нужно выполнить для прочтения текстового файла?

Для прочтения текстового файла в Java нужно выполнить несколько шагов.

1⃣Выбор метода чтения файла
В Java есть несколько способов читать текстовые файлы:
1. Использование класса FileReader.
2. Использование BufferedReader для построчного чтения.
3. Использование java.nio.file.Files для чтения всех строк файла.
4. Применение Scanner для гибкого чтения данных.

2⃣Открытие файла
Нужно указать путь к файлу (абсолютный или относительный) и передать его в выбранный класс. Например:
FileReader reader = new FileReader("file.txt");


3⃣Чтение данных
Выберите подходящий метод для обработки содержимого файла:
- Построчное чтение.
- Чтение символов.
- Чтение всех строк сразу (например, через Files.readAllLines).

4⃣Закрытие ресурса
Для освобождения системных ресурсов после работы с файлом нужно закрыть поток. Это делается вручную или с помощью конструкции try-with-resources.

🚩Примеры реализации

Построчное чтение с BufferedReader
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class FileReadExample {
public static void main(String[] args) {
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line); // Вывод строки
}
} catch (IOException e) {
System.out.println("Ошибка: " + e.getMessage());
}
}
}


Чтение файла с помощью Files
import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.IOException;
import java.util.List;

public class FileReadExample {
public static void main(String[] args) {
try {
List<String> lines = Files.readAllLines(Paths.get("file.txt"));
for (String line : lines) {
System.out.println(line);
}
} catch (IOException e) {
System.out.println("Ошибка: " + e.getMessage());
}
}
}


Чтение через Scanner
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class FileReadExample {
public static void main(String[] args) {
try (Scanner scanner = new Scanner(new File("file.txt"))) {
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine()); // Построчное чтение
}
} catch (FileNotFoundException e) {
System.out.println("Файл не найден: " + e.getMessage());
}
}
}


Ставь 👍 и забирай 📚 Базу знаний
👍9
🤔 Чем отличается Comparator от Comparable?

- Comparable — реализуется самим объектом:
- Один способ сортировки (compareTo()).
- Используется по умолчанию в TreeSet, Collections.sort().
- Comparator — внешний компаратор:
- Можно создать несколько стратегий сортировки.
- Используется, когда нельзя менять класс объекта.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍14
🤔 Какие методы располагаются в интерфейсе?

В интерфейсе могут располагаться различные типы методов, начиная с Java 8, когда в язык были добавлены новые возможности, такие как default методы и static методы. До Java 8 интерфейсы могли содержать только абстрактные методы. Ниже представлены типы методов, которые могут быть объявлены в интерфейсе:

🟠Абстрактные методы
Это методы без тела, предназначенные для переопределения в классах, которые реализуют интерфейс. Абстрактные методы представляют собой контракт, который должен быть выполнен классом-реализатором. Все методы в интерфейсе неявно являются public abstract, даже если явно не указаны эти модификаторы.
void myMethod();


🟠Default методы (начиная с Java 8)
Позволяют определять реализацию метода непосредственно в интерфейсе. Классы, реализующие интерфейс, могут переопределять эти методы, но это не обязательно. Default методы были введены для обеспечения обратной совместимости, позволяя добавлять новые методы в интерфейсы без нарушения существующих реализаций.
default void defaultMethod() {
// Реализация
}


🟠Static методы (начиная с Java 8)
Позволяют определять методы с реализацией, которые могут быть вызваны без создания экземпляра класса, реализующего интерфейс. Эти методы нельзя переопределить в реализующем интерфейс классе.
static void staticMethod() {
// Реализация
}


🟠Private методы (начиная с Java 9)
Позволяют определять вспомогательные методы, которые предназначены для использования в default или static методах внутри того же интерфейса. Эти методы не могут быть вызваны извне интерфейса или реализующих его классов.
private void privateMethod() {
// Реализация
}


Пример
public interface MyInterface {
// Абстрактный метод
void abstractMethod();

// Default метод
default void defaultMethod() {
System.out.println("Default implementation");
}

// Static метод
static void staticMethod() {
System.out.println("Static implementation");
}

// Private метод (используется внутри интерфейса)
private void privateMethod() {
System.out.println("Private helper method");
}
}


Ставь 👍 и забирай 📚 Базу знаний
👍5🔥1
🤔 Какое базовое отличие между интерфейсами Collection и List?

- Collection — базовый интерфейс, представляющий неупорядоченное множество элементов. Он не знает про индексы, дубликаты или порядок.
- List — расширяет Collection, добавляя:
- упорядоченность элементов;
- доступ по индексу;
- поддержку дубликатов.
То есть List — это частный случай Collection, который работает как последовательность с доступом по позиции.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍10
🤔 Для чего нужны паттерны программирования?

Паттерны проектирования (design patterns) — это готовые решения распространённых задач, возникающих при разработке программного обеспечения.

🚩Зачем они нужны?

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

🚩Виды паттернов программирования

🟠Порождающие (Creational)
управляют созданием объектов.
🟠Структурные (Structural)
Определяют, как классы и объекты взаимодействуют.
🟠Поведенческие (Behavioral)
управляют взаимодействием объектов и потоками выполнения.

public class Singleton {
private static Singleton instance;

private Singleton() {} // Закрытый конструктор

public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}


Структурные паттерны (определяют отношения между классами и объектами)
// Старый интерфейс
class OldPrinter {
void printText(String text) {
System.out.println("Печать: " + text);
}
}

// Новый интерфейс
interface ModernPrinter {
void print(String text);
}

// Адаптер
class PrinterAdapter implements ModernPrinter {
private final OldPrinter oldPrinter = new OldPrinter();

@Override
public void print(String text) {
oldPrinter.printText(text);
}
}

// Использование
public class AdapterExample {
public static void main(String[] args) {
ModernPrinter printer = new PrinterAdapter();
printer.print("Hello, world!");
}
}


import java.util.ArrayList;
import java.util.List;

// Интерфейс подписчика
interface Observer {
void update(String message);
}

// Интерфейс издателя
class NewsChannel {
private final List<Observer> observers = new ArrayList<>();

void subscribe(Observer observer) {
observers.add(observer);
}

void notifyObservers(String news) {
for (Observer observer : observers) {
observer.update(news);
}
}
}

// Подписчик
class Subscriber implements Observer {
private final String name;

Subscriber(String name) {
this.name = name;
}

@Override
public void update(String message) {
System.out.println(name + " получил новость: " + message);
}
}

// Использование
public class ObserverExample {
public static void main(String[] args) {
NewsChannel channel = new NewsChannel();
Observer user1 = new Subscriber("Алиса");
Observer user2 = new Subscriber("Боб");

channel.subscribe(user1);
channel.subscribe(user2);

channel.notifyObservers("Новый выпуск Java 21!");
}
}


Ставь 👍 и забирай 📚 Базу знаний
👍11💊1
🤔 Какая величина у байта?

Байт равен 8 битам и является базовой единицей измерения данных в памяти.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
💊19👍18🔥1
🤔 Какие типы данных есть в контексте JVM?

В контексте JVM (Java Virtual Machine) типы данных делятся на два основных класса: примитивные типы данных и ссылочные типы данных.

🚩Примитивные типы данных

Примитивные типы данных представляют собой базовые типы, которые не являются объектами и хранят непосредственно значения. Они делятся на числовые типы, логический тип и символьный тип.

🟠Числовые типы
Целочисленные типы
byte: 8-битный знаковый целочисленный тип данных (диапазон от -128 до 127).
short: 16-битный знаковый целочисленный тип данных (диапазон от -32,768 до 32,767).
int: 32-битный знаковый целочисленный тип данных (диапазон от -2^31 до 2^31-1).
long: 64-битный знаковый целочисленный тип данных (диапазон от -2^63 до 2^63-1).
Типы с плавающей точкой
float: 32-битный IEEE 754 тип данных с плавающей точкой одинарной точности.
double: 64-битный IEEE 754 тип данных с плавающей точкой двойной точности.

🟠Логический тип
boolean: Представляет логическое значение (true или false).

🟠Символьный тип
char: 16-битный тип данных, представляющий символ Unicode (диапазон от '\u0000' до '\uffff').

🚩Ссылочные типы данных

Ссылочные типы данных представляют собой объекты и массивы. Они хранят ссылку на область памяти, где хранятся данные объекта или массива.

🟠Классы (Classes)
Любой объектный тип данных является экземпляром класса. Классы могут быть как стандартными (например, String, Integer), так и пользовательскими.
🟠Интерфейсы (Interfaces)
Интерфейсы определяют набор методов, которые должны быть реализованы классами, которые их реализуют.
🟠Массивы (Arrays)
Массивы могут быть одномерными или многомерными и могут хранить как примитивные, так и ссылочные типы данных.

🚩Пример примитивных и ссылочных типов данных

public class DataTypesExample {
public static void main(String[] args) {
// Примитивные типы данных
byte aByte = 10;
short aShort = 100;
int anInt = 1000;
long aLong = 10000L;
float aFloat = 10.5f;
double aDouble = 10.55;
boolean aBoolean = true;
char aChar = 'A';

// Ссылочные типы данных
String aString = "Hello, World!";
Integer anInteger = 1000;
int[] anArray = {1, 2, 3, 4, 5};

// Вывод примитивных типов данных
System.out.println("byte: " + aByte);
System.out.println("short: " + aShort);
System.out.println("int: " + anInt);
System.out.println("long: " + aLong);
System.out.println("float: " + aFloat);
System.out.println("double: " + aDouble);
System.out.println("boolean: " + aBoolean);
System.out.println("char: " + aChar);

// Вывод ссылочных типов данных
System.out.println("String: " + aString);
System.out.println("Integer: " + anInteger);
System.out.println("Array: " + java.util.Arrays.toString(anArray));
}
}


Ставь 👍 и забирай 📚 Базу знаний
👍8
🤔 Как используется SOLID принцип открытости-закрытости при проектировании?

Принцип открытости-закрытости (Open-Closed Principle) предполагает, что программные сущности должны быть открыты для расширения, но закрыты для модификации. Это достигается через использование абстракций, интерфейсов и наследования.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍12
🤔 Расскажи про Hash Code & Equals Contract

Когда вы переопределяете методы equals() и hashCode(), важно соблюдать контракт, иначе объект может вести себя некорректно в коллекциях (HashMap, HashSet и др.).

🚩Контракт `equals()`

Метод equals() определяет, когда два объекта равны.
Рефлексивность – x.equals(x) всегда true.
Симметричность – x.equals(y) == y.equals(x).
Транзитивность – если x.equals(y) и y.equals(z), то x.equals(z).
Согласованность – многократные вызовы x.equals(y) дают один и тот же результат.
Сравнение с null всегда falsex.equals(null) == false.
class Person {
private String name;
private int age;

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

@Override
public boolean equals(Object obj) {
if (this == obj) return true; // Проверка на одинаковые ссылки
if (obj == null || getClass() != obj.getClass()) return false; // Проверка типа
Person person = (Person) obj;
return age == person.age && name.equals(person.name); // Сравнение полей
}
}


🚩Контракт `hashCode()`

Метод hashCode() возвращает числовой хеш-код объекта. Он должен соответствовать equals()!
Если x.equals(y) == true, то x.hashCode() == y.hashCode().
Если x.hashCode() != y.hashCode(), то x.equals(y) == false (но обратное не всегда верно).
Хеш-код должен оставаться неизменным, если объект не изменяется.
@Override
public int hashCode() {
return Objects.hash(name, age);
}


🚩Почему контракт `equals()` и `hashCode()` важен?

В коллекциях, таких как HashSet, HashMap, HashTable, объекты хранятся по хеш-коду.
Что будет, если equals() переопределён, но hashCode() – нет?
Set<Person> people = new HashSet<>();
people.add(new Person("Иван", 25));
people.add(new Person("Иван", 25)); // Ожидаем, что не добавится

System.out.println(people.size()); // Будет 2, а не 1, если `hashCode()` отсутствует!


Правильный вариант (equals() + hashCode())
class Person {
private String name;
private int age;

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

@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return age == person.age && name.equals(person.name);
}

@Override
public int hashCode() {
return Objects.hash(name, age);
}
}


Ставь 👍 и забирай 📚 Базу знаний
👍6🤔2
🤔 В чём разница между Dependency Injection и инверсией управления?

Inversion of Control (IoC) — это общая концепция, при которой внешняя система управляет созданием и связыванием объектов.
Dependency Injection (DI) — это конкретный способ реализации IoC, при котором зависимости:
- внедряются в объект через:
- конструктор;
- поле;
- метод.
Иными словами:
- IoC — это философия.
- DI — это инструмент.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍13🔥6
🤔 Чем отличается компонент от бина?

В Spring оба термина – Bean и Component – связаны с управляемыми объектами, но у них разное предназначение.

🚩Что такое `@Component`?

@Component делает класс Spring-бином автоматически.
Spring сам создаст и зарегистрирует объект в контейнере.
import org.springframework.stereotype.Component;

@Component
public class Car {
public void drive() {
System.out.println("Машина едет...");
}
}


Как получить объект?
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Car car = context.getBean(Car.class);
car.drive();
}
}


🚩Что такое `@Bean`?

@Bean создаёт Bean вручную в @Configuration-классе.
Можно использовать, если нужно передать параметры или создать Bean из библиотеки.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {
@Bean
public Car car() {
return new Car(); // Создаём объект вручную
}
}


Как получить Bean?
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Car car = context.getBean(Car.class);


Ставь 👍 и забирай 📚 Базу знаний
👍4
🤔 Чем отличается композиция от наследования?

- Наследование — это отношение "является", позволяет использовать поведение родительского класса.
- Композиция — это отношение "имеет", позволяет встраивать поведение других объектов и менять их без изменения иерархии. Композиция более гибкая и предпочтительна при проектировании.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍4🔥1💊1
🤔 Чем отличаются методы Thread.sleep() и Thread.yield()?

Методы Thread.sleep() и Thread.yield() в Java используются для управления потоком, но работают по-разному.

🚩Thread.sleep()

Метод Thread.sleep(milliseconds) приостанавливает выполнение текущего потока на указанное время.

🚩Как это работает?

Поток уходит в состояние "ожидания" (TIMED_WAITING).
Операционная система не даёт ему процессорное время в течение заданного времени.
После завершения паузы поток снова становится готовым к выполнению (RUNNABLE).

public class SleepExample {
public static void main(String[] args) {
System.out.println("Начало работы...");
try {
Thread.sleep(2000); // Пауза на 2 секунды
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Прошло 2 секунды");
}
}


Результат
Начало работы...
(пауза 2 секунды)
Прошло 2 секунды


🚩Thread.yield()

Метод Thread.yield() даёт возможность другим потокам выполнить работу.

🚩Как это работает?

Текущий поток передаёт управление планировщику (scheduler).
Если есть другие потоки с таким же или более высоким приоритетом, они получат процессорное время.
Если таких потоков нет, то текущий поток продолжит выполняться.

public class YieldExample {
public static void main(String[] args) {
Runnable task = () -> {
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + " - " + i);
Thread.yield(); // Позволяет другим потокам выполняться
}
};

Thread t1 = new Thread(task, "Поток-1");
Thread t2 = new Thread(task, "Поток-2");

t1.start();
t2.start();
}
}


Результат (примерный, зависит от планировщика ОС)
Поток-1 - 1
Поток-2 - 1
Поток-1 - 2
Поток-2 - 2
...


Ставь 👍 и забирай 📚 Базу знаний
👍4
🤔 Request мы можем использовать во всех Spring приложениях?

Нет. Объект HttpServletRequest и скоуп request доступны только в веб-приложениях на Spring MVC или Spring Boot.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍6
🤔 Что должно быть если объект equals нал?

Метод equals() в Java предназначен для проверки равенства двух объектов. Если объект, переданный в метод equals(), равен null, метод должен возвращать `false`, так как объект не может быть равен null по определению. Рассмотрим этот случай более подробно.

🚩Почему важно обрабатывать `null` в `equals()`?

🟠Контракт метода `equals()`
Согласно спецификации Java, метод equals() должен возвращать false, если объект, переданный в метод, равен null. Это необходимо для корректного сравнения объектов.
Симметричность: Если a.equals(b) возвращает true, то b.equals(a) также должно возвращать true.
Для null: Вызов x.equals(null) всегда должен возвращать false.

🟠Предотвращение `NullPointerException`
Если метод equals() не проверяет, является ли объект null, попытка доступа к его полям или методам вызовет NullPointerException.

🚩Как правильно реализовать метод `equals()`?

Вот стандартный способ реализации метода equals()
@Override
public boolean equals(Object obj) {
// Проверяем, равен ли объект null
if (obj == null) {
return false;
}

// Проверяем, является ли объект тем же самым
if (this == obj) {
return true;
}

// Проверяем, принадлежит ли объект тому же классу
if (getClass() != obj.getClass()) {
return false;
}

// Приводим объект к текущему типу
MyClass other = (MyClass) obj;

// Сравниваем необходимые поля
return this.field.equals(other.field);
}


🚩Что происходит, если игнорировать проверку `null`?

Если вы не проверяете null, код вроде следующего приведет к ошибке
MyClass obj1 = new MyClass("Hello");
MyClass obj2 = null;

System.out.println(obj1.equals(obj2)); // Если нет проверки null -> NullPointerException


Пример с проверкой null
class MyClass {
private String value;

public MyClass(String value) {
this.value = value;
}

@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (this == obj) {
return true;
}
if (getClass() != obj.getClass()) {
return false;
}
MyClass other = (MyClass) obj;
return value.equals(other.value);
}
}

public class Main {
public static void main(String[] args) {
MyClass obj1 = new MyClass("Hello");
MyClass obj2 = null;

System.out.println(obj1.equals(obj2)); // false
}
}


Ставь 👍 и забирай 📚 Базу знаний
👍1