Пакеты и модификаторы доступа
Пока программа помещается в один файл, порядок в ней держать легко. Настоящие проекты состоят из сотен классов, и их раскладывают по пакетам. Пакеты вместе с модификаторами доступа решают, какой код кому виден.
Пакет — это папка
Пакет — именованная группа классов. Первая строка файла package shop.model; говорит, что класс лежит в пакете shop.model, и файл обязан находиться в папке shop/model. В проекте каждый открытый класс живёт в своём файле с тем же именем:
// src/shop/model/Product.java
package shop.model;
public class Product {
private final String title;
private final double price;
public Product(String title, double price) {
this.title = title;
this.price = price;
}
public String getTitle() {
return title;
}
public double getPrice() {
return PriceRules.round(price);
}
}Имена пакетов пишут строчными буквами через точку. Полное имя класса включает пакет: shop.model.Product.
import
Чтобы пользоваться классом из другого пакета, его импортируют — после этого достаточно короткого имени. Главный файл может оставаться компактным, с void main():
// src/Main.java
import shop.model.Product;
void main() {
Product tea = new Product("Чай", 4.499);
IO.println(tea.getTitle() + ": " + tea.getPrice() + " €");
}Классы из пакета java.lang — String, Math, Integer — доступны всегда. В компактном файле вроде этого автоматически импортированы и остальные пакеты модуля java.base, например java.util — поэтому в примерах учебника List и Map работали без import. В классических файлах с package их нужно импортировать: import java.util.List;.
Четыре уровня доступа
Модификатор перед классом, полем или методом определяет, откуда к нему можно обратиться:
| Модификатор | Кому доступно |
|---|---|
public | всем |
protected | своему пакету и подклассам в любом пакете |
| без модификатора | только своему пакету |
private | только своему классу |
Доступ без модификатора называют пакетным (package-private). Обычное правило: поля — private, методы для внешнего мира — public, всё вспомогательное — без модификатора или private.
Пакетный доступ
Класс без public виден только внутри своего пакета. Здесь PriceRules — внутренняя деталь пакета shop.model: Product им пользуется, а код снаружи — нет:
// src/shop/model/PriceRules.java
package shop.model;
class PriceRules {
static double round(double price) {
return Math.round(price * 100) / 100.0;
}
}Закрытые поля тоже по-настоящему закрыты. Если в Main написать IO.println(tea.title);, компилятор ответит:
Main.java:5: error: title has private access in Product
IO.println(tea.title);
^В одном файле, как в прошлых уроках, такой ошибки не было бы: там все классы вложены в один неявный класс.
Сборка и запуск
Проект выглядит так: в папке src лежат Main.java и shop/model/ с двумя классами. Начиная с Java 22, запускать его можно прямо из исходников — Java сама найдёт и скомпилирует нужные файлы по именам пакетов:
cd src
java Main.javaКлассический способ — сначала скомпилировать всё командой javac в папку out, а потом запустить готовые .class-файлы, указав, где их искать (-cp, classpath):
cd src
javac -d ../out Main.java shop/model/*.java
java -cp ../out MainРезультат в обоих случаях:
Чай: 4.5 €Большие проекты собирают инструментами Maven или Gradle: они делают то же самое и заодно скачивают библиотеки. Код там лежит в папке src/main/java. А ещё более крупный уровень порядка, чем пакеты, — модули с файлом module-info.java — разобран в уроке Модули.
Частая ошибка
Класс без public нельзя использовать из другого пакета. Если в Main обратиться к PriceRules напрямую — shop.model.PriceRules.round(4.499), — сборка остановится:
Main.java:4: error: PriceRules is not public in shop.model; cannot be accessed from outside package
IO.println(shop.model.PriceRules.round(4.499));
^Либо сделайте класс public, если он действительно нужен снаружи, либо дайте доступ через открытый метод другого класса, как getPrice() в Product.