Java for Beginner
681 subscribers
579 photos
158 videos
12 files
881 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Нюансы и внутренние механизмы

Управление памятью.
Maven работает как
Java-приложение, и его потребление памяти зависит от размера проекта, количества зависимостей и используемых плагинов.

Основные объекты в памяти:

POM model: Хранит структуру проекта, зависимости и конфигурации. Создается при загрузке POM-файла и обновляется на каждой фазе.
Plugin instances: Каждый плагин создает экземпляр объекта Mojo, который хранит конфигурацию и состояние выполнения.
Classloaders: Maven использует отдельные загрузчики классов для плагинов, чтобы изолировать их зависимости. Это может привести к фрагментации памяти при большом количестве плагинов.


Для оптимизации памяти можно использовать флаги JVM, такие как -Xmx и -Xms, или минимизировать количество одновременно выполняемых плагинов.
Обработка ошибок. Если фаза завершается с ошибкой (например, тест не проходит), Maven прерывает выполнение цикла. Промежуточные результаты сохраняются в target, но объекты в памяти освобождаются сборщиком мусора. Это важно учитывать при отладке, так как повторный запуск может требовать полной перезагрузки модели проекта.


Многомодульные проекты.
В проектах с несколькими модулями Maven создает отдельную модель для каждого модуля, что увеличивает потребление памяти. Фазы выполняются для каждого модуля последовательно, но Maven оптимизирует процесс, кэшируя зависимости в локальном репозитории.


Параллельное выполнение.
С флагом -T (например, -T 4 для четырех потоков) Maven может выполнять фазы параллельно для разных модулей. Это ускоряет сборку, но увеличивает пиковое потребление памяти, так как одновременно обрабатываются несколько моделей POM и плагинов.

Кэширование
.
Maven кэширует зависимости и артефакты в локальном репозитории (~/.m2/repository), что снижает нагрузку на сеть и память при повторных сборках. Однако при использовании флага --offline Maven полностью полагается на локальный кэш, что может вызвать ошибки, если зависимости отсутствуют.

#Java #middle #Maven #LifeCicle
3. Основные возможности ObjectMapper

ObjectMapper предоставляет широкий набор функций для настройки сериализации и десериализации.

Вот ключевые возможности:

3.1. Базовые операции

import com.fasterxml.jackson.databind.ObjectMapper;

public class Example {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();

// POJO
public class User {
private String name;
private int age;

// Getters and setters
}

// Сериализация
User user = new User("Alice", 30);
String json = mapper.writeValueAsString(user);
System.out.println(json); // {"name":"Alice","age":30}

// Десериализация
String jsonInput = "{\"name\":\"Bob\",\"age\":25}";
User deserializedUser = mapper.readValue(jsonInput, User.class);
System.out.println(deserializedUser.getName()); // Bob
}
}


3.2. Конфигурация ObjectMapper

ObjectMapper позволяет настраивать поведение через методы конфигурации:
ObjectMapper mapper = new ObjectMapper();

// Игнорировать неизвестные поля при десериализации
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

// Включить форматирование JSON (pretty print)
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);

// Игнорировать null-поля при сериализации
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);


3.3. Аннотации

Jackson предоставляет множество аннотаций для управления сериализацией/десериализацией:
@JsonProperty("customName"): Переименовывает поле в JSON.
@JsonIgnore: Исключает поле из сериализации/десериализации.
@JsonInclude(JsonInclude.Include.NON_NULL): Исключает null-поля.
@JsonFormat: Задает формат для дат, чисел и других типов.
@JsonTypeInfo и @JsonSubTypes: Поддержка полиморфизма.


Пример:
public class User {
@JsonProperty("full_name")
private String name;

@JsonIgnore
private String password;

@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate birthDate;
}


3.4. Работа с Generics и сложными типами

Для работы с коллекциями или сложными типами используется TypeReference:
List<User> users = mapper.readValue(json, new TypeReference<List<User>>(){});


3.5. Потоковая обработка

Для больших данных можно использовать потоковый API:
JsonGenerator generator = mapper.getFactory().createGenerator(new FileOutputStream("output.json"));
generator.writeStartObject();
generator.writeStringField("name", "Alice");
generator.writeEndObject();
generator.close();


3.6. Кастомные сериализаторы/десериализаторы


Для нестандартных типов данных можно написать свои сериализаторы:
public class CustomSerializer extends StdSerializer<CustomType> {
public CustomSerializer() {
super(CustomType.class);
}

@Override
public void serialize(CustomType value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartObject();
gen.writeStringField("custom", value.getValue());
gen.writeEndObject();
}
}

// Регистрация
mapper.registerModule(new SimpleModule().addSerializer(CustomType.class, new CustomSerializer()));


#Java #middle #on_request #ObjectMapper
4. Взаимодействие с Spring

ObjectMapper глубоко интегрирован с Spring Framework, особенно в модуле Spring Web (REST API). Spring использует Jackson для обработки JSON в контроллерах и взаимодействия с клиентами через HTTP.

4.1. Spring Boot и Jackson

Spring Boot автоматически настраивает ObjectMapper как бин, если в зависимостях есть jackson-databind. Вы можете кастомизировать его через свойства или конфигурацию.

Конфигурация через application.properties:
spring.jackson.serialization.indent-output=true
spring.jackson.deserialization.fail-on-unknown-properties=false
spring.jackson.date-format=yyyy-MM-dd


Кастомизация через Java-конфигурацию:
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return mapper;
}
}


4.2. Использование в контроллерах

Spring автоматически использует ObjectMapper для сериализации ответов и десериализации запросов:

@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping
public User createUser(@RequestBody User user) {
// user автоматически десериализуется из JSON
return user;
}

@GetMapping
public List<User> getUsers() {
// Список автоматически сериализуется в JSON
return List.of(new User("Alice", 30));
}
}


4.3. Кастомизация для HTTP

Spring использует MappingJackson2HttpMessageConverter для обработки JSON.

Вы можете настроить его, чтобы использовать кастомный ObjectMapper:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(customObjectMapper());
converters.add(converter);
}

@Bean
public ObjectMapper customObjectMapper() {
return new ObjectMapper().configure(SerializationFeature.INDENT_OUTPUT, true);
}
}



5. Взаимодействие с другими библиотеками


ObjectMapper часто используется в связке с другими библиотеками, которые работают с JSON или требуют сериализации/десериализации.

5.1. MongoDB

Spring Data MongoDB использует Jackson для сериализации объектов в BSON (бинарный JSON).

Вы можете настроить MongoTemplate для использования кастомного ObjectMapper:
@Configuration
public class MongoConfig {
@Bean
public MongoTemplate mongoTemplate(MongoDatabaseFactory factory) {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return new MongoTemplate(factory, new MappingMongoConverter(new DefaultDbRefResolver(factory), new MongoMappingContext()));
}
}


5.2. Kafka

В Apache Kafka ObjectMapper используется для сериализации/десериализации сообщений.

Например:
@Bean
public KafkaTemplate<String, User> kafkaTemplate(ProducerFactory<String, User> producerFactory) {
return new KafkaTemplate<>(producerFactory);
}

@KafkaListener(topics = "user-topic")
public void listen(@Payload String json, ConsumerRecord<String, String> record) throws IOException {
User user = new ObjectMapper().readValue(json, User.class);
System.out.println("Received: " + user);
}


5.3. Elasticsearch

Spring Data Elasticsearch также использует Jackson для работы с JSON. Вы можете настроить RestHighLevelClient с кастомным ObjectMapper.

5.4. Другие форматы

Jackson поддерживает модули для работы с другими форматами (XML, YAML, CBOR). Например:
ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory());
String yaml = yamlMapper.writeValueAsString(user);


#Java #middle #on_request #ObjectMapper
6. Оптимизация и лучшие практики

Переиспользование ObjectMapper:
Создавайте один экземпляр ObjectMapper на приложение, чтобы избежать накладных расходов на инициализацию.
Используйте Spring для внедрения ObjectMapper как бина.


Потоковая обработка:
Для больших JSON используйте JsonParser и JsonGenerator вместо полной загрузки в JsonNode.

Кэширование:
ObjectMapper автоматически кэширует метаданные классов. Избегайте частого создания новых экземпляров.

Обработка ошибок:
Обрабатывайте исключения, такие как JsonProcessingException, для повышения надежности.
Настройте DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES для игнорирования неизвестных полей.


Тестирование:
Проверяйте сериализацию/десериализацию в тестах, особенно для сложных объектов.
Используйте библиотеки, такие как jsonassert, для сравнения JSON.


Безопасность:
Избегайте десериализации непроверенных данных, чтобы предотвратить атаки (например, уязвимости в стиле XXE).
Используйте
@JsonIgnoreProperties для ограничения полей.


7. Распространенные проблемы и их решения

Циклические ссылки:

Используйте @JsonIdentityInfo для обработки циклических зависимостей:
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class User {
private Long id;
private List<User> friends;
}


Неизвестные поля:
Настройте mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).

Неправильные форматы дат:
Используйте @JsonFormat или настройте глобальный формат через mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd")).

Производительность:
Для больших объектов используйте потоковую обработку или минимизируйте использование JsonNode.


8. Пример реального сценария

Допустим, вы разрабатываете REST API для управления пользователями. JSON-запросы и ответы должны быть в определенном формате, а некоторые поля — опциональными.

POJO:
@JsonInclude(JsonInclude.Include.NON_NULL)
public class User {
@JsonProperty("full_name")
private String name;

@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate birthDate;

@JsonIgnore
private String password;

// Getters and setters
}


Контроллер:
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
// Логика сохранения
return ResponseEntity.ok(user);
}
}


Конфигурация:
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule()); // Для LocalDate
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return mapper;
}
}


#Java #middle #on_request #ObjectMapper
Базовый синтаксис Java.
Структура простой программы


Классы в Java: синтаксис и структура

Java — строго объектно-ориентированный язык, и класс является его основной строительной единицей. Классы определяют форму объектов, объединяя состояние (поля) и поведение (методы).

1. Что такое класс в Java?

Класс в Java — это шаблон, по которому создаются объекты.

Он определяет:
Поля (переменные экземпляра) — для хранения состояния объекта.
Конструкторы — для создания и инициализации объектов.
Методы — для описания поведения (в этом уроке мы их не рассматриваем).
Вложенные классы — если нужно логически сгруппировать поведение.
Блоки инициализации — для дополнительной логики во время загрузки или создания объекта.
Каждый объект — это экземпляр класса, имеющий собственное состояние (значения полей) и поведение (определённое методами).



2. Минимальная структура класса
class ClassName {
// Поля
// Конструкторы
// Вложенные классы
// Блоки инициализации
}


Пример базового класса:
class Person {
String name;
int age;

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

void introduce() {
System.out.println("Меня зовут " + name + ", мне " + age + " лет.");
}
}


Структурные элементы:
class: ключевое слово для объявления класса.
ClassName: имя класса.
{}: тело класса.

В одном .
java файле может быть несколько классов, но только один — public, и его имя должно совпадать с именем файла.


3. Поля класса

Поля определяют внутреннее состояние объектов и объявляются внутри класса, но вне методов или конструкторов.

Синтаксис:
Тип имяПоля;
Тип имяПоля = начальноеЗначение;


Пример:
class Car {
String model; // Поле экземпляра
int speed = 0; // Поле с начальным значением
static int totalCars = 0; // Статическое поле
}


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


Инициализация:
Поля экземпляра могут инициализироваться:

При объявлении;
В блоке инициализации;
В конструкторе.



4. Конструкторы

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

Синтаксис:
ClassName(параметры) {
// Инициализация объекта
}


Пример:
class Student {
String name;
int grade;

Student(String name, int grade) {
this.name = name;
this.grade = grade;
}

Student() {
this("Unknown", 0); // Вызов другого конструктора
}
}


Особенности:
Если ни один конструктор не определён, компилятор автоматически добавляет конструктор без параметров.
При наличии хотя бы одного конструктора — компилятор ничего не добавляет автоматически.


Ключевое слово this используется для:
обращения к полям экземпляра;
вызова другого конструктора внутри класса.


Пример использования this:
class Book {
String title;

Book(String title) {
this.title = title;
}
}



#Java #для_новичков #beginner #java_syntax #Class
5. Вложенные классы

Классы могут быть определены внутри других классов. Это удобно для логического объединения компонентов.

Виды вложенных классов:
Статический вложенный класс (static)
Внутренний класс (нестатический)
Локальный класс (внутри метода)
Анонимный класс (без имени, создаётся на лету)
Пример статического вложенного класса:


class Outer {
static class StaticNested {
void display() {
System.out.println("Static nested class");
}
}
}


Пример внутреннего класса:
class Outer {
class Inner {
void display() {
System.out.println("Inner class");
}
}
}


Создание экземпляров:
Outer.StaticNested nested = new Outer.StaticNested();

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();



6. Блоки инициализации

Блоки инициализации выполняются перед выполнением конструктора.

Существуют два вида:
Экземплярный блок - выполняется каждый раз при создании объекта, до конструктора.
class Example {
{
System.out.println("Экземплярный блок инициализации");
}
}



Статический блок
- выполняется один раз при загрузке класса в память (JVM).

class Example {
static {
System.out.println("Статический блок инициализации");
}
}


Порядок инициализации:
Статические поля → статические блоки (в порядке появления).
Поля экземпляра → блоки экземпляра → конструктор (в порядке появления в коде).



7. Дополнительные структурные аспекты

Несколько классов в одном файле:
class A {
// основной класс
}

class B {
// вспомогательный
}


Только один класс может быть public, и его имя должно совпадать с именем файла.

Структура .java файла:
package my.project;

import java.util.List;

class Example {
// тело класса
}


Каждый класс принадлежит пакету. Структура пакетов влияет на компиляцию, размещение файлов и импорт.


8. Лучшие практики по структуре

Инкапсуляция полей через геттеры/сеттеры — отдельная тема, но важно помнить, что прямой доступ к полям может быть нежелателен.
Минимизация дублирования кода в конструкторах — через this(...).
Инициализация по умолчанию — используется с осторожностью; рекомендуется явно задавать значения.



9. Типичные ошибки и подводные камни

Неинициализированные поля: хотя JVM задаёт значения по умолчанию (0, null, false), это может приводить к логическим ошибкам.
Статические поля: избыточное или некорректное использование может привести к ошибкам синхронизации и неправильному поведению.
Повторяющийся код в конструкторах: рекомендуется использовать цепочку вызовов this(...).



#Java #для_новичков #beginner #java_syntax #Class
Плагины и цели Maven

Плагины и цели (goals) являются ключевыми компонентами Maven, обеспечивающими выполнение конкретных задач в процессе сборки. Они интегрируются с жизненным циклом Maven, позволяя разработчикам компилировать код, запускать тесты, создавать артефакты и выполнять другие действия.


Что такое плагин и цель


Плагин в Maven — это модуль, содержащий набор функций для выполнения задач сборки, таких как компиляция, тестирование или развертывание. Каждый плагин состоит из одной или нескольких целей (goals), которые представляют собой конкретные действия, выполняемые плагином. Например, плагин maven-compiler-plugin имеет цели compile (компиляция исходного кода) и testCompile (компиляция тестового кода).

Цели привязываются к фазам жизненного цикла Maven (например, compile, test, package), что позволяет автоматически вызывать их в нужный момент сборки. Плагины и их цели загружаются в память как Java-объекты во время выполнения, что делает их гибкими, но требует внимания к управлению ресурсами.


Архитектура Maven Plugins

Плагины Maven построены на основе архитектуры, которая использует Plexus — контейнер инверсии управления (IoC), встроенный в Maven. Каждый плагин представляет собой JAR-файл, содержащий Java-классы, реализующие интерфейс org.apache.maven.plugin.Mojo.

Основные аспекты архитектуры:
Mojo (Maven plain Old Java Object): Основной строительный блок плагина. Mojo — это Java-класс, реализующий интерфейс Mojo, который определяет метод execute() для выполнения задачи. Каждый Mojo соответствует одной цели плагина. Например, в maven-compiler-plugin цель compile реализована классом CompilerMojo.
Plexus Container: Управляет жизненным циклом плагинов, загружая их классы и зависимости. Plexus создает изолированные классовые загрузчики (classloaders) для каждого плагина, чтобы избежать конфликтов зависимостей.
POM Model Integration: Плагины получают доступ к объектной модели проекта (POM) через API Maven, что позволяет им читать конфигурацию, зависимости и свойства проекта.
Descriptor File: Каждый плагин содержит файл META-INF/maven/plugin.xml, описывающий его цели, параметры и привязки к фазам жизненного цикла.


В памяти плагины загружаются как экземпляры Mojo, каждый из которых хранит свою конфигурацию и состояние. Это увеличивает потребление памяти, особенно при использовании множества плагинов или сложных конфигураций. Maven оптимизирует загрузку, кэшируя зависимости плагинов в локальном репозитории (~/.m2/repository), но интенсивные задачи, такие как компиляция, могут временно увеличивать использование оперативной памяти.

#Java #middle #Maven #Plugin #Goals
Встроенные плагины

Maven поставляется с набором встроенных плагинов, которые покрывают основные задачи сборки. Они определены в "super POM" — базовом POM-файле, который наследуется всеми проектами.

Рассмотрим ключевые встроенные плагины:

maven-compiler-plugin: Отвечает за компиляцию исходного и тестового кода. Цели:
compile: Компилирует исходный код в target/classes.
testCompile: Компилирует тестовый код в target/test-classes. В памяти плагин загружает исходные файлы, зависимости и настройки компилятора, создавая временные структуры данных для байт-кода.
maven-surefire-plugin: Выполняет модульные тесты. Основная цель:
test: Запускает тесты с использованием фреймворков, таких как JUnit или TestNG, и сохраняет результаты в target/surefire-reports. Плагин создает изолированную среду выполнения тестов, что требует дополнительной памяти для загрузки тестовых классов и их зависимостей.
maven-jar-plugin: Создает JAR-артефакты. Основная цель:
jar: Пакует скомпилированный код и ресурсы в JAR-файл в target. Плагин работает с файловой системой, минимизируя использование памяти, так как данные записываются непосредственно на диск.
maven-install-plugin: Устанавливает артефакты в локальный репозиторий. Цель:
install: Копирует артефакт в ~/.m2/repository. Плагин использует минимальный объем памяти, так как выполняет операции копирования файлов.
maven-deploy-plugin: Разворачивает артефакты в удаленный репозиторий. Цель:
deploy: Загружает артефакт в указанный репозиторий (например, Nexus или Artifactory). Плагин взаимодействует с сетью, что может замедлить выполнение, но не сильно нагружает память.
maven-site-plugin: Генерирует документацию проекта. Цели:
site: Создает HTML-документацию в target/site.
site-deploy: Разворачивает документацию на сервер. Плагин загружает в память шаблоны и данные проекта, что может быть ресурсоемким для крупных проектов.


Эти плагины автоматически привязаны к соответствующим фазам жизненного цикла (например, maven-compiler-plugin:compile к фазе compile), что определено в super POM. Их поведение можно переопределить или расширить в пользовательском POM-файле.


Конфигурация плагинов: <plugin>, <executions>, <configuration>


Конфигурация плагинов задается в POM-файле через элемент <plugin>.

Основные элементы конфигурации:
<plugin>: Определяет плагин, его groupId, artifactId и version.

Например:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
</plugin>


<configuration>: Задает параметры плагина, такие как версия Java или дополнительные опции.

Например:
```
<configuration>
<source>11</source>
<target>11</target>
<compilerArgs>
<arg>-Xlint:unchecked</arg>
</compilerArgs>
</configuration>

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

<executions>: Позволяет привязать цели плагина к конкретным фазам жизненного цикла или настроить многократное выполнение цели.

Например:
<executions>
<execution>
<id>custom-compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</execution>
</executions>

Каждая <execution> создает отдельный экземпляр Mojo в памяти, что может увеличить потребление ресурсов при сложных конфигурациях.


Конфигурация загружается в объектную модель POM во время инициализации проекта. Maven парсит XML, создавая структуры данных, которые хранятся в оперативной памяти. Для крупных проектов с множеством плагинов это может привести к значительному потреблению памяти, особенно если конфигурации содержат сложные параметры или большие списки зависимостей.

#Java #middle #Maven #Plugin #Goals
Наследование и управление версиями плагинов

Maven поддерживает наследование конфигураций плагинов через родительские POM-файлы.

В многомодульных проектах родительский POM может определять общие плагины и их версии в секции <pluginManagement>:

<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>


Дочерние модули наследуют эти настройки, но могут их переопределить. Это позволяет централизованно управлять версиями плагинов, минимизируя дублирование кода. В памяти Maven хранит только одну копию конфигурации из <pluginManagement>, которая копируется в дочерние модели POM при необходимости, оптимизируя использование ресурсов.

Управление версиями также осуществляется через локальный и удаленные репозитории. Maven загружает плагины из ~/.m2/repository или скачивает их из центрального репозитория (например, Maven Central). Кэширование плагинов снижает нагрузку на сеть, но требует места на диске.


Mojo (Maven plain Old Java Object)

Mojo — это Java-класс, реализующий интерфейс org.apache.maven.plugin.Mojo. Он определяет логику выполнения цели плагина.

Основные элементы Mojo:
Аннотации: Используются для указания имени цели, привязки к фазе и параметров.

Например:
```
@Mojo(name = "custom-goal", defaultPhase = LifecyclePhase.COMPILE)
public class CustomMojo extends AbstractMojo {
@Parameter(property = "customParam", defaultValue = "value")
private String customParam;

public void execute() throws MojoExecutionException {
getLog().info("Executing custom goal with param: " + customParam);
}
}

Аннотация @Parameter позволяет связать поле класса с параметром из POM-файла.
```

Метод execute(): Содержит логику выполнения цели. Может взаимодействовать с моделью POM, файловой системой и другими ресурсами.
Логирование: Используется метод getLog() для вывода сообщений в консоль Maven.


В памяти каждый Mojo создается как отдельный объект, связанный с конкретной целью и фазой. Plexus управляет жизненным циклом Mojo, создавая и уничтожая их по мере выполнения фаз. Это может привести к фрагментации памяти, если плагин выполняет ресурсоемкие операции, такие как обработка больших файлов.


Сторонние плагины

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

Примеры популярных сторонних плагинов:
findbugs-maven-plugin: Выполняет статический анализ кода для поиска ошибок.
maven-shade-plugin: Создает "uber-JAR", включая зависимости.
docker-maven-plugin: Интегрирует сборку Docker-образов в процесс Maven.


Сторонние плагины добавляются в POM-файл аналогично встроенным:
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.7.3.0</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>


Сторонние плагины загружаются из репозиториев, указанных в POM-файле, и могут увеличивать потребление памяти, особенно если они используют дополнительные библиотеки. Например, maven-shade-plugin загружает в память все зависимости артефакта для создания uber-JAR, что может быть ресурсоемким для крупных проектов.


#Java #middle #Maven #Plugin #Goals
Создание собственного плагина

Создание собственного плагина позволяет реализовать специфические задачи сборки.

Процесс включает следующие шаги:
Создание проекта Maven: Используйте архетип maven-archetype-mojo для генерации структуры:
mvn archetype:generate -DgroupId=com.example -DartifactId=custom-maven-plugin -DarchetypeArtifactId=maven-archetype-mojo

Реализация Mojo: Создайте Java-класс, реализующий AbstractMojo.

Например:
package com.example;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

@Mojo(name = "say-hello")
public class HelloMojo extends AbstractMojo {
@Parameter(property = "message", defaultValue = "Hello, Maven!")
private String message;

public void execute() throws MojoExecutionException {
getLog().info(message);
}
}


Конфигурация plugin.xml: Файл META-INF/maven/plugin.xml генерируется автоматически и содержит метаданные о целях и параметрах.

Сборка и установка:
mvn clean install


Использование плагина: Добавьте плагин в POM-файл другого проекта:
<plugin>
<groupId>com.example</groupId>
<artifactId>custom-maven-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>say-hello</goal>
</goals>
</execution>
</executions>
</plugin>


Создание плагина требует загрузки дополнительных зависимостей, таких как maven-plugin-api и maven-plugin-annotations, в память. Плагин компилируется как обычный Java-проект, а его цели становятся доступны для привязки к фазам жизненного цикла. Важно оптимизировать логику Mojo, чтобы минимизировать потребление памяти, особенно если плагин обрабатывает большие объемы данных.


Нюансы и внутренние механизмы

Управление памятью:
Плагины загружаются как отдельные JAR-файлы, каждый со своим классовым загрузчиком. Это изолирует зависимости, но увеличивает фрагментацию памяти.
Mojo-объекты создаются для каждой цели в рамках <execution>. Многократное выполнение цели увеличивает потребление памяти.
Для оптимизации используйте флаги JVM, такие как -Xmx, и минимизируйте количество одновременно выполняемых плагинов.


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

Кэширование:
Maven кэширует плагины и их зависимости в ~/.m2/repository, что снижает нагрузку на сеть. Однако при использовании устаревших версий плагинов могут возникнуть проблемы совместимости.

Параллельное выполнение:
В многомодульных проектах плагины могут выполняться параллельно с флагом -T. Это ускоряет сборку, но увеличивает пиковое потребление памяти из-за одновременной загрузки нескольких Mojo.

Отладка:
Для отладки плагинов используйте флаг -X или настройте логирование в Mojo через getLog(). Это помогает выявить проблемы, но увеличивает объем выводимых данных.


#Java #middle #Maven #Plugin #Goals
Методы в Java — синтаксис и структура

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


1. Что такое метод в Java?

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

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


2. Базовая структура метода

Метод объявляется внутри тела класса и состоит из заголовка (сигнатуры) и тела.

Вот общий синтаксис метода:
[модификаторы] тип_возврата имя_метода(параметры) [throws исключения] {
// Тело метода
}


Пример минимального метода:
class Calculator {
int calculateSum(int a, int b) {
return a + b;
}
}


Компоненты метода:
Модификаторы: Определяют поведение метода (например, static, final).
Тип возврата: Указывает, какой тип данных метод возвращает (или void, если ничего не возвращается).
Имя метода: Уникальное имя, описывающее действие метода.
Параметры: Список входных данных (может быть пустым).
Список исключений (throws): Указывает, какие проверяемые исключения метод может выбросить.

Тело метода: Код, выполняющий логику метода, заключенный в фигурные скобки {}.


3. Типы методов

Методы в Java делятся на два основных типа в зависимости от их принадлежности:

3.1. Экземплярные методы
Экземплярные методы принадлежат объекту класса и работают с его состоянием (полями экземпляра). Для их вызова требуется создание экземпляра класса.

Пример:
class Person {
String name;

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

void printGreeting() {
System.out.println("Меня зовут " + name);
}
}


Использование:
Person person = new Person("Алексей");
person.printGreeting(); // Вывод: Меня зовут Алексей


3.2. Статические методы
Статические методы принадлежат классу, а не объекту, и вызываются через имя класса. Они не имеют доступа к полям экземпляра напрямую, только к статическим полям.

Пример:
class MathUtils {
static double calculateSquare(double x) {
return x * x;
}
}


Использование:
double result = MathUtils.calculateSquare(5.0); // Вывод: 25.0



4. Параметры метода

Методы могут принимать параметры (аргументы), которые передаются при вызове. Параметры указываются в скобках в сигнатуре метода.

Синтаксис параметров:

тип_параметра имя_параметра [, тип_параметра имя_параметра ...]


Пример:
class Calculator {
int calculateProduct(int a, int b) {
return a * b;
}
}


Особенности параметров:
Передача по значению: В Java все параметры передаются по значению. Для примитивных типов передается копия значения, для объектов — копия ссылки. Это означает, что изменения объекта внутри метода видны снаружи, но переназначение ссылки не влияет на исходный объект.
Переменное число параметров (varargs): Метод может принимать переменное количество аргументов одного типа с использованием синтаксиса ....


Пример varargs:
class MathUtils {
int calculateSum(int... numbers) {
int total = 0;
for (int num : numbers) {
total += num;
}
return total;
}
}



#Java #для_новичков #beginner #java_syntax #Method
5. Возвращаемые значения

Методы могут возвращать значение определенного типа или ничего (void). Тип возврата указывается перед именем метода.

Пример:
class Example {
String getWelcomeMessage() {
return "Привет, мир!";
}

void printMessage() {
System.out.println("Сообщение без возврата");
}
}



Особенности возвращаемых значений:

Если метод имеет возвращаемый тип, он должен содержать оператор return с соответствующим значением.
Для void методов оператор return необязателен, но может использоваться для прерывания выполнения.
Метод может возвращать любой тип данных: примитивы (int, double), объекты, массивы или даже null для ссылочных типов.

Пример возврата массива:
class ArrayUtils {
int[] generateNumbers() {
return new int[] {1, 2, 3};
}
}



6. Перегрузка методов


Перегрузка методов (method overloading) позволяет определять несколько методов с одинаковым именем, но разными сигнатурами (списками параметров).

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


Пример:
class Printer {
void printMessage(String message) {
System.out.println(message);
}

void printNumber(int number) {
System.out.println("Число: " + number);
}

void printRepeatedMessage(String message, int times) {
for (int i = 0; i < times; i++) {
System.out.println(message);
}
}
}


Использование:
Printer printer = new Printer();
printer.printMessage("Привет"); // Вывод: Привет
printer.printNumber(42); // Вывод: Число: 42
printer.printRepeatedMessage("Повтор", 3); // Вывод: Повтор (3 раза)



7. Исключения в методах

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

Пример:
class FileReader {
void readFileContent(String path) throws IOException {
// Код, который может выбросить IOException
}
}


Особенности:
Проверяемые исключения (checked exceptions): Должны быть объявлены в throws или обработаны в блоке try-catch.
Непроверяемые исключения (unchecked exceptions): Не требуют явного объявления (например, RuntimeException).


8. Рекурсивные методы

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

Пример:
class Factorial {
int calculateFactorial(int n) {
if (n <= 1) {
return 1;
}
return n * calculateFactorial(n - 1);
}
}


Использование:
Factorial fact = new Factorial();
int result = fact.calculateFactorial(5); // Вывод: 120


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



#Java #для_новичков #beginner #java_syntax #Method
9. Правила именования методов

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

Соглашения об именовании:
Используйте camelCase: Имя метода начинается с маленькой буквы, каждое последующее слово начинается с заглавной (например, calculateSum, printMessage).
Глаголы для действий: Имя метода должно начинаться с глагола, описывающего выполняемое действие (например, get, set, calculate, print, find).
Описательность: Имя должно четко отражать назначение метода (например, calculateTotalPrice вместо calc).
Избегайте сокращений: Используйте полные слова вместо сокращений, чтобы избежать двусмысленности (например, computeAverage вместо compAvg).
Префиксы для геттеров и сеттеров: Для методов, возвращающих или устанавливающих значения полей, используйте префиксы get и set (например, getName, setSalary).
Префикс is для булевых методов: Для методов, возвращающих boolean, используйте префикс is или has (например, isEmpty, hasAccess).


Примеры правильного и неправильного именования:
class Example {
// Правильно: описывает действие, использует camelCase
String getUserName() {
return "Алексей";
}

// Неправильно: не описывает действие, использует сокращение
String user() {
return "Алексей";
}

// Правильно: использует is для булевого значения
boolean isActive() {
return true;
}

// Неправильно: не соответствует соглашению для булевых методов
boolean active() {
return true;
}
}


Советы по именованию:

Согласованность: Следуйте одному стилю именования во всем проекте.
Избегайте избыточности: Не добавляйте лишние слова, такие как do или perform, если они не уточняют смысл (например, calculateSum вместо doCalculateSum).
Учитывайте контекст: Имя метода должно быть понятно в контексте класса (например, в классе Order метод calculateTotal очевиден без уточнения calculateOrderTotal).



10. Методы и память в Java

Понимание того, как методы работают в памяти, важно для написания эффективного кода и избежания ошибок.

10.1. Стек вызовов (Call Stack)
Каждый раз, когда метод вызывается, JVM создает новый фрейм в стеке вызовов (call stack).

Фрейм стека содержит:
Локальные переменные метода.
Параметры метода.
Возвращаемый адрес (место, куда вернется управление после завершения метода).
При завершении метода его фрейм удаляется из стека, освобождая память.
Рекурсивные методы увеличивают глубину стека, что может привести к StackOverflowError при чрезмерной глубине.

Пример:
class StackExample {
void methodA() {
methodB();
}

void methodB() {
System.out.println("В методе B");
}
}


Вызов methodA создает фрейм в стеке.
Вызов methodB из methodA добавляет новый фрейм поверх фрейма methodA.
После завершения methodB его фрейм удаляется, и управление возвращается к methodA.


10.2. Статические методы и память
Статические методы хранятся в области памяти, называемой Metaspace (в Java 8 и выше), вместе с метаданными класса.
Они не привязаны к объектам, поэтому не требуют создания экземпляра класса и не используют память кучи для хранения состояния объекта.
Статические методы имеют доступ только к статическим полям, которые также хранятся в Metaspace.



#Java #для_новичков #beginner #java_syntax #Method
10.3. Экземплярные методы и память
Экземплярные методы также хранятся в Metaspace, но при вызове они работают с конкретным объектом, который находится в куче (Heap).
Каждый объект в куче содержит ссылку на таблицу методов своего класса, что позволяет вызывать экземплярные методы.
Локальные переменные и параметры метода хранятся в стеке вызовов, а поля объекта — в куче.


10.4. Передача параметров и память
Примитивные типы: Передаются по значению, копия значения сохраняется в стеке вызова метода.
Ссылочные типы: Передается копия ссылки, указывающая на объект в куче. Изменения объекта видны снаружи, но переназначение ссылки внутри метода не влияет на исходную ссылку.


Пример:
class MemoryExample {
void modifyObject(StringBuilder sb, int value) {
sb.append("Изменено"); // Изменяет объект в куче
value = 100; // Изменяет локальную копию, не влияет на исходное значение
}
}


Использование:
StringBuilder sb = new StringBuilder("Привет");
int value = 42;
MemoryExample example = new MemoryExample();
example.modifyObject(sb, value);
System.out.println(sb); // Вывод: ПриветИзменено
System.out.println(value); // Вывод: 42


10.5. Оптимизация памяти
Избегайте глубоких рекурсий: Используйте итеративные подходы для задач, требующих больших вычислений.
Минимизируйте локальные переменные: Используйте только необходимые переменные, чтобы сократить использование стека.
Осторожно с varargs: Передача большого количества аргументов через varargs создает массив в куче, что может увеличить потребление памяти.



11. Лучшие практики


Четкие имена методов: Следуйте соглашениям об именовании, чтобы код был читаемым и понятным.
Ограничение длины метода: Методы должны быть короткими и выполнять одну задачу.
Использование перегрузки: Перегружайте методы только тогда, когда это логически оправдано.
Обработка исключений: Обрабатывайте исключения или явно объявляйте их в сигнатуре метода.
Документация: Используйте Javadoc для описания назначения метода, параметров и возвращаемого значения.


Пример Javadoc:
/**
* Вычисляет сумму двух чисел.
* @param firstNumber Первое число
* @param secondNumber Второе число
* @return Сумма чисел
*/
int calculateSum(int firstNumber, int secondNumber) {
return firstNumber + secondNumber;
}



12. Ошибки и подводные камни

Неправильная перегрузка:
Перегруженные методы с неоднозначными сигнатурами могут вызвать ошибки компиляции.
Игнорирование возвращаемого значения: Если метод возвращает значение, его нужно либо использовать, либо явно игнорировать.
Длинные методы: Слишком длинные методы трудно читать и поддерживать. Разбивайте их на меньшие подзадачи.
Необработанные исключения: Проверяемые исключения должны быть либо обработаны, либо объявлены.
Проблемы с памятью: Глубокая рекурсия или чрезмерное использование varargs может привести к переполнению стека или кучи.



#Java #для_новичков #beginner #java_syntax #Method
Управление зависимостями в Maven

Управление зависимостями — одна из ключевых возможностей Maven, которая позволяет автоматизировать загрузку, разрешение и использование библиотек в проекте. Maven обеспечивает централизованное управление зависимостями через локальный и удаленные репозитории, а также предоставляет механизмы для работы с транзитивными зависимостями, разрешения конфликтов и настройки специфичных сценариев.


Механизм разрешения зависимостей


Maven использует декларативный подход к управлению зависимостями, которые определяются в файле POM.xml в секции <dependencies>.


Каждая зависимость указывается через три ключевых атрибута:
groupId: Уникальный идентификатор организации или проекта.
artifactId: Имя артефакта.
version: Версия артефакта.


Пример:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.20</version>
</dependency>


Когда Maven выполняет сборку, он разрешает зависимости следующим образом:
Загрузка POM-модели: Maven парсит POM.xml, создавая объектную модель проекта (POM model) в оперативной памяти. Эта модель включает информацию о зависимостях, репозиториях и настройках.
Поиск зависимостей: Maven сначала проверяет локальный репозиторий (~/.m2/repository). Если зависимость отсутствует, он обращается к удаленным репозиториям, указанным в <repositories> или унаследованным из super POM (по умолчанию Maven Central).
Загрузка артефактов: Maven скачивает JAR-файлы и их POM-файлы, сохраняя их в локальном репозитории. В памяти создаются структуры данных, представляющие зависимости, включая их метаданные (groupId, artifactId, version).
Разрешение транзитивных зависимостей: Maven анализирует POM-файлы загруженных зависимостей, чтобы определить их собственные зависимости (см. ниже).


В памяти Maven хранит граф зависимостей — направленный ациклический граф (DAG), где узлы представляют зависимости, а ребра — их взаимосвязи. Этот граф используется для определения порядка загрузки и разрешения конфликтов. Размер графа зависит от количества зависимостей и их транзитивных связей, что может значительно увеличивать потребление памяти в крупных проектах.


Транзитивные зависимости

Транзитивные зависимости — это зависимости, которые требуются другим зависимостям. Например, если проект зависит от spring-core, а spring-core требует commons-logging, то commons-logging становится транзитивной зависимостью.


Maven автоматически включает транзитивные зависимости в сборку, что упрощает управление, но может привести к конфликтам или ненужным библиотекам.

Процесс разрешения транзитивных зависимостей:
Maven загружает POM-файл каждой зависимости и рекурсивно анализирует их <dependencies>.
Все найденные зависимости добавляются в граф зависимостей, который хранится в памяти.
Maven применяет правила разрешения конфликтов (см. ниже) для выбора подходящих версий.


Транзитивные зависимости увеличивают объем данных в памяти, так как Maven должен загрузить и обработать все связанные POM-файлы. Для оптимизации Maven кэширует зависимости в локальном репозитории, но при первом разрешении или при использовании флага --update-snapshots может происходить интенсивная сетевая активность.


Dependency Mediation и Nearest-Wins Strategy

Когда разные зависимости требуют одну и ту же библиотеку, но с разными версиями, возникает конфликт.

Maven использует стратегию dependency mediation с правилом nearest-wins (ближайший побеждает):
Nearest-wins strategy: Maven выбирает версию зависимости, которая находится ближе к корню графа зависимостей (т.е. имеет меньшую глубину в цепочке транзитивных зависимостей).
Пример: Если проект напрямую зависит от commons-logging:1.2, а другая зависимость требует commons-logging:1.1, то Maven выберет 1.2, так как она указана в корневом POM-файле (глубина 1). Если обе версии находятся на одинаковой глубине, Maven выберет ту, что встретилась первой в порядке парсинга.


#Java #middle #Maven #Dependencies
В памяти Maven строит граф зависимостей, где каждая версионная коллизия разрешается путем выбора ближайшей версии. Это требует хранения временных структур данных для сравнения версий, что может быть ресурсоемким для проектов с большим количеством зависимостей.

Для явного контроля версий можно использовать секцию <dependencyManagement> в POM-файле:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
</dependencyManagement>


Зависимости, указанные в <dependencyManagement>, имеют приоритет над транзитивными, что позволяет избежать конфликтов. Эти данные загружаются в память как часть POM-модели и применяются во время разрешения графа.


Dependency Convergence

Dependency convergence — это концепция, которая требует, чтобы в графе зависимостей использовалась только одна версия каждой библиотеки. Нарушение конвергенции (например, использование двух версий одной библиотеки) может привести к ошибкам, таким как ClassNotFoundException или несовместимость API.


Для обеспечения конвергенции используется плагин maven-enforcer-plugin с правилом dependencyConvergence:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<id>enforce</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<dependencyConvergence/>
</rules>
</configuration>
</execution>
</executions>
</plugin>


Если плагин обнаруживает конфликт версий, сборка завершается с ошибкой. В памяти maven-enforcer-plugin загружает полный граф зависимостей для анализа, что может значительно увеличить потребление ресурсов в крупных проектах.


Optional Dependencies

Опциональные зависимости (<optional>true</optional>) — это зависимости, которые не включаются в транзитивный граф проектов, использующих данный артефакт. Они полезны, когда библиотека предоставляет дополнительные функции, которые не требуются всем потребителям.


Пример:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
<optional>true</optional>
</dependency>


Если проект A включает slf4j-api как опциональную зависимость, то проект B, зависящий от A, не унаследует slf4j-api, пока не объявит его явно. Это уменьшает размер графа зависимостей, снижая потребление памяти и вероятность конфликтов.

В памяти Maven отмечает опциональные зависимости в POM-модели, исключая их из транзитивного разрешения, что оптимизирует обработку графа.


BOM (Bill of Materials)

BOM-файл — это специальный POM-файл, который определяет версии зависимостей для согласованного управления ими в проекте или группе проектов. BOM используется через <dependencyManagement> с областью видимости (scope) import.

Пример использования BOM от Spring Boot:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.7.18</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>


BOM-файл загружается в память как часть POM-модели и предоставляет централизованный список версий, которые применяются ко всем зависимостям в проекте. Это упрощает управление версиями и обеспечивает согласованность, особенно в многомодульных проектах. В памяти BOM увеличивает объем данных, так как Maven должен загрузить и обработать дополнительный POM-файл.

#Java #middle #Maven #Dependencies
Использование import scope

Область видимости import используется исключительно в <dependencyManagement> для импорта BOM-файлов. Она позволяет включить конфигурацию зависимостей из внешнего POM-файла, не добавляя сам артефакт в сборку.

Пример:

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>custom-bom</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>


В памяти Maven загружает импортированный POM-файл как часть модели проекта, добавляя его зависимости в структуру <dependencyManagement>. Это увеличивает потребление памяти, но упрощает управление версиями, так как все модули проекта используют единый набор версий.



Dependency Tree (mvn dependency:tree) и анализ конфликтов

Плагин maven-dependency-plugin с целью tree позволяет визуализировать граф зависимостей:
mvn dependency:tree


Пример вывода:
[INFO] com.example:my-project:jar:1.0-SNAPSHOT
[INFO] +- org.springframework:spring-core:jar:5.3.20:compile
[INFO] | \- commons-logging:commons-logging:jar:1.2:compile
[INFO] \- org.junit.jupiter:junit-jupiter:jar:5.9.2:test


Команда dependency:tree загружает в память полный граф зависимостей, включая транзитивные зависимости, и выводит его в консоль. Это полезно для анализа конфликтов и выявления нежелательных зависимостей. Для более детального анализа можно использовать флаг -Dverbose для отображения исключенных или конфликтующих версий.


Для разрешения конфликтов можно:
Исключить зависимости:
```
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.20</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>

Исключения уменьшают размер графа зависимостей, снижая потребление памяти.
Явно указать версию в <dependencyManagement> для принудительного выбора версии.

```

Использовать dependency:analyze для выявления неиспользуемых или необъявленных зависимостей:
mvn dependency:analyze


В памяти dependency:tree создает временные структуры для хранения графа, что может быть ресурсоемким для проектов с сотнями зависимостей. Оптимизация, такая как использование <exclusions> или BOM, помогает сократить объем данных.


Нюансы и внутренние механизмы

Управление памятью:
Граф зависимостей хранится в памяти как DAG, где каждый узел представляет артефакт, а ребра — зависимости. Размер графа пропорционален количеству зависимостей.
Maven использует Aether (библиотеку для работы с репозиториями), которая загружает метаданные зависимостей в память. Это может привести к пиковому потреблению памяти при первом разрешении.
Для оптимизации используйте флаг -o (offline) для работы с локальным кэшем или настройте JVM с помощью -Xmx.


Кэширование:
Локальный репозиторий (~/.m2/repository) кэширует JAR-файлы, POM-файлы и метаданные, что снижает сетевую нагрузку.
Maven хранит метаданные о версиях в файлах _remote.repositories, что ускоряет повторное разрешение.


Конфликты и Classpath:
Неправильное разрешение зависимостей может привести к включению двух версий одной библиотеки в classpath, вызывая ошибки вроде NoClassDefFoundError.
Используйте dependency:tree и maven-enforcer-plugin для предотвращения таких проблем.


Параллельное разрешение:
В многомодульных проектах Maven разрешает зависимости для каждого модуля отдельно, но кэширует результаты в памяти. Параллельное выполнение (-T) увеличивает пиковое потребление памяти из-за одновременной обработки нескольких графов.

Сетевые проблемы:
Если удаленный репозиторий недоступен, Maven может завершиться с ошибкой. Настройка зеркал в settings.xml или использование --offline помогает избежать этого.


#Java #middle #Maven #Dependencies
Идентификаторы доступа в Java

Идентификаторы доступа (access modifiers) в Java определяют область видимости и доступ к классам, полям, конструкторам и методам. Они являются важной частью инкапсуляции, одного из ключевых принципов объектно-ориентированного программирования.


1. Что такое идентификаторы доступа в Java?

Идентификаторы доступа — это ключевые слова, которые управляют видимостью и доступом к элементам программы (классам, полям, конструкторам, методам) в Java. Они позволяют разработчикам контролировать, какие части кода могут обращаться к определенным компонентам, обеспечивая безопасность, инкапсуляцию и модульность.

В Java существуют четыре уровня доступа:
public
protected
package-private (по умолчанию, если модификатор не указан)
private


Эти модификаторы применяются к классам, полям, конструкторам и методам, определяя их доступность в различных контекстах.


2. Синтаксис идентификаторов доступа

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

Общий синтаксис:
[идентификатор_доступа] тип_элемента имя_элемента;


Пример:
public class Example {
private int privateField;
protected String protectedField;
int packagePrivateField; // package-private (по умолчанию)
public void publicMethod() {
// Код метода
}
}


Компоненты идентификаторов доступа:
Ключевое слово модификатора: public, protected, private или отсутствие модификатора (package-private).
Элемент программы: Может быть классом, полем, конструктором или методом.
Положение: Модификатор указывается перед типом элемента (или перед ключевым словом class для классов).



3. Типы идентификаторов доступа

Каждый идентификатор доступа определяет уровень видимости элемента.

3.1. public
Описание: Элемент доступен из любого места в программе, где доступен его класс.
Применение: Используется для классов, методов и полей, которые должны быть доступны всем частям программы, включая внешние пакеты.


Пример:
public class PublicClass {
public int publicField = 42;

public void publicMethod() {
System.out.println("Публичный метод");
}
}


Использование
PublicClass obj = new PublicClass();
System.out.println(obj.publicField); // Доступно
obj.publicMethod(); // Доступно


3.2. protected
Описание: Элемент доступен в пределах своего пакета и в подклассах, даже если они находятся в других пакетах.
Применение: Используется для полей и методов, которые должны быть доступны в подклассах, но не для внешнего кода вне пакета.


Пример:
class BaseClass {
protected String protectedField = "Защищенное поле";

protected void protectedMethod() {
System.out.println("Защищенный метод");
}
}


Использование:
// В том же пакете
BaseClass obj = new BaseClass();
System.out.println(obj.protectedField); // Доступно
obj.protectedMethod(); // Доступно

// В подклассе в другом пакете
class SubClass extends BaseClass {
void accessProtected() {
System.out.println(protectedField); // Доступно через наследование
}
}


3.3. package-private (по умолчанию)
Описание: Если модификатор доступа не указан, элемент доступен только в пределах своего пакета.
Применение: Используется для ограничения доступа к классам, полям или методам внутри одного пакета, обеспечивая модульность.


Пример:
class PackagePrivateClass {
int packagePrivateField = 100;

void packagePrivateMethod() {
System.out.println("Метод с доступом по умолчанию");
}
}


Использование:

// В том же пакете
PackagePrivateClass obj = new PackagePrivateClass();
System.out.println(obj.packagePrivateField); // Доступно
obj.packagePrivateMethod(); // Доступно

// В другом пакете
// Ошибка компиляции: PackagePrivateClass не виден


#Java #для_новичков #beginner #java_syntax #Access_modifiers
3.4. private
Описание: Элемент доступен только внутри своего класса.
Применение: Используется для полной инкапсуляции, чтобы скрыть внутренние детали реализации от внешнего кода.


Пример
:
class PrivateExample {
private int privateField = 10;

private void privateMethod() {
System.out.println("Приватный метод");
}

public void accessPrivate() {
System.out.println(privateField); // Доступно внутри класса
privateMethod(); // Доступно внутри класса
}
}


Использование:
PrivateExample obj = new PrivateExample();
// System.out.println(obj.privateField); // Ошибка компиляции
// obj.privateMethod(); // Ошибка компиляции
obj.accessPrivate(); // Доступно



4. Применение к различным элементам программы

4.1. Классы
Ограничения: Классы верхнего уровня могут быть только public или package-private. Внутренние (nested) и вложенные (inner) классы могут использовать все модификаторы.

Пример:
public class OuterClass {
private class InnerClass {
// Приватный внутренний класс
}
}


4.2. Поля
Поля часто делают private для инкапсуляции, предоставляя доступ через геттеры и сеттеры.
protected используется для полей, которые должны быть доступны в подклассах.
public поля редки, так как нарушают инкапсуляцию.


4.3. Конструкторы
private конструкторы используются в шаблонах, таких как Singleton.
public конструкторы применяются для создания объектов из внешнего кода.
protected конструкторы ограничивают создание объектов подклассами.


Пример Singleton:
class Singleton {
private static final Singleton INSTANCE = new Singleton();

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

public static Singleton getInstance() {
return INSTANCE;
}
}


4.4. Методы
private методы скрывают внутреннюю логику класса.
protected методы предоставляют доступ подклассам.
public методы формируют публичный API класса.



5. Правильное применение идентификаторов доступа

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

Вот рекомендации по их применению:

5.1. Принципы инкапсуляции
Минимизируйте доступ: Используйте наиболее строгий модификатор, который позволяет реализовать функциональность. Например, предпочтите private вместо public, если доступ не требуется извне.
Скрывайте детали реализации: Поля и методы, не предназначенные для внешнего использования, должны быть private.
Используйте геттеры и сеттеры: Для доступа к private полям предоставляйте public или protected методы-геттеры/сеттеры.


Пример:
class Employee {
private String name;
private double salary;

public String getName() {
return name;
}

public void setSalary(double salary) {
if (salary >= 0) {
this.salary = salary;
}
}
}


5.2. Модульность и пакеты
Используйте package-private для классов и методов, которые должны быть доступны только внутри пакета, чтобы ограничить их использование другими частями программы.
Организуйте код в пакеты так, чтобы логически связанные классы находились в одном пакете, минимизируя необходимость public доступа.


5.3. Наследование
Используйте protected для полей и методов, которые должны быть доступны в подклассах, но не для внешнего кода.
Избегайте чрезмерного использования protected, так как это может нарушить инкапсуляцию.


5.4. Публичный API
Делайте public только те классы, методы и конструкторы, которые предназначены для использования внешними клиентами (например, в библиотеках или API).
Убедитесь, что публичные методы хорошо задокументированы и стабильны, чтобы избежать проблем при изменении реализации.


5.5. Шаблоны проектирования
Singleton: Используйте private конструктор и public static метод для доступа к единственному экземпляру.
Фабричные методы: Часто используют protected или package-private конструкторы, чтобы ограничить создание объектов.
Инкапсуляция данных: Поля всегда должны быть private, с доступом через методы.



#Java #для_новичков #beginner #java_syntax #Access_modifiers
6. Идентификаторы доступа и память

Понимание того, как идентификаторы доступа влияют на управление памятью в Java, помогает оптимизировать производительность и предотвращать ошибки.

6.1. Хранение метаданных

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


6.2. Проверка доступа в JVM
Когда код обращается к полю или методу, JVM проверяет модификатор доступа. Эта проверка происходит на этапе загрузки класса и выполнения байт-кода.
Проверка доступа не создает значительных накладных расходов, так как она выполняется на уровне байт-кода и оптимизирована JIT-компилятором.


6.3. Влияние на объекты в куче
Идентификаторы доступа не влияют напрямую на размер объектов в куче (Heap). Например, private и public поля занимают одинаковое количество памяти, так как модификаторы хранятся в метаданных класса, а не в самом объекте.
Однако неправильное использование доступа (например, избыточное использование public полей) может привести к нежелательным изменениям объектов в куче, что усложняет управление состоянием.

Пример:
class MemoryExample {
private int privateField = 10;
public int publicField = 20;

void accessFields() {
privateField = 30; // Доступно внутри класса
publicField = 40; // Доступно везде
}
}

Поля privateField и publicField занимают одинаковое место в куче (4 байта для int), но private ограничивает доступ, защищая целостность объекта.


6.4. Статические элементы и память
Статические поля и методы, независимо от их модификатора доступа, хранятся в Metaspace и не привязаны к объектам в куче.
private static поля защищают общие данные класса от внешнего доступа, что важно для предотвращения непреднамеренных изменений в многопоточных приложениях.


Пример:
class Counter {
private static int count = 0;

public static void increment() {
count++;
}
}

Поле count хранится в Metaspace, а private модификатор гарантирует, что доступ возможен только через метод increment.


6.5. Оптимизация памяти

Минимизация публичного доступа: Сокращение числа public полей и методов уменьшает вероятность ошибок, связанных с неправильным управлением состоянием объектов в куче.
Использование private для инкапсуляции: Это предотвращает несанкционированный доступ к данным, что особенно важно в многопоточных приложениях, где состояние объекта может быть изменено несколькими потоками.
Кэширование проверок доступа: JVM кэширует результаты проверок доступа в JIT-компиляторе, минимизируя накладные расходы на проверку модификаторов во время выполнения.


6.6. Ошибки, связанные с памятью
Утечки памяти: Неправильное использование public или protected полей может привести к тому, что внешний код сохраняет ссылки на объекты, препятствуя их сборке мусора.
Нарушение инкапсуляции: Если внутренние поля класса доступны через public, это может привести к неожиданным изменениям состояния объекта, что усложняет отладку и увеличивает риск ошибок в куче.



#Java #для_новичков #beginner #java_syntax #Access_modifiers