W3docs

Пакеты Java

Группируйте классы Java в пакеты, соблюдайте соглашения об именовании и структурируйте проекты для удобного сопровождения.

В Java нет единого пространства имён для всех классов. Каждый класс находится внутри пакета — именованного пространства, которое служит одновременно единицей организации и пространством имён на уровне языка. Два класса с именем Logger могут сосуществовать без конфликтов, если они находятся в разных пакетах, а имя пакета используется повсеместно: в операторах import, в полностью квалифицированных именах, в файловой системе и даже в манифестах JAR. Понимание пакетов позволяет с первого взгляда разобраться в структуре чужого проекта.

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

Что такое пакет

Пакет выполняет три функции одновременно:

  • Пространство имён. java.util.Date и java.sql.Date — разные классы; имя пакета разделяет их.
  • Граница доступа. Без модификатора члены класса видны только внутри того же пакета — уровень доступа «package-private». Это реальная, структурная форма инкапсуляции; см. Модификаторы доступа.
  • Каталог. Имя пакета однозначно соответствует пути к папке. com.example.app.util находится по пути com/example/app/util/.

Одно и то же имя используется в трёх местах — в объявлении, пути к файлу и операторе import — и все три должны совпадать.

Соглашения об именовании

В Java принято именование в обратном порядке DNS на основе домена, которым вы владеете:

  • Только строчные буквы: com.example, а не Com.Example.
  • Обратный порядок домена: проект на w3docs.com использует com.w3docs в качестве корня.
  • Далее следуют сегменты проекта, модуля и функциональности: com.w3docs.learnjava.parser.
  • Избегайте зарезервированных слов Java в качестве сегментов (int, class, new). Если ваш домен содержит одно из них, измените его: com.example.int_ или разбейте иначе.

Эти соглашения важны не только для эстетики. Именно правило обратного DNS позволяет безопасно добавлять JAR от разных организаций в один classpath без конфликтов имён.

Пакет по умолчанию

Файл .java без объявления package принадлежит пакету по умолчанию (безымянному). Из этого следует два последствия:

  • Нельзя использовать import из пакета по умолчанию в именованный пакет. Всё, что находится в нём, фактически отрезано от реального кода.
  • Инструменты сборки, IDE и модульные системы рассматривают пакет по умолчанию как вырожденный случай — многие из них просто отказываются компилировать против него.

Используйте его для разовых файлов типа Hello.java. Не выпускайте из него ничего в продакшн.

Как пакеты соотносятся с каталогами

Если вы объявляете package com.w3docs.learnjava.parser; в начале файла Tokenizer.java, этот файл должен находиться по пути:

com/w3docs/learnjava/parser/Tokenizer.java

относительно корня исходного кода. Компилятор не выводит пакет из пути — он читает объявление и доверяет вам. Однако среда выполнения (и большинство инструментов) будет недовольна, если они не совпадают.

Этот корень исходного кода — начало кроличьей норы classpath: JVM должна знать, где начинается дерево пакетов, иначе она ничего не найдёт.

Объявление пакета

Оператор package присваивает класс пакету. Он должен быть самым первым оператором в файле — перед любым import, перед самим классом. Перед ним могут стоять только комментарии и пустые строки.

// File: com/w3docs/learnjava/parser/Tokenizer.java
package com.w3docs.learnjava.parser;

import java.util.List;

public class Tokenizer {
    public List<String> tokenize(String source) {
        // ...
        return List.of();
    }
}

Отныне этот класс известен везде под полностью квалифицированным именем com.w3docs.learnjava.parser.Tokenizer. Код в том же пакете может обращаться к нему просто как Tokenizer; код из других пакетов либо импортирует его, либо указывает полное имя.

Внимание
Файл может содержать не более одного оператора package, и только один публичный класс верхнего уровня — имя которого должно совпадать с именем файла. Поместите package в любое место, кроме начала файла, и компилятор отклонит его.

Практический пример: два Loggerа

Самый наглядный аргумент в пользу пакетов — конфликты, которые они предотвращают. Следующая программа использует два класса с именем Logger по духу — java.util.logging.Logger из JDK по полностью квалифицированному имени — и показывает, как пакет класса становится частью его идентификации во время выполнения.

java— editable, runs on the server

Два вывода из запуска программы: классы знают свой пакет во время выполнения (через Class.getName() и Class.getPackage()), а полностью квалифицированное имя — это то, что однозначно идентифицирует тип: Logger сам по себе неоднозначен, а java.util.logging.Logger — нет.

Что дальше

Назвать пакет — это одно; подключить его типы в свой код — совсем другое. Следующая глава посвящена оператору import — импорту отдельных типов, импорту с подстановочными символами, статическому импорту и тому, когда каждый из них уместен.

Практика

Практика
Почему стандартное соглашение Java для имён пакетов использует обратный порядок домена, например `com.example.app`?
Почему стандартное соглашение Java для имён пакетов использует обратный порядок домена, например `com.example.app`?
Was this page helpful?