Пакеты Java
Назначение
Пакет образует пространство имён и границу package-private-доступа. Полное имя типа включает пакет:
package dev.javanotes.orders;
public final class OrderService {
}Файл обычно лежит в каталоге, соответствующем пакету, потому что так устроены стандартные инструменты сборки. Само имя пакета — часть API: перенос публичного класса меняет его полное имя.
Обычно используют обратное доменное имя организации и далее предметную область: com.example.billing.
Импорты
import позволяет использовать короткое имя в исходном файле:
import java.time.Instant;
import java.util.List;
import static java.util.Objects.requireNonNull;Импорт не «загружает» класс в JVM и не добавляет библиотеку в проект. Он лишь помогает компилятору разрешить имена. Пакет java.lang и типы текущего пакета доступны без импорта.
Wildcard:
import java.util.*;не импортирует подпакеты: java.util.* не включает java.util.concurrent.*. Два типа с одинаковым коротким именем нельзя импортировать одновременно — один используют по полному имени.
Доступ внутри пакета
Член без модификатора имеет package-private-доступ:
final class OrderValidator { // виден только пакету
boolean valid(Order order) { // тоже package-private
return order != null;
}
}Это полезный способ оставить внутреннюю реализацию скрытой, не создавая публичный API. protected даёт доступ классам того же пакета, а также наследникам с дополнительными правилами для обращения через экземпляр.
Структура проекта
Пакеты лучше организовывать по функциональным областям, если это сохраняет связанный код рядом:
orders/
Order.java
OrderRepository.java
OrderService.java
payments/
Payment.java
PaymentService.javaСтруктура только по техническим слоям (controllers, services, repositories) в большом приложении может разнести одну функцию по всему проекту. Выбор зависит от масштаба, но границы должны отражать зависимости и ответственность.
Пакеты и модули
Пакет не равен модулю. Модуль Java (module-info.java) явно перечисляет экспортируемые пакеты и зависимости. Публичный класс из неэкспортируемого пакета недоступен другому модулю через обычный модульный контракт.
Unnamed package годится только для коротких учебных примеров: типы из него нельзя нормально импортировать в именованные пакеты.
Ловушки
- Считать подпакет частью родительского пакета с точки зрения доступа:
a.bиa.b.internal— разные пакеты. - Полагать, что wildcard импортирует подпакеты.
- Делать каждый класс
public, раздувая внешний API. - Путать import и зависимость системы сборки.
- Создавать циклические зависимости между пакетами.
- Переносить публичные типы, не учитывая совместимость полного имени.
Вопросы на собеседовании
- Для чего нужны пакеты кроме группировки файлов?
- Что делает
importво время компиляции и выполнения? - Импортирует ли
java.util.*подпакеты? - Чем package-private отличается от
protected? - Является ли подпакет частью родительского пакета?
- Чем пакет отличается от Java-модуля?
Связанные заметки
Модификаторы доступа · Интерфейсы Java · Объекты Java · Инкапсуляция