fa
Feedback
Секреты Java

Секреты Java

رفتن به کانال در Telegram

Обсуждаем Java, архитектуру, фреймворки и всё, о чем должны говорить разработчики. Владелец: @Alexey_Morl Реклама на бирже: https://telega.in/c/java_secrets

نمایش بیشتر
7 991
مشترکین
اطلاعاتی وجود ندارد24 ساعت
اطلاعاتی وجود ندارد7 روز
اطلاعاتی وجود ندارد30 روز
آرشیو پست ها
Варианты использования аннотации @OneToOne Аннотация JPA @OneToOne необходима нам для установления связи один-к-одному для записей из двух таблиц. Пример: у нас есть человек-гражданин (пусть это будет Person) и у него есть один действующий паспорт (Passport). Существует несколько вариантов использования этой аннотации, о которых следует знать. Рассмотрим на примере Person и Passport: ▫️Внешний ключ В первом варианте таблица Person имеет внешний ключ passport_id:
@Entity
@Table(name = "persons")
public class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "passport_id", referencedColumnName = "id")
    private Passport passport;
}

@Entity
@Table(name = "passports")
public class Passport {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String number;

    @OneToOne(mappedBy = "passport")
    private Person person;
}
Параметр name в аннотации @JoinColumn служит для указания внешнего ключа в таблице persons, а referencedColumnName - для определения первичного ключа таблицы passports, на который будет ссылаться внешний ключ passport_id. ▫️Общий первичный ключ В данном варианте у нас нет дополнительного столбца для связи таблиц. Эту роль взял на себе первичный ключ person_id из таблицы passports, который так же является и внешним ключом.
@Entity
@Table(name = "persons")
public class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @OneToOne(mappedBy = "person", cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumn
    private Passport passport;
}

@Entity
@Table(name = "passports")
public class Passport {
    @Id
    @Column(name = "person_id")
    private Long id;

    private String number;

    @OneToOne
    @MapsId
    @JoinColumn(name = "person_id")
    private Person person;
}
Аннотация @PrimaryKeyJoinColumn указывает на то, что первичный ключ из таблицы persons будет использоваться в качестве внешнего ключа в passports. А аннотация @MapsId говорит о том, что обе таблицы используют один первичный ключ и он будет скопирован из "родительской" таблицы persons в в таблицу passports.

Как управлять выделенной памятью для нашего приложения? Может случится, что в процессе развития наше приложение выросло на столько, что начинает долго соображать или вовсе падать с OutOfMemoryError. Если дело не в memory leak, то пора выделить приложению больше памяти. Параметры -Xms и -Xmx в Java используются для управления выделением и использованием памяти при выполнении приложений. Пример:
java -Xms512m -Xmx2g MyApp
▫️Тут мы запускаем приложение MyApp стандартной командой (при запуске из Idea идем в "Run" -> "Edit Configurations..." и у выбранного приложения прописываем параметры в "VM options") ▫️Параметр -Xms задает начальный размер выделенной памяти для JVM. В примере выше JVM начинает работу, выделяя 512 мегабайт. Установка оптимального значения -Xms может помочь ускорить запуск JVM и улучшить производительность приложения. ▫️Параметр -Xmx указывает максимальный размер памяти, который JVM может использовать. На примере - 2ГБ. Установка правильного значения -Xmx поможет нам предотвратить переполнение памяти. ▫️Виды значений, которые можно установить для параметров -Xms и -Xmx, указываются в килобайтах (k), мегабайтах (m) или гигабайтах (g). ▫️Без указания параметров, дефолтные значения рассчитываются JVM в зависимости от операционной системы и характеристик компьютера. Обычно имеет смысл устанавливать значение -Xms равным -Xmx, это позволит приложению запускаться быстрее, не тратить время на выделение дополнительной памяти и не конкурировать с другими приложениями за свободную память компьютера.

Интерфейс (interface) Интерфейс (interface) — это специальный тип, который содержит только абстрактные методы (до Java 8) и константы. Интерфейсы служат для задания контракта, который классы обязаны реализовать, если они его используют. В интерфейсе нельзя создать реализацию методов (до Java 8), и все методы по умолчанию являются public и abstract. С версии Java 8 в интерфейсах можно использовать методы с реализацией (default и static), а с версии Java 9 — private методы. Интерфейсы позволяют реализовать множественное наследование, поскольку класс может реализовать несколько интерфейсов.

new vs valueOf Старайтесь всегда использовать метод valueOf вместо конструктора в стандартных классах-оболочках примитивных т
new vs valueOf Старайтесь всегда использовать метод valueOf вместо конструктора в стандартных классах-оболочках примитивных типов, кроме тех случаев, когда вам нужно конкретно выделить память под новое значение. Все оболочки, кроме чисел с плавающей точкой, от Byte до Long, имеют кэш. По умолчанию этот кэш содержит значения от -128 до 127. Следовательно, если ваше значение попадает в этот диапазон, то значение вернется из кэша. Значение из кэша достается в 3.5 раза быстрее, чем при использовании конструктора, плюс идёт экономия памяти. Помимо этого, наиболее часто используемые значения могут также быть закэшированы компилятором и виртуальной машиной.

Паттерн Builder или Строитель Этот паттерн является порождающим и используется для создания объектов с большим количеством необязательных полей.
Паттерн Строитель инкапсулирует конструирование продукта и позволяет разделить его на этапы
"Паттерны проектирования" Э. Фриман Давайте на примере: У нас есть класс Person с обязательным полем name и необязательными полями age и address.
public class Person {
    private final String name; // Обязательное
    private int age; // Необязательное
    private String address; // Необязательное

    private Person(Builder builder) {
        this.name = builder.name;
        this.age = builder.age;
        this.address = builder.address;
    }

// Геттеры

    public static class Builder {
        private final String name;
        private int age;
        private String address;

        public Builder(String name) {
            this.name = name;
        }

        public Builder age(int age) {
            this.age = age;
            return this;
        }

        public Builder address(String address) {
            this.address = address;
            return this;
        }

        public Person build() {
           return new Person(this);
        }
    }
}
Вложенный класс Builder используется для "поэтапного и инкапсулированного" конструирования объекта Person. Мы можем установить необходимые поля с помощью методов age() и address(), а затем создать объект Person с помощью метода build():
var person = new Person.Builder("John")
                        .age(30)
                        .address("My Street")
                        .build();
Без паттерна Builder нам бы пришлось делать 4 конструктора для двух необязательных полей, чтобы покрыть возможные варианты создания объекта! Таким образом, шаблон Builder обеспечивает удобный способ создания объектов с различными составом полей, делая код более читаемым и гибким, но будет плохим решением если все поля обязательные. Кстати, библиотека lombok предлагает очень удобную аннотацию @Builder.

CompletableFuture CompletableFuture — это класс в Java, введенный в Java 8, который представляет собой асинхронную задачу, ко
CompletableFuture CompletableFuture — это класс в Java, введенный в Java 8, который представляет собой асинхронную задачу, которая будет выполнена в будущем и возвращает результат. CompletableFuture предоставляет множество методов для работы с асинхронными задачами, комбинирования их и управления их выполнением. Основные возможности CompletableFuture включают: — Вы можете запустить задачу асинхронно и продолжить выполнение кода без блокировки. — CompletableFuture позволяет комбинировать результаты нескольких задач, выполняя определенные действия при завершении каждой задачи. — Вы можете указать обработчики ошибок для обработки исключений, возникающих во время выполнения задачи. — Вы можете блокировать выполнение кода и ждать завершения задачи.

Имя класса В Java у Class существует несколько методов, которые возвращают имя класса, но в разных формах и служат разным целям - это getSimpleName(), getCanonicalName(), getTypeName() и getName(). Часто они вызывают путаницу и в этом посте мы попытаемся с ними разобраться. Допустим у нас есть класс:
package com.example;

public class Outer {
    public class Inner {
    }
}
Вызов метода: ▫️getSimpleName() - возвращает просто название класса без учета пакета и вложенных классов.
Outer.class.getSimpleName();
Inner.class.getSimpleName();
Код в строках выше вернет 'Outer' и 'Inner' соответственно. Метод может быть полезен, например, для логирования, но возвращаемое значение не гарантирует уникальность. ▫️getName() - возвращает полное имя класса, включая пакет, а вложенные классы отделяются символом "$". В рамках определенного ClassLoader этот метод возвращает уникальные имена и эти имена мы можем использовать для динамической загрузки классов через Class.forName().
Outer.class.getName();
Inner.class.getName();
Вернут 'com.example.Outer' и 'com.example.Outer$Inner'. ▫️getCanonicalName() - возвращает каноническое имя, как определено в JLS. Включает пакет и вложенные класса, отделенные точкой. Каноническое имя используется оператором import.
Outer.class.getCanonicalName();
Inner.class.getCanonicalName();
Вернут 'com.example.Outer' и 'com.example.Outer.Inner'. ▫️getTypeName() - возвращает "an informative string for the name of this type" - информативную строку для имени этого типа. Это имя только информативно и не имеет никакой контрактной значимости.
Outer.class.getTypeName();
Inner.class.getTypeName();
Вернут то же, что и getName();

Redis как брокер сообщений Redis не является брокером сообщений, но всё-таки позволяет реализовать простую модель обмена сообщениями. В отличие от RabbitMQ и Kafka, с помощью Redis не получится настроить сложную маршрутизацию. Сообщения будут отправляться в определенные каналы, из которых подписчики будут получать данные. Для реализации обмена сообщений необходимо настроить подключение к Redis. Как это сделать, было рассказано в предыдущих статьях. Создадим класс для отправки сообщений в нужный топик(канал):
@Service
@RequiredArgsConstructor
public class Producer {

    private final RedisTemplate<String, String> redisTemplate;
    private final ChannelTopic channelTopic;

    public void produce(String message) {
        redisTemplate.convertAndSend(channelTopic.getTopic(), message);
    }
}
Данный класс публикует сообщение, используя RedisTemplate. Метод convertAndSend принимает название топика и само сообщение. В этом примере используется класс ChannelTopic из пакета org.springframework.data.redis.listener. Вместо этого можно передавать сроковое значение, которое будет содержать название очереди. Создадим класс, в котором будет содержаться логика обработки сообщений:
@Service
public class Receiver {

    public void handleMessage(String message) {
      System.out.println("Received: " + message);
    }
}
В классе конфигурации добавим следующие бины:
@Bean
public ChannelTopic channelTopic() {
    return new ChannelTopic("channel");
}
ChannelTopic - класс хранящий название топика для сообщений.
@Bean
public MessageListenerAdapter messageListenerAdapter(Receiver receiver) {
    return new MessageListenerAdapter(receiver);
}
MessageListenerAdapter - класс, являющийся подписчиком для топика. Этот класс делегирует обработку сообщения пользовательскому классу, который должен реализовывать интерфейс MessageListener либо содержать метод с названием handleMessage. Название метода можно изменить, но тогда при определении MessageListenerAdapter необходимо указать название метода, который будет получать сообщения.
@Bean
public RedisMessageListenerContainer сontainer(RedisConnectionFactory redisConnectionFactory,MessageListenerAdapter messageListenerAdapter, ChannelTopic channelTopic) {

    var container = new RedisMessageListenerContainer();
    container.setConnectionFactory(redisConnectionFactory);
    container.addMessageListener(messageListenerAdapter, channelTopic);
    return container;
}
RedisMessageListenerContainer - это класс, который обрабатывает низкоуровневые детали получения и преобразования сообщений. С помощью метода addMessageListener() указывается слушатель сообщений и название топика, из которого будут читаться данные. Redis может быть хорем выбор в качестве брокера сообщений из-за высокой скорости работы. Но если важна надежность, гарантия доставки сообщений и сложная маршрутизация, то лучше сделать выбор в пользу RabbitMQ или Kafka.

Метод isPowerOfTwo() Метод isPowerOfTwo() используется для проверки, является ли число степенью двойки. Этот метод полезен пр
Метод isPowerOfTwo() Метод isPowerOfTwo() используется для проверки, является ли число степенью двойки. Этот метод полезен при работе с битовыми операциями и оптимизации алгоритмов.

StringBuilder Все мы знаем, что объекты типа String являются immutable (неизменяемыми) и каждое их изменение влечет добавление новой строки в String pool. А, что если в вашей программе строки часто изменяются? Это приведет к расходованию памяти, а этот ресурс не бесконечный. В таком случае лучше использовать StringBuilder . StringBuilder - это класс из пакета java.lang, который предоставляет возможность работы с изменяемыми строками. Вместо обычных строк StringBuilder под капотом использует массив, размер(capacity) которого по умолчанию равен 16. Существует несколько способов объявить переменную типа StringBuilder:
StringBuilder sb1 = new StringBuilder(); 
StringBuilder sb2 = new StringBuilder("Java"); 
StringBuilder sb3 = new StringBuilder(sb2);
StringBuilder sb4 = new StringBuilder(20); 
▫️Использовать пустой конструктор - capacity = 16 ▫️Передать в конструктор сроку - capacity = длина строки + 16 ▫️Передать в конструктор любой класс, который реализует интерфейс CharSequence - capacity = длина CharSequence + 16 ▫️Указать нужный capacity при создании переменной Модификация значений происходит с помощью встроенных методов: ▫️append("str") - добавление строки в конец ▫️insert(index, "str") - добавление строки в определенную позицию ▫️sb.delete(start, end) - удаление подстроки Вот пример их использования:
sb.append("Hello");
sb.insert(5, " World"); 
sb.delete(6, 11); 
Важная особенность StringBuilder заключается в том, что его нельзя использовать в многопоточной среде, но для этой цели существует StringBuffer, который предоставляет такой же функционал, но скорость его работы, естественно, ниже.

Классы с поддержкой атомарных операций Если в вашем коде нужна переменная, к которой будут обращаться разные потоки не только для чтения, но и для записи, можно использовать volatile + synchronized. Но это влечет за собой потери в производительности. Для решения этой проблемы в Java есть пакет java.concurrent.atomic, который содержит классы для работы с атомарными операциями. Атомарная операция - это операция, которая может быть безопасно выполнена в многопоточной среде. Классы из пакета java.concurrent.atomic используют низкоуровневые операции, такие как CAS (compare-and-swap). Подобные инструкции работают быстрее, чем синхронизация через блокировки потоков. Вот пример использования класса AtomicInteger:

public class Counter {
    private final AtomicInteger counter = new AtomicInteger(0);
    
    int getValue() {
        return counter.get();
    }
    
    void increment() {
        counter.incrementAndGet();
    }
}
В данном коде определяется переменная counter типа AtomicInteger, через метод getValue() можно получить текущее значение счетчика, а через increment() - увеличить его значение. В этом примере мы можем быть уверены, что в многопоточной среде этот класс будет иметь предсказуемое поведение! Класс AtomicInteger содержит множество методов для выполнения атомарных операций: ▫️incrementAndGet() - увеличивает на единицу значение и возвращает значение ▫️getAndIncrement() - возвращает значение и после увеличивает значение на единицу ▫️ decrementAndGet() и getAndDecrement() - работают аналогично increment методам, но уменьшают значение ▫️ set(int value) - присваивает новое значение А так же множество других методов. Помимо AtomicInteger, существуют подобные классы для работы с Boolean, Long, массивами и ссылками на объекты. Использование этих классов позволяет избавить от лишних блоков синхронизации потоков, которые могут вызвать deadlock. Несмотря на высокую производительность этих классов, не стоит их использовать в однопоточных программах, так как накладные расходы на выполнение атомарных операций увеличивают время выполнения кода.

Задачи с собеседований: Сортировка пузырьком (jun) - Расскажите про сортировку пузырьком и реализуйте её. Это простой алгорит
Задачи с собеседований: Сортировка пузырьком (jun) - Расскажите про сортировку пузырьком и реализуйте её. Это простой алгоритм сортировки, который использует два вложенных цикла. Внешний цикл отвечает за количество проходов по массиву, а внутренний сравнивает соседние элементы. Если текущий элемент больше следующего, они меняются местами. Так продолжается, пока массив не будет отсортирован. 💡 Ключевые моменты: ▪️ Худший случай: O(n^2) ▪️ Используется строго для небольших наборов данных. Реализация на картинке 👆

Java Naming Conventions. Названия констант Конвенции, изложенные в официальной документации Oracle и Google Java Style Guide помогают поддерживать единый стиль в коде и упрощают его сопровождение. Начнем с того, что такое константы? Константами обычно считаются переменные, объявленные как static final. Они неизменяемы после инициализации и часто используются для хранения значений, которые не должны изменяться на протяжении всего выполнения программы. Это - начало определения. Его дополнит определение из Основные правила по соглашениям об именовании: ▫️Oracle :
Константы должны быть написаны в верхнем регистре, а слова разделены символами подчеркивания (_)."
Так назваемый UPPER_SNAKE_CASE. Пример:
public static final int MAX_WIDTH = 100;
public static final String DEFAULT_LANGUAGE = "EN";
▫️Google дает еще более точное определение:
Но что такое константа, на самом деле? Константы — это статические final-поля, содержимое которых глубоко неизменяемо (deeply immutable), а методы не имеют обнаруживаемых побочных эффектов. Примерами служат примитивы, строки, неизменяемые классы значений и все, что установлено в null. Если какое-либо из наблюдаемых состояний экземпляра может измениться, оно не является константой.
И приводит интересные примеры:
java 
// Constants
static final int NUMBER = 5;
static final ImmutableList<String> NAMES = ImmutableList.of("Ed", "Ann");
static final Map<String, Integer> AGES = ImmutableMap.of("Ed", 35, "Ann", 32);
static final Joiner COMMA_JOINER = Joiner.on(','); // because Joiner is immutable
static final SomeMutableType[] EMPTY_ARRAY = {};

// Not constants
static String nonFinal = "non-final";
final String nonStatic = "non-static";
static final Set<String> mutableCollection = new HashSet<String>();
static final ImmutableSet<SomeMutableType> mutableElements = ImmutableSet.of(mutable);
static final ImmutableMap<String, SomeMutableType> mutableValues =
    ImmutableMap.of("Ed", mutableInstance, "Ann", mutableInstance2);
static final Logger logger = Logger.getLogger(MyClass.getName());
static final String[] nonEmptyArray = {"these", "can", "change"};
Т.е. если объект может менять свое состояние - он не является константой и именовать его стоит по конвенциям именования переменных - lowerCamelCase. Заключение Конвенции именования констант в Java помогают сделать код более читаемым, структурированным и легким для поддержки. Следуя рекомендациям Oracle и Google, вы улучшаете качество своего кода, делая его понятным для других разработчиков и будущих версий вас самих.

Паттерны проектирования Шаблон (паттерн) проектирования (design pattern) — это проверенное и готовое к использованию решение. Это не класс и не библиотека, которую можно подключить к проекту, это нечто большее - он не зависит от языка программирования, не является законченным образцом, который может быть прямо преобразован в код и может быть реализован по-разному в разных языках программирования. ПОРОЖДАЮЩИЕ ПАТТЕРНЫ ✔️ Абстрактная фабрика (Abstract factory) - Класс, который представляет собой интерфейс для создания других классов. ✔️ Строитель (Builder) - Класс, который представляет собой интерфейс для создания сложного объекта. ✔️ Фабричный метод (Factory method) - Делегирует создание объектов наследникам родительского класса. Это позволяет использовать в коде программы не специфические классы, а манипулировать абстрактными объектами на более высоком уровне. ✔️ Прототип (Prototype) - Определяет интерфейс создания объекта через клонирование другого объекта вместо создания через конструктор. ✔️ Одиночка (Singleton) - Класс, который может иметь только один экземпляр. СТРУКТУРНЫЕ ПАТТЕРНЫ ✔️ Адаптер (Adapter) - Объект, обеспечивающий взаимодействие двух других объектов, один из которых использует, а другой предоставляет несовместимый с первым интерфейс. ✔️ Мост (Bridge) - Структура, позволяющая изменять интерфейс обращения и интерфейс реализации класса независимо. ✔️ Компоновщик (Composite) - Объект, который объединяет в себе объекты, подобные ему самому. ✔️ Декоратор (Decorator) - Класс, расширяющий функциональность другого класса без использования наследования. ✔️ Фасад (Facade) - Объект, который абстрагирует работу с несколькими классами, объединяя их в единое целое. ✔️ Приспособленец (Flyweight) - Это объект, представляющий себя как уникальный экземпляр в разных местах программы, но по факту не являющийся таковым. ✔️ Заместитель (Proxy) - Объект, который является посредником между двумя другими объектами, и который реализует/ограничивает доступ к объекту, к которому обращаются через него. ПОВЕДЕНЧЕСКИЕ ПАТТЕРНЫ ✔️ Цепочка обязанностей (Chain of responsibility) - Предназначен для организации в системе уровней ответственности. ✔️ Команда (Command) - Представляет действие. Объект команды заключает в себе само действие и его параметры. ✔️ Интерпретатор (Interpreter) - Решает часто встречающуюся, но подверженную изменениям, задачу. ✔️ Итератор (Iterator) - Представляет собой объект, позволяющий получить последовательный доступ к элементам объекта-агрегата без использования описаний каждого из объектов, входящих в состав агрегации. ✔️ Посредник (Mediator) - Обеспечивает взаимодействие множества объектов, формируя при этом слабую связанность и избавляя объекты от необходимости явно ссылаться друг на друга. ✔️ Хранитель (Memento) - Позволяет, не нарушая инкапсуляцию зафиксировать и сохранить внутренние состояния объекта так, чтобы позднее восстановить его в этих состояниях. ✔️ Наблюдатель (Observer) - Определяет зависимость типа «один ко многим» между объектами таким образом, что при изменении состояния одного объекта все зависящие от него оповещаются об этом событии. ✔️ Состояние (State) - Используется в тех случаях, когда во время выполнения программы объект должен менять своё поведение в зависимости от своего состояния. ✔️ Стратегия (Strategy) - Предназначен для определения семейства алгоритмов, инкапсуляции каждого из них и обеспечения их взаимозаменяемости. ✔️ Шаблонный метод (Template method) - Определяет основу алгоритма и позволяет наследникам переопределять некоторые шаги алгоритма, не изменяя его структуру в целом. ✔️ Посетитель (Visitor) - Описывает операцию, которая выполняется над объектами других классов. При изменении класса Visitor нет необходимости изменять обслуживаемые классы.

Захват переменных в лямбда-выражениях Лямбда-выражения могут захватывать переменные из внешнего окружения, делая их доступными внутри лямбда-функции. При этом переменные могут быть: 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 доступен, так как это статическое поле.

Что такое класс Object? Какие в нем есть методы? Object это базовый класс для всех остальных объектов в Java. Любой класс наследуется от Object и, соответственно, наследуют его методы: public boolean equals(Object obj) – служит для сравнения объектов по значению; int hashCode() – возвращает hash код для объекта; String toString() – возвращает строковое представление объекта; Class getClass() – возвращает класс объекта во время выполнения; protected Object clone() – создает и возвращает копию объекта; void notify() – возобновляет поток, ожидающий монитор; void notifyAll() – возобновляет все потоки, ожидающие монитор; void wait() – остановка вызвавшего метод потока до момента пока другой поток не вызовет метод notify() или notifyAll() для этого объекта; void wait(long timeout) – остановка вызвавшего метод потока на определённое время или пока другой поток не вызовет метод notify() или notifyAll() для этого объекта; void wait(long timeout, int nanos) – остановка вызвавшего метод потока на определённое время или пока другой поток не вызовет метод notify() или notifyAll() для этого объекта; protected void finalize() – может вызываться сборщиком мусора в момент удаления объекта при сборке мусора.

Что такое Aspect, Advice, Pointcut и JoinPoint в АОП? Aspect — класс, который реализует сквозные функции, такие как управление транзакциями. Advice — методы, которые выполняются, когда в приложении достигается определенный JoinPoint с соответствующим Pointcut. Pointcut — набор регулярных выражений, которые сопоставляются с JoinPoint, чтобы определить, нужно ли выполнять Advice или нет. JoinPoint — некая точка во время выполнения программы, например выполнение метода или обработка исключения.

Методы isLetter() и isDigit() ▶️Character.isLetter() — метод, который проверяет, является ли символ буквой. Он возвращает зна
Методы isLetter() и isDigit() ▶️Character.isLetter() — метод, который проверяет, является ли символ буквой. Он возвращает значение true, если символ является буквой, и false в противном случае. ▶️Character.isDigit() — метод, который проверяет, является ли символ цифрой. Он возвращает значение true, если символ является цифрой, и false в противном случае.

Spring Profiles Spring Profiles - это мощный механизм Spring Framework, который позволяет легко настраивать контекст и использовать разные конфигурации для разных сред. ▫️Как определить профили? Профили определяются с использованием аннотации @Profile. Каждый профиль может иметь свои собственные настройки и бины:
@Configuration
@Profile("dev")
public class DevConfig {
}
Так же можно повесить эту аннотацию на класс компонента:
@Component
@Profile("dev")
public class DevComponent {
}
или метод создания бина в классе конфигурации:
@Configuration
public class Config {

  @Bean
  @Profile("dev")
  public MyBean myBean() {
    return new MyBean();
}
Для xml конфигурации:
<beans profile="dev">
    <bean id="myBean" class="org.demo.MyBean" />
</beans>
▫️Как установить активный профиль? Чтобы установить активный профиль, так же есть несколько различных вариантов: - Через JVM параметры
-Dspring.profiles.active=dev
- Профили можно активировать через переменные окружения (для Unix/Windows соответственно):
export SPRING_PROFILES_ACTIVE=dev
set SPRING_PROFILES_ACTIVE=dev
- Через файл application.properties (yaml):
spring.profiles.active=dev
- Программно через ConfigurableEnvironment:
@Autowired
private ConfigurableEnvironment env;
...
env.setActiveProfiles("dev");
▫️Рассмотрим простой пример для понимания: Есть два класса конфигурации для дева и прода:
@Configuration
@Profile("dev")
public class DevConfig {
   // Конфигурация для дев
}

@Configuration
@Profile("prod")
public class ProdConfig {
   // Конфигурация для прода
}
В файле application.yml:
spring:
    profiles:
       active: dev
В этом примере Spring будет использовать конфигурацию из DevConfig , а ProdConfig - только при активации профиля prod. Профили могут использоваться для конфигурации различных аспектов приложения, таких как базы данных, настройки безопасности, внешние сервисы и многое другое. Например, можно настроить разные подключения к базам данных для разных сред выполнения (тестирование, разработка, продакшн).

Предположим, что класс В наследует класс А. Какое утверждение обязательно верно?
Anonymous voting