Java for Beginner
717 subscribers
650 photos
173 videos
12 files
1.02K links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

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

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Сценарии и практики разделения: Core/Api/Impl

Разделение проекта на модули (Core, Api, Impl) — распространенная практика в корпоративной разработке для обеспечения модульности и повторного использования.

Core:
Содержит общую бизнес-логику, утилиты, модели данных.

Пример:
module-core/build.gradle:plugins {
id 'java-library'
}
dependencies {
implementation 'org.apache.commons:commons-lang3:3.12.0'
}


Api:
Определяет публичные интерфейсы, DTO или контракты.

Пример:
module-api/build.gradle:plugins {
id 'java-library'
}
dependencies {
api project(':module-core')
}


Impl:
Реализует интерфейсы из Api, добавляя конкретную функциональность.

Пример: module-impl/build.gradle:
plugins {
id 'java'
}
dependencies {
implementation project(':module-api')
}


Структура:
my-project/
├── module-core/
│ └── build.gradle
├── module-api/
│ └── build.gradle
├── module-impl/
│ └── build.gradle
├── build.gradle
├── settings.gradle


Практики:
Используйте api в модуле module-api для экспорта публичных интерфейсов.
Минимизируйте зависимости между модулями, чтобы избежать циклических зависимостей.
Централизуйте версии через Version Catalog или dependencyManagement в корневом build.gradle.


В памяти: Каждый модуль добавляет объект Project, задачи и зависимости в модель Gradle, увеличивая потребление памяти. Разделение на Core/Api/Impl уменьшает размер каждого модуля, но увеличивает общее количество объектов в памяти.



Gradle Composite Builds

Composite Builds позволяют включать другие Gradle-проекты как зависимости, без необходимости публикации в репозиторий.

Настройка:
В settings.gradle корневого проекта:
includeBuild '../other-project'


В build.gradle используйте проект как зависимость:
dependencies {
implementation project(':other-project:module-x')
}


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


В памяти: Composite Builds загружают модели всех включенных проектов в память, значительно увеличивая overhead (100-500 МБ на проект). Используйте с осторожностью для крупных систем.


Процесс сборки с помощью task-graph (Task Avoidance, Parallel Build)

Gradle строит граф задач (Directed Acyclic Graph, DAG) для определения порядка выполнения задач в многомодульных проектах.

Task Avoidance:

Gradle использует инкрементальную сборку, пропуская задачи, чьи входные/выходные данные не изменились (up-to-date checks).

Пример:
tasks.named('compileJava') {
inputs.files('src/main/java')
outputs.dir('build/classes/java/main')
}

В памяти: Gradle хранит хэши входов/выходов в памяти и ~/.gradle/caches, добавляя небольшой overhead (10-50 МБ).


Parallel Build:
Gradle поддерживает параллельное выполнение задач с флагом --parallel:
./gradlew build --parallel


#Java #middle #Gradle #Task #include_projects
August 4
Раздел 4: Управляющие конструкции

Условные операторы. if / else в Java

Условные операторы if и else в
Java позволяют выполнять разные части кода в зависимости от определенных условий. Они являются основой для управления потоком программы, позволяя принимать решения на основе значений переменных или выражений.

1. Что такое if и else в Java?

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

Зачем нужны if и else?
Принятие решений: Например, проверять, достаточно ли у пользователя денег для покупки.
Управление потоком: Позволяют программе выполнять разные действия в разных случаях.
Читаемость: Делают код понятным, показывая, какие действия зависят от условий.
Гибкость: Позволяют писать программы, которые реагируют на разные входные данные.



2. Синтаксис if и else

if проверяет условие, и если оно истинно (true), выполняется блок кода. else указывает, что делать, если условие ложно (false). Условие — это выражение, которое возвращает boolean (true или false).

Общий синтаксис:
if (условие) {
// Код, который выполняется, если условие истинно
} else {
// Код, который выполняется, если условие ложно
}


Простой пример:
int age = 18;
if (age >= 18) {
System.out.println("Вы взрослый!");
} else {
System.out.println("Вы несовершеннолетний!");
}

Вывод: Вы взрослый!


Вариации синтаксиса:
Простой if (без else):
if (age >= 18) {
System.out.println("Вы можете голосовать!");
}


Многоуровневый if-else (else if):

int score = 85;
if (score >= 90) {
System.out.println("Отлично!");
} else if (score >= 70) {
System.out.println("Хорошо!");
} else {
System.out.println("Попробуйте еще раз!");
}

Вывод: Хорошо!


Однострочный if (без фигурных скобок, если только одна команда):
if (age >= 18) System.out.println("Вы взрослый!");


Примечания к синтаксису:
Условие в скобках должно возвращать boolean (true или false).
Фигурные скобки {} обязательны, если блок кода содержит больше одной строки.
else и else if необязательны.



3. Типы конструкций if / else

3.1. Простой if
Используется, когда нужно выполнить код только при истинном условии.

Пример:
int temperature = 25;
if (temperature > 20) {
System.out.println("На улице тепло!");
}

Вывод: На улице тепло!


3.2. if с else
Выполняет один блок кода, если условие истинно, и другой — если ложно.

Пример:
int number = 10;
if (number % 2 == 0) {
System.out.println("Число четное!");
} else {
System.out.println("Число нечетное!");
}

Вывод: Число четное!


3.3. if с else if
Позволяет проверять несколько условий последовательно.

Пример:
int grade = 75;
if (grade >= 90) {
System.out.println("Оценка: A");
} else if (grade >= 80) {
System.out.println("Оценка: B");
} else if (grade >= 70) {
System.out.println("Оценка: C");
} else {
System.out.println("Оценка: D");
}

Вывод: Оценка: C


3.4. Вложенные if
if внутри другого if для более сложных проверок.

Пример:
int age = 20;
boolean hasID = true;
if (age >= 18) {
if (hasID) {
System.out.println("Можно войти!");
} else {
System.out.println("Нужен документ!");
}
} else {
System.out.println("Слишком молод!");
}

Вывод: Можно войти!



4. Правильное применение if / else

Чтобы писать понятный и эффективный код с if и else, следуйте этим рекомендациям:

4.1. Простота условий
Пишите простые и понятные условия. Разбивайте сложные выражения на переменные.

Пример:
// Плохо: сложное условие
if (score >= 70 && score <= 100 && isExamPassed) {
System.out.println("Экзамен сдан!");
}

// Хорошо: разбиваем на части
boolean isValidScore = score >= 70 && score <= 100;
if (isValidScore && isExamPassed) {
System.out.println("Экзамен сдан!");
}



#Java #для_новичков #beginner #if #else
August 5
4.2. Избегайте лишних условий
Не проверяйте условия, которые можно упростить или убрать.

Пример:
// Плохо: избыточное условие
if (isActive == true) {
System.out.println("Активен!");
}

// Хорошо: упрощение
if (isActive) {
System.out.println("Активен!");
}



4.3. Используйте else if для взаимоисключающих условий

Если условия взаимосвязаны, используйте else if, чтобы не проверять лишние условия.

Пример:
int x = 5;
if (x > 0) {
System.out.println("Положительное");
} else if (x < 0) {
System.out.println("Отрицательное");
} else {
System.out.println("Ноль");
}


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

Пример:
// Плохо: глубокая вложенность
if (age >= 18) {
if (hasTicket) {
if (isVenueOpen) {
System.out.println("Вход разрешен!");
}
}
}

// Хорошо: упрощение
boolean canEnter = age >= 18 && hasTicket && isVenueOpen;
if (canEnter) {
System.out.println("Вход разрешен!");
}


4.5. Проверяйте на null
При работе с объектами всегда проверяйте на null, чтобы избежать NullPointerException.

Пример:
String name = null;
if (name != null) {
System.out.println("Имя: " + name);
} else {
System.out.println("Имя не задано!");
}



5. Назначение if / else

if и else выполняют важные функции в программировании:

5.1. Управление потоком
Позволяют программе выбирать, какой код выполнять, в зависимости от условий.

5.2. Обработка разных случаев
Помогают обрабатывать разные сценарии, например, успех или ошибку.


5.3. Улучшение читаемости
Делают логику программы понятной, показывая, как данные влияют на поведение.

5.4. Гибкость
Позволяют писать код, который адаптируется к разным входным данным.


6. Работа if / else под капотом

Понимание, как if и else работают в JVM, поможет писать более эффективный код.

6.1. Компиляция в байт-код

Компилятор Java (javac) переводит if и else в инструкции условного перехода в байт-коде.
Условие в if преобразуется в сравнение (например, if_icmpgt для сравнения чисел), а затем JVM решает, какой блок кода выполнить.


Пример:
int x = 5;
if (x > 0) {
System.out.println("Положительное");
}


Байт-код (упрощенно):

iload x        // Загружаем x в стек
ifgt label // Если x > 0, перейти к метке
return // Иначе выйти
label:
invokevirtual // Вызов System.out.println
return


6.2. Память и стек
Стек операндов: Условие if вычисляется в стеке операндов JVM. Например, для x > 0 JVM загружает x и 0, сравнивает их и сохраняет результат (true или false).
Локальные переменные: Переменные, используемые в условии (например, x), хранятся в стеке вызовов.
Куча: Если в условии используются объекты (например, name != null), они находятся в куче, а их ссылки — в стеке.


6.3. Оптимизация в JVM

JIT-компиляция: JIT-компилятор может оптимизировать if/else, встраивая часто используемые условия в машинный код.
Короткое замыкание: Если условие в if использует логические операторы (&&, ||), JVM пропускает ненужные вычисления.
Константные условия: Если условие всегда true или false (например, if (true)), компилятор может убрать ненужный код.


Пример оптимизации:
if (false) {
System.out.println("Никогда не выполнится");
}

Компилятор полностью удалит этот блок из байт-кода.


6.4. Ошибки в памяти
Глубокие вложенности: Слишком много вложенных if увеличивают глубину стека вызовов, но это редко вызывает проблемы.
NullPointerException: Работа с объектами без проверки на null в условии может привести к ошибке.
Неэффективные условия: Сложные условия, такие как a > b && b > c && c > d, могут замедлить выполнение, если не оптимизированы.


Пример ошибки:
String text = null;
if (text.equals("Hello")) { // Ошибка: NullPointerException
System.out.println("Совпадение!");
}

Исправление:
if (text != null && text.equals("Hello")) {
System.out.println("Совпадение!");
}


#Java #для_новичков #beginner #if #else
August 5
Конфигурация, профили, параметры и свойства в Gradle: Управление сборкой

Системные свойства, переменные окружения

Gradle поддерживает настройку через системные свойства и переменные окружения, которые задают параметры JVM и сборки.

Системные свойства:
Задаются через -D в командной строке или в gradle.properties.

Пример:
./gradlew build -Dorg.gradle.jvmargs=-Xmx2048m


Доступ в build.gradle:
println System.getProperty('org.gradle.jvmargs')



Переменные окружения:
Доступны через System.getenv().

Пример:
println System.getenv('JAVA_HOME')


Полезны для передачи внешних параметров (например, API-ключи, пути).

Приоритет:
Системные свойства (-D) > gradle.properties > переменные окружения.
В памяти: Системные свойства и переменные окружения загружаются как часть конфигурации JVM, добавляя минимальный overhead (менее 10 МБ). Gradle кэширует их значения в модели проекта.


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

Проверяйте наличие переменных:
if (System.getenv('CI')) {
println 'Running in CI environment'
}



gradle.properties: Project-level, global (~/.gradle)

Файл gradle.properties используется для определения свойств Gradle, JVM и проекта.

Project-level (projectDir/gradle.properties):
Применяется к конкретному проекту.

Пример:
version=1.0.0
org.gradle.jvmargs=-Xmx2048m
org.gradle.parallel=true


Global (~/.gradle/gradle.properties):
Применяется ко всем проектам на машине.

Пример:
org.gradle.caching=true
org.gradle.jvmargs=-Xmx4096m


Использование:
Свойства доступны через project.property:println project.version
В памяти: Свойства загружаются как часть модели проекта на фазе инициализации, добавляя минимальный overhead (5-10 МБ). Глобальный gradle.properties парсится для всех сборок, увеличивая время инициализации.


Нюансы:
Храните чувствительные данные (например, ключи API) в ~/.gradle/gradle.properties с ограниченными правами (chmod 600).
Используйте для централизованных настроек, таких как JVM-память или включение кэширования.



Использование -P, -D, --info, --debug, --scan

Gradle предоставляет командные параметры для настройки и отладки сборки.

-P:
Задает свойства проекта.

Пример:
./gradlew build -PmyProperty=value


Доступ:
println project.hasProperty('myProperty') ? project.myProperty : 'default'


-D:
Задает системные свойства JVM.

Пример:
./gradlew build -Dorg.gradle.jvmargs=-Xmx2048m


--info:
Выводит информационные логи.

Пример:
./gradlew build --info


--debug:
Выводит подробные логи для отладки.

Пример:
./gradlew build --debug


--scan:
Генерирует Build Scan для анализа сборки.

Пример:
./gradlew build --scan


Требует настройки плагина:
plugins {
id 'com.gradle.build-scan' version '3.17.4'
}
buildScan {
termsOfServiceUrl = 'https://gradle.com/terms-of-service'
termsOfServiceAgree = 'yes'
}


В памяти: Параметры -P и -D добавляют свойства в модель проекта или JVM, минимально влияя на память. Логи --info и --debug увеличивают объем вывода, а --scan загружает метаданные сборки в память (50-100 МБ для крупных проектов).


Нюансы:
Используйте --scan для анализа производительности и выявления узких мест.
Параметры -P и -D имеют приоритет над
gradle.properties.


Lazy vs Eager Configuration

Gradle поддерживает два подхода к конфигурации: eager (немедленная) и lazy (ленивая).

Eager Configuration:
Вычисления выполняются на фазе конфигурации, даже если задача не выполняется.

Пример:
task example {
def value = computeExpensiveValue()
doLast {
println value
}
}


Lazy Configuration:
Вычисления откладываются до фазы выполнения с использованием Provider или Property.

Пример:
task example {
def value = providers.provider { computeExpensiveValue() }
doLast {
println value.get()
}
}


В памяти: Ленивая конфигурация снижает потребление памяти на фазе конфигурации, так как значения вычисляются только при необходимости. Eager-конфигурация загружает все значения сразу, увеличивая overhead (до 100-200 МБ для сложных скриптов).


#Java #middle #Gradle #Task #deploy
August 6
Конфигурационные API: Provider, Property, ListProperty

Gradle предоставляет API для ленивой конфигурации, что улучшает производительность и гибкость.

Provider:
Интерфейс для ленивых значений, вычисляемых при необходимости.

Пример:
def versionProvider = providers.provider { project.version }
task printVersion {
doLast {
println "Version: ${versionProvider.get()}"
}
}


Property:
Для управления одиночными значениями.

Пример:
task example {
def outputFile = objects.property(String)
outputFile.set('build/output.txt')
doLast {
println "Output: ${outputFile.get()}"
}
}


ListProperty:
Для управления списками значений.

Пример:
task example {
def files = objects.listProperty(String)
files.set(['file1.txt', 'file2.txt'])
doLast {
files.get().each { println it }
}
}


Kotlin DSL:
tasks.register("example") {
val outputFile = objects.property<String>()
outputFile.set("build/output.txt")
doLast {
println("Output: ${outputFile.get()}")
}
}


В памяти: Provider, Property и ListProperty хранят ссылки на значения, а не сами значения, минимизируя потребление памяти до фазы выполнения. Это снижает overhead на фазе конфигурации (экономия 10-50 МБ на задачу).


Нюансы:
Используйте Provider для динамических вычислений.
Property и ListProperty обеспечивают строгую типизацию, особенно в Kotlin DSL.



Ext-переменные (ext {})

Блок ext позволяет определять пользовательские свойства для проекта или объектов.

Пример:
ext {
myVersion = '1.0.0'
myProperty = 'value'
}
task printExt {
doLast {
println project.ext.myVersion
}
}


Kotlin DSL:
extra["myVersion"] = "1.0.0"
extra["myProperty"] = "value"
tasks.register("printExt") {
doLast {
println(project.extra["myVersion"])
}
}


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


В памяти: ext-переменные хранятся как часть модели проекта, добавляя минимальный overhead (менее 10 МБ). Однако большое количество переменных может усложнить модель.


Нюансы:
Избегайте чрезмерного использования ext в пользу Version Catalog для версий зависимостей.
Используйте для кастомных настроек, не связанных с зависимостями.


buildSrc/ директория

Директория buildSrc позволяет определять кастомную логику (плагины, задачи) в проекте.


Структура:
my-project/
├── buildSrc/
│ ├── src/main/groovy/com/example/MyPlugin.groovy
│ ├── build.gradle
├── build.gradle
├── settings.gradle


Пример плагина (buildSrc/src/main/groovy/com/example/MyPlugin.groovy):
package com.example

import org.gradle.api.Plugin
import org.gradle.api.Project

class MyPlugin implements Plugin<Project> {
void apply(Project project) {
project.tasks.register('myTask') {
doLast {
println 'Hello from buildSrc plugin!'
}
}
}
}


Настройка (buildSrc/build.gradle):
plugins {
id 'groovy'
}


Использование:
apply plugin: 'com.example.my-plugin'


Kotlin DSL:
Создайте buildSrc/src/main/kotlin/com/example/MyPlugin.kt:
package com.example

import org.gradle.api.Plugin
import org.gradle.api.Project

class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.tasks.register("myTask") {
doLast {
println("Hello from buildSrc plugin!")
}
}
}
}


Настройте buildSrc/build.gradle.kts:
plugins {
`kotlin-dsl`
}


В памяти: buildSrc компилируется как отдельный проект, добавляя собственный classpath и зависимости в JVM. Это увеличивает потребление памяти (100-200 МБ), особенно если buildSrc включает сложные плагины.


Нюансы:
buildSrc автоматически доступен всем модулям проекта.
Используйте для кастомных задач и плагинов, специфичных для проекта.
Для повторно используемых плагинов предпочтительнее создавать отдельный проект и публиковать в репозиторий.



#Java #middle #Gradle #Task #deploy
August 6
Shared Build Logic через init-скрипты

Init-скрипты позволяют задавать глобальную логику, применяемую ко всем Gradle-сборкам.


Настройка:

Создайте файл ~/.gradle/init.d/my-init.gradle:
allprojects {
repositories {
mavenCentral()
}
}
gradle.projectsLoaded {
rootProject.tasks.register('globalTask') {
doLast {
println 'Global task from init script'
}
}
}


Kotlin DSL (~/.gradle/init.d/my-init.gradle.kts):
allprojects {
repositories {
mavenCentral()
}
}
gradle.projectsLoaded {
rootProject.tasks.register("globalTask") {
doLast {
println("Global task from init script")
}
}
}


Использование:
Init-скрипты автоматически применяются ко всем проектам.

Можно указать скрипт явно:
./gradlew build --init-script my-init.gradle


В памяти: Init-скрипты загружаются на фазе инициализации, добавляя задачи и конфигурации в модель проекта. Это увеличивает overhead (10-50 МБ), но распределяется между всеми сборками.


Нюансы:
Используйте для глобальных настроек (например, репозитории, JVM-параметры).
Избегайте сложной логики, чтобы не замедлять инициализацию.



Gradle Wrapper (gradlew): настройка и версионирование

Gradle Wrapper (gradlew) — это скрипт, обеспечивающий воспроизводимость сборки с фиксированной версией Gradle.

Настройка:
Сгенерируйте Wrapper:
gradle wrapper --gradle-version 8.1


Создает файлы:
my-project/
├── gradlew
├── gradlew.bat
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ ├── gradle-wrapper.properties


gradle-wrapper.properties:
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip


Использование:
Запускайте сборку через Wrapper:
./gradlew build


Wrapper загружает указанную версию Gradle, если она отсутствует в ~/.gradle/wrapper.

Версионирование:

Обновите версию:
./gradlew wrapper --gradle-version 8.2


Используйте конкретную версию для согласованности в CI/CD.

В памяти: Wrapper загружает Gradle в отдельный JVM-процесс, добавляя базовый overhead (200-300 МБ). Кэширование дистрибутива в ~/.gradle/wrapper минимизирует сетевые запросы.


Нюансы:
Всегда включайте Wrapper в репозиторий для воспроизводимости.
Проверяйте целостность distributionUrl для безопасности (используйте HTTPS).
Настройте прокси в
gradle-wrapper.properties, если требуется:systemProp.http.proxyHost=proxy.example.com
systemProp.http.proxyPort=8080



#Java #middle #Gradle #Task #deploy
August 6
Switch/case в Java

Оператор switch в Java позволяет выбирать, какой код выполнять, в зависимости от значения переменной. Он часто используется вместо множества if-else условий, когда нужно проверить одно значение на совпадение с несколькими вариантами. С Java 14 switch также поддерживает switch expressions (выражения), которые делают код короче и удобнее.

1. Что такое switch в Java?

switch — это условный оператор, который проверяет значение переменной и выполняет соответствующий блок кода в зависимости от совпадения с заданными вариантами (case). Switch expressions — это улучшенная версия switch, которая позволяет возвращать значение и писать более компактный код.

Зачем нужен switch?
Простота выбора: Удобен, когда нужно выбрать действие из нескольких вариантов.
Читаемость: Делает код чище, чем длинная цепочка if-else.
Гибкость: Поддерживает разные типы данных и новые возможности (switch expressions).
Эффективность: В некоторых случаях работает быстрее, чем if-else.



2. Синтаксис switch

2.1. Классический switch (оператор)
Классический switch выполняет блок кода для совпадающего case и использует break, чтобы выйти из конструкции.

Синтаксис:
switch (выражение) {
case значение1:
// Код для значения1
break;
case значение2:
// Код для значения2
break;
default:
// Код, если ни один case не совпал
}


Пример:
int day = 3;
switch (day) {
case 1:
System.out.println("Понедельник");
break;
case 2:
System.out.println("Вторник");
break;
case 3:
System.out.println("Среда");
break;
default:
System.out.println("Другой день");
}

Вывод: Среда


2.2. Switch Expression (с Java 14)
Switch expressions позволяют возвращать значение и использовать стрелочный синтаксис (->) для упрощения кода. Они не требуют break, так как автоматически завершают выполнение.

Синтаксис:
switch (выражение) {
case значение1 -> результат1;
case значение2 -> результат2;
default -> результат_по_умолчанию;
}


Пример:
int day = 3;
String dayName = switch (day) {
case 1 -> "Понедельник";
case 2 -> "Вторник";
case 3 -> "Среда";
default -> "Другой день";
};
System.out.println(dayName);

Вывод: Среда


Примечания к синтаксису:
Поддерживаемые типы:
Классический switch: byte, short, int, char, String, enum.
Switch expression: Те же типы + возможность возвращать значения.
break: В классическом switch нужен, чтобы избежать "проваливания" (fall-through) в следующий case.
default: Выполняется, если ни один case не совпал. Необязателен, но рекомендуется.
Стрелочный синтаксис (->): Используется в switch expressions для компактности.


#Java #для_новичков #beginner #switch_case
August 7
3. Типы конструкций switch

3.1. Классический switch (оператор)

Используется для выполнения блока кода для одного или нескольких значений.
Требует break, чтобы остановить выполнение после совпадения.


Пример:
char grade = 'B';
switch (grade) {
case 'A':
System.out.println("Отлично!");
break;
case 'B':
System.out.println("Хорошо!");
break;
case 'C':
System.out.println("Нормально");
break;
default:
System.out.println("Неизвестная оценка");
}

Вывод: Хорошо!


3.2. Switch с fall-through
Если не указать break, выполнение "проваливается" в следующий case.

Пример:
int month = 4;
switch (month) {
case 3:
case 4:
case 5:
System.out.println("Весна");
break;
default:
System.out.println("Другой сезон");
}

Вывод: Весна


3.3. Switch Expression
Возвращает значение, которое можно присвоить переменной.
Использует -> для компактности или {} для сложной логики.


Пример:
int number = 2;
int result = switch (number) {
case 1 -> 10;
case 2 -> {
int temp = number * 20;
yield temp; // Возвращает значение
}
default -> 0;
};
System.out.println(result);

Вывод: 40


3.4. Switch с enum
Удобен для работы с перечислениями (enum).

Пример:
enum Day { MONDAY, TUESDAY, WEDNESDAY }
Day day = Day.TUESDAY;
String message = switch (day) {
case MONDAY -> "Начало недели";
case TUESDAY -> "Второй день";
case WEDNESDAY -> "Середина недели";
};
System.out.println(message);

Вывод: Второй день


4. Правильное применение switch

Чтобы писать понятный и эффективный код с switch, следуйте этим рекомендациям:

4.1. Используйте switch для четких вариантов
Применяйте switch, когда проверяете одно значение на несколько конкретных вариантов (например, дни недели, оценки).

Пример:
int score = 85;
switch (score / 10) {
case 9:
System.out.println("Оценка: A");
break;
case 8:
System.out.println("Оценка: B");
break;
default:
System.out.println("Оценка ниже");
}


4.2. Не забывайте break в классическом switch
Без break код продолжит выполняться для всех последующих case, что может привести к ошибкам.

Пример ошибки:
int day = 1;
switch (day) {
case 1:
System.out.println("Понедельник");
case 2:
System.out.println("Вторник");
}

Вывод: Понедельник и Вторник (из-за отсутствия break).


4.3. Используйте switch expressions для простоты
Если нужно вернуть значение, используйте switch expression вместо классического switch.

Пример:
String fruit = "apple";
String type = switch (fruit) {
case "apple", "pear" -> "Фрукт";
case "carrot" -> "Овощ";
default -> "Неизвестно";
};


4.4. Добавляйте default
Всегда включайте default, чтобы обработать неожиданные значения.

Пример:
char letter = 'Z';
switch (letter) {
case 'A':
System.out.println("Первая буква");
break;
default:
System.out.println("Другая буква");
}


4.5. Проверяйте типы данных
Убедитесь, что выражение в switch соответствует поддерживаемому типу (int, String, enum и т.д.).

Пример ошибки:
double value = 1.5;
switch (value) { // Ошибка: double не поддерживается
case 1.5:
System.out.println("1.5");
}



#Java #для_новичков #beginner #switch_case
August 7
5. Работа switch под капотом

5.1. Компиляция в байт-код
Классический switch компилируется в одну из двух инструкций байт-кода:
tableswitch: Для последовательных значений case (например, 1, 2, 3).
lookupswitch: Для непоследовательных значений (например, 1, 10, 100).


Эти инструкции создают таблицу переходов, которая быстрее, чем многократные сравнения в if-else.

Пример:
int day = 2;
switch (day) {
case 1:
System.out.println("Понедельник");
break;
case 2:
System.out.println("Вторник");
break;
}


Байт-код (упрощенно):
iload day
tableswitch {
1: invokevirtual // Вызов для "Понедельник"
2: invokevirtual // Вызов для "Вторник"
default: return
}


5.2. Switch Expressions
Switch expressions компилируются в байт-код с использованием invokedynamic (с Java 12+), что позволяет JVM оптимизировать выполнение.
Ключевое слово yield в switch expressions генерирует инструкцию для возврата значения.


5.3. Память и стек
Стек операндов: Значение в switch загружается в стек операндов JVM для сравнения с case.
Куча: Если switch работает с объектами (например, String или enum), их ссылки хранятся в стеке, а сами объекты — в куче.
Переменные: Локальные переменные внутри case хранятся в стеке вызовов.


5.4. Оптимизация в JVM
Таблицы переходов: tableswitch и lookupswitch быстрее, чем многократные сравнения в if-else, особенно для большого числа case.
JIT-компиляция: JIT-компилятор может встраивать switch в машинный код, улучшая производительность.
Оптимизация констант: Если switch использует константы, JVM может заранее вычислить переходы.


5.5. Ошибки в памяти
Проваливание (fall-through): Без break в классическом switch выполняется лишний код, что может привести к ошибкам.
Неправильные типы: Использование неподдерживаемых типов (например, double) вызывает ошибку компиляции.
Слишком много case: Большое количество case увеличивает размер байт-кода, но это редко влияет на производительность.


Пример ошибки:
String fruit = "apple";
switch (fruit) {
case "apple":
System.out.println("Фрукт");
case "banana": // Без break
System.out.println("Тоже фрукт");
}

Вывод: Фрукт и Тоже фрукт (из-за fall-through).



6. Новшества в switch


Java 17 (сентябрь 2021, LTS)

Pattern Matching для switch (превью): Теперь switch может проверять не только значения, но и типы объектов. Это делает код короче и понятнее, чем if-else с instanceof.
javaObject obj = "Hello";
String result = switch (obj) {
case Integer i -> "Это число";
case String s -> "Это строка";
default -> "Неизвестно";
};


Guarded Patterns (с &&):
Можно добавлять дополнительные условия в case с помощью &&.
javacase Employee e && e.getDept().equals("IT") -> "IT сотрудник";


Обработка null: switch больше не выбрасывает NullPointerException для null, если есть case null.


Java 21 (сентябрь 2023, LTS)

Pattern Matching стал постоянной фичей: Улучшено и стабилизировано из Java 17. Теперь поддерживает сложные проверки типов и декомпозицию объектов (например, с record).
javarecord Point(int x, int y) {}
Object obj = new Point(1, 2);
String result = switch (obj) {
case Point(var x, var y) -> "Точка: " + x + ", " + y;
default -> "Не точка";
};


Guarded Patterns с when:
Вместо && используется when для условий в case, что делает код читаемее.
javacase Employee e when e.getDept().equals("IT") -> "IT сотрудник";


Полная проверка покрытия: Для sealed классов или enum компилятор проверяет, что все случаи учтены, без default.


Java 25 (сентябрь 2025, Early Access)

Поддержка примитивных типов в Pattern Matching (превью): Теперь switch может работать с примитивными типами (int, long, double) в case с паттернами.

javaint value = 42;
String result = switch (value) {
case int i when i > 0 -> "Положительное число";
case int i -> "Ноль или отрицательное";
};


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

javacase Point(int x, _) -> "Только x: " + x;


#Java #для_новичков #beginner #switch_case
August 7
Интеграции, публикации в Gradle

Публикация артефактов

Gradle поддерживает публикацию артефактов (JAR, WAR, и т.д.) в репозитории с помощью плагина maven-publish.
maven-publish, publishing {}


Плагин maven-publish:
Позволяет публиковать артефакты в Maven-совместимые репозитории.

Пример (build.gradle):
plugins {
id 'java'
id 'maven-publish'
}
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
groupId = 'com.example'
artifactId = 'my-library'
version = '1.0.0'
}
}
repositories {
maven {
url 'https://nexus.example.com/repository/maven-releases'
credentials {
username = project.findProperty('nexusUsername')
password = project.findProperty('nexusPassword')
}
}
}
}


Kotlin DSL:
plugins {
java
`maven-publish`
}
publishing {
publications {
create<MavenPublication>("mavenJava") {
from(components["java"])
groupId = "com.example"
artifactId = "my-library"
version = "1.0.0"
}
}
repositories {
maven {
url = uri("https://nexus.example.com/repository/maven-releases")
credentials {
username = project.findProperty("nexusUsername") as String?
password = project.findProperty("nexusPassword") as String?
}
}
}
}


Команда публикации:
./gradlew publish


В памяти: Публикация загружает метаданные артефактов (POM-файлы, JAR) и зависимости в память, добавляя 50-100 МБ overhead. Сетевые операции для загрузки в репозиторий увеличивают время выполнения.


Нюансы:
Храните учетные данные в ~/.gradle/gradle.properties:
nexusUsername=myuser
nexusPassword=mypassword


Используйте HTTPS для безопасной публикации.


Подпись: GPG и signing

Плагин signing:
Добавляет подпись артефактов с помощью GPG для проверки их целостности.


Пример:
plugins {
id 'java'
id 'maven-publish'
id 'signing'
}
signing {
sign publishing.publications.mavenJava
}
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
}
}
}


Настройка GPG:

Установите GPG и сгенерируйте ключ:
gpg --gen-key


Настройте ~/.gradle/gradle.properties:
signing.keyId=12345678
signing.password=yourpassword
signing.secretKeyRingFile=/path/to/.gnupg/secring.gpg


В памяти: Подпись добавляет операции шифрования, увеличивая потребление памяти (10-50 МБ) и время выполнения задачи.


Нюансы:

Публикуйте публичный ключ в keyserver (например, gpg --send-keys 12345678).
Для Maven Central подпись обязательна.



Интеграция с Maven Central, Artifactory, Nexus

Maven Central:
Используйте плагин io.github.gradle-nexus.publish-plugin для упрощения публикации:
plugins {
id 'io.github.gradle-nexus.publish-plugin' version '1.3.0'
}
nexusPublishing {
repositories {
sonatype {
nexusUrl = uri('https://s01.oss.sonatype.org/service/local/')
snapshotRepositoryUrl = uri('https://s01.oss.sonatype.org/content/repositories/snapshots/')
username = project.findProperty('sonatypeUsername')
password = project.findProperty('sonatypePassword')
}
}
}


Команда:
 ./gradlew publishToSonatype closeAndReleaseRepository.


Artifactory:
Настройте репозиторий:
publishing {
repositories {
maven {
url 'https://artifactory.example.com/artifactory/libs-release'
credentials {
username = project.findProperty('artifactoryUsername')
password = project.findProperty('artifactoryPassword')
}
}
}
}


Nexus:
Аналогично Artifactory, укажите URL и учетные данные.

В памяти: Публикация в удаленные репозитории загружает метаданные и артефакты в память, а также выполняет сетевые операции, увеличивая overhead (50-200 МБ).


#Java #middle #Gradle #Task #integration
August 8