JavaRush /Курси /JAVA 25 SELF /Вступ до модулів: навіщо вони потрібні

Вступ до модулів: навіщо вони потрібні

JAVA 25 SELF
Рівень 60 , Лекція 0
Відкрита

1. Проблеми старого підходу

«Classpath party»: коли всі на одній кухні

До появи модулів у Java (у дев’ятій версії) увесь код застосунку, бібліотеки та залежності звалювалися в одну велику купу — classpath. Меж між бібліотеками фактично не було: будь-який клас, що потрапив до classpath, міг бути знайдений і використаний ким завгодно.

Виникали конфлікти імен: дві бібліотеки містили клас з однаковою повною назвою, наприклад com.example.Util — обирався «якийсь один», а ви дізнавалися про проблему вже за дивною поведінкою в рантаймі.

Класика жанру — dependency hell: одна бібліотека вимагає логер версії 1.2, інша — 1.3, а в classpath опиняються обидві. JVM не знає, що підхопити, і ви ловите загадкову NoSuchMethodError.

І ще: навіть якщо клас оголошено як public, але він призначений для внутрішніх потреб бібліотеки, інший код усе одно може його використати — і в процесі щось зламати. Великі проєкти перетворювалися на мінне поле, а супроводження — на пригоду.

2. Що таке модуль у Java?

Модуль: як коробка з дірочками

У Java 9 з’явилася модульна система (JPMS). Модуль — це коробка з класами та пакетами, у якій ви самі робите «дірочки»: явно зазначаєте, що показуєте назовні (exports), а що ховаєте всередині. Так, навіть public-клас не видно іншим модулям, якщо його пакет не експортовано.

Формальне визначення

Модуль — логічна одиниця поділу коду, що об’єднує пов’язані пакети та класи. Кожен модуль явно оголошує:

  • що він експортує — робить доступним іншим модулям (exports);
  • і що він імпортує — від яких інших модулів залежить (requires).

Ключові властивості модуля:

  • Явні межі. Чітко визначено, що доступно ззовні, а що ні.
  • Явні залежності. Модуль не «бачить» інший модуль без явного requires.
  • Ізоляція. Внутрішні деталі можуть бути повністю приховані від зовнішнього світу.

3. Переваги модульності

Поліпшена інкапсуляція

З’явився новий рівень видимості — на рівні модуля. Навіть якщо клас public, але його пакет не експортується, він видимий лише всередині модуля. Внутрішні деталі бібліотеки більше не «просочуються» назовні.

Явний опис залежностей

Потрібні залежності перелічуються в module-info.java. Компілатор і IDE заздалегідь попередять, якщо ви забули вказати модуль, від якого залежите.

Поліпшена безпека та надійність

Обмеження доступу до внутрішніх API ускладнює випадкове або навмисне втручання. Це критично для великих бібліотек і платформних модулів.

Можливість створення «урізаних» JRE

За допомогою jlink можна зібрати мінімальний рантайм лише з потрібних модулів. У хмарі та embedded-сценаріях це заощаджує дисковий простір і пам’ять.

Бонус: прискорення старту та зменшення розміру

Завантажуються не всі модулі, а тільки реально використані — застосунки стартують швидше і споживають менше ресурсів.

4. Де використовуються модулі

У стандартній бібліотеці Java

Починаючи з Java 9, сама платформа модульна. Приклади:

  • java.base — базовий модуль (обов’язковий для будь-якої програми).
  • java.sql — робота з базами даних.
  • java.xml — робота з XML.

Якщо ви не використовуєте XML, модуль java.xml навіть не потрапить у ваш рантайм.

У великих застосунках і бібліотеках

Must-have для корпоративних систем, де десятки команд розробляють частини монорепозиторію. Модульність зменшує конфлікти та спрощує супровід.

У власних проєктах

Навіть у pet-проєкті модулі допомагають тренувати архітектурне мислення й тримати код у порядку, уникаючи «спагеті».

5. Короткий огляд синтаксису: module-info.java

Найголовніше — файл module-info.java

Файл module-info.java лежить у корені вихідників модуля та оголошує його межі й залежності.

module my.awesome.module {
    exports com.example.api;        // Експортований пакет
    requires java.sql;              // Залежність від стандартного модуля
}

Основні ключові слова:

  • module <назва> — оголошує модуль.
  • exports <пакет> — робить пакет доступним іншим модулям.
  • requires <модуль> — оголошує залежність від іншого модуля.

Мінімальний приклад:

module com.myproject.core {
    exports com.myproject.core.api;
}

Приклад із залежністю:

module com.myproject.app {
    requires com.myproject.core;
    requires java.sql;
}

Додаткові можливості (коротко): для рефлексії є opens, для сервісів — uses і provides ... with ... (детальніше — у просунутих лекціях).

6. Корисні нюанси

Модулі — це як «паспорти» для класів. Раніше будь-який клас з візою public міг «подорожувати» застосунком. Тепер потрібен ще й модульний «паспорт» — експорт пакета (exports). Без нього клас залишається «вдома».

Dependency hell — це реальний термін. Якщо бачили ClassNotFoundException: com.google.common.base.Strings, ви там побували. Модулі покликані розігнати цих «чортів» суворими межами та залежностями.

Уся Java тепер модульна. Навіть якщо ви не пишете власні модулі, платформа вже розбита на десятки. Спробуйте команду:

java --list-modules

Як це впливає на розробку

Ви явно керуєте видимістю коду, і внутрішні пакети більше не стають доступними всім «випадково». IDE та компілятор ловлять помилки раніше: забули оголосити залежність — проєкт не збереться. Підтримка великих систем спрощується: легше зрозуміти, хто від кого залежить і що зламається при змінах.

7. Типові помилки під час переходу на модулі

Помилка № 1: забули експортувати пакет, але клас public. Ви оголосили клас як public, але не експортували його пакет — інші модулі не зможуть його використовувати. Компілатор повідомить: «Пакет не експортується модулем».

Помилка № 2: не оголосили залежність через requires. Ви використовуєте тип з іншого модуля, але забули додати requires у module-info.java. Підсумок — помилка компіляції «модуль не може знайти потрібний клас».

Помилка № 3: дубльовані імена модулів. У великому проєкті два модулі випадково отримали однакову назву. JVM такого не прощає — перейменуйте та дотримуйтеся єдиного найменування.

Помилка № 4: забули про стандартні модулі. Наприклад, використовуєте JDBC, але не додали requires java.sql;. У Java 8 «і так було видно», а у 9+ — ні.

Помилка № 5: спроба використати внутрішній клас іншого модуля. Якщо пакет не експортується, навіть public-клас залишається невидимим ззовні. Експортуйте пакет або винесіть API у «публічний» шар.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ