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

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

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
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
Что выведет код?

public class Task020725 {

static void modify(int x) {
x = x + 10;
}

public static void main(String[] args) {
int num = 5;
modify(num);
System.out.println(num);
}
}


#Tasks
Варианты ответа:
Anonymous Quiz
0%
0
71%
5
19%
15
10%
Ошибка компиляции
Продолжаем выбирать темы для разбора и голосовать за рассмотрение предложенных! 🤓

Голосуем за тему к рассмотрению в эти выходные!

Выбираем новую тему!
(можете предложить что-то из того, что предлагали на прошлой и позапрошлых неделях и что проиграло в голосовании!)

Не стесняемся! ✌️
Please open Telegram to view this post
VIEW IN TELEGRAM
Что такое паттерн Factory Method? 🤓

Ответ:

Factory Method — паттерн проектирования, где метод создает объекты подклассов, скрывая детали их создания. Определяется в абстрактном классе или интерфейсе, а подклассы реализуют логику.

Пример:
interface Shape {
static Shape create(String type) {
return switch (type) {
case "circle" -> new Circle();
case "square" -> new Square();
default -> throw new IllegalArgumentException();
};
}
}

class Circle implements Shape {}
class Square implements Shape {}

Позволяет легко расширять код новыми типами объектов.

#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Управление зависимостями в 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
Что выведет код?

public class Task030725 {
private static int counter = 0;

public static void main(String[] args) throws InterruptedException {
Runnable task = () -> {
for (int i = 0; i < 1_000_000; i++) {
counter++;
}
};

Thread t1 = new Thread(task);
Thread t2 = new Thread(task);

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

t1.join();
t2.join();

System.out.println(counter);
}
}


#Tasks
Что такое паттерн Builder и зачем он нужен? 🤓

Ответ:

Builder — паттерн для пошагового создания сложных объектов с множеством параметров.
Упрощает создание объектов и улучшает читаемость.

Пример:
class User {
private String name;
private int age;
private User(Builder builder) {
this.name = builder.name;
this.age = builder.age;
}
static class Builder {
private String name;
private int age;
Builder setName(String name) {
this.name = name; return this; }
Builder setAge(int age) { this.age = age; return this; }
User build() { return new User(this); }
}
}
User user = new User.Builder().setName("Alice").setAge(25).build();

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

#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM