Пакеты 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 и зависимость системы сборки.
  • Создавать циклические зависимости между пакетами.
  • Переносить публичные типы, не учитывая совместимость полного имени.

Вопросы на собеседовании

  1. Для чего нужны пакеты кроме группировки файлов?
  2. Что делает import во время компиляции и выполнения?
  3. Импортирует ли java.util.* подпакеты?
  4. Чем package-private отличается от protected?
  5. Является ли подпакет частью родительского пакета?
  6. Чем пакет отличается от Java-модуля?

Связанные заметки

Модификаторы доступа · Интерфейсы Java · Объекты Java · Инкапсуляция