Пакеты и организация кода
Когда классов становится сотни, пакеты поддерживают порядок. Пакеты — это папки для Java-классов, обеспечивающие пространства имён и структуру проекта.
Зачем нужны пакеты
Представьте библиотеку с тысячами книг на полках без порядка. Найти нужную книгу — кошмар. Пакеты — это как полки с подписями: «Романы», «Наука», «Детективы».
Пакет в Java решает две задачи:
- Организация: классы группируются по функциональности (model, service, util). В большом проекте с сотнями классов без пакетов impossible найти нужный.
- Пространство имён: классы с одинаковыми именами могут сосуществовать в разных пакетах. Например, java.util.Date и java.sql.Date — два разных класса Date.
Аналогия: два человека named «Александр Иванов». Без контекста — непонятно, о ком речь. Но если один — «Иванов из Москвы», а другой — «Иванов из Питера», всё ясно. Пакет — это как город для класса.
Объявление пакета
Первая строка в файле — объявление пакета. Путь должен совпадать со структурой папок:
// Файл: com/company/model/User.java
package com.company.model;
public class User {
private String name;
private String email;
public User(String name, String email) {
this.name = name;
this.email = email;
}
public String getName() {
return name;
}
public String getEmail() {
return email;
}
}
Структура папок должна точно соответствовать пакету:
src/
main/
java/
com/
company/
model/
User.java ← package com.company.model
service/
UserService.java ← package com.company.service
util/
StringUtils.java ← package com.company.util
Если пакет не объявлен, класс находится в default-пакете. Это работает, но плохо — в реальных проектах всегда используйте пакеты.
Импорт классов
Чтобы использовать класс из другого пакета, нужно импортировать его через import:
// Файл: com/company/service/UserService.java
package com.company.service;
import com.company.model.User;
import java.util.List;
import java.util.ArrayList;
public class UserService {
private List<User> users = new ArrayList<>();
public void addUser(User user) {
users.add(user);
}
public User findByName(String name) {
for (User user : users) {
if (user.getName().equals(name)) {
return user;
}
}
return null;
}
public void listAll() {
for (User user : users) {
System.out.println(user.getName() + " — " + user.getEmail());
}
}
}
Обратите внимание: сначала идёт package, потом import. Порядок важен!
Полный путь к файлу:
src/main/java/com/company/service/UserService.java
Полное квалифицированное имя (FQN)
FQN (Fully Qualified Name) — полное имя класса включая пакет: java.util.ArrayList, com.company.model.User.
// Без import — используйте FQN:
java.util.List<String> list = new java.util.ArrayList<>();
java.util.Date date = new java.util.Date();
java.io.File file = new java.io.File("test.txt");
// С import — гораздо чище:
import java.util.List;
import java.util.ArrayList;
import java.util.Date;
import java.io.File;
List<String> list = new ArrayList<>();
Date date = new Date();
File file = new File("test.txt");
FQN полезен, когда нужно явно указать, какой класс имеется в виду (например, при конфликте имён):
import java.util.Date;
import java.sql.Date;
public class Example {
java.util.Date utilDate = new java.util.Date();
java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());
}
Соглашения об именовании пакетов
Соглашения помогают другим разработчикам понимать структуру вашего проекта:
- Обратный домен:
com.company.project(вместоproject.company.com) - Все буквы нижнего регистра:
com.company.model(неcom.company.Model) - Без специальных символов: только буквы, цифры и точки
- Одно слово на уровень:
com.company.project.module
// Типичная структура:
com.company.project
├── model // Модели данных (User, Product, Order)
├── service // Бизнес-логика (UserService, ProductService)
├── repository // Работа с БД (UserRepository)
├── controller // Обработка запросов (UserController)
├── util // Утилиты (StringUtils, DateUtils)
├── config // Конфигурация (AppConfig)
└── exception // Исключения (UserNotFoundException)
Типичная структура проекта
Вот как выглядит реальный Java-проект:
myapp/
src/
main/
java/
com/
myapp/
model/
User.java
Product.java
Order.java
service/
UserService.java
ProductService.java
OrderService.java
repository/
UserRepository.java
ProductRepository.java
controller/
UserController.java
ProductController.java
util/
StringUtils.java
DateUtils.java
config/
AppConfig.java
exception/
UserNotFoundException.java
InvalidDataException.java
resources/
application.properties
test/
java/
com/
myapp/
service/
UserServiceTest.java
Каждый пакет имеет свою ответственность:
- model — данные: классы User, Product, Order
- service — бизнес-логика: обработка данных, вычисления
- repository — работа с хранилищем (БД, файлы)
- controller — обработка внешних запросов
- util — общие утилиты: работа со строками, датами
- config — настройки приложения
- exception — пользовательские исключения
import static
import static импортирует статические成员, чтобы не писать имя класса:
// Без import static:
double x = Math.sqrt(16);
System.out.println(Math.PI);
System.out.println(Math.max(10, 20));
// С import static:
import static java.lang.Math.*;
import static java.lang.System.out;
public class Test {
public static void main(String[] args) {
double x = sqrt(16);
out.println(PI);
out.println(max(10, 20));
}
}
import static java.lang.Math.* — импортирует ВСЕ статические成员 класса Math. Можно импортировать конкретный: import static java.lang.Math.PI;
Пакеты и модификаторы доступа
Пакеты тесно связаны с модификаторами доступа. Package-private доступ работает именно на уровне пакета:
// Файл: com/company/model/User.java
package com.company.model;
public class User {
String name; // package-private — доступен в com.company.model
public String email; // public — доступен везде
}
// Файл: com/company/service/UserService.java
package com.company.service;
import com.company.model.User;
public class UserService {
void process(User user) {
// user.name = "Иван"; // Ошибка! name из другого пакета
user.email = "i@mail.ru"; // OK — email public
}
}
Если классы в разных пакетах, package-private члены недоступны. Для доступа нужен public или protected.
Полный пример: многофайловый проект
Давайте создадим простой проект «Библиотека» с несколькими пакетами:
// Файл: com/library/model/Book.java
package com.library.model;
public class Book {
private String title;
private String author;
private boolean isAvailable;
public Book(String title, String author) {
this.title = title;
this.author = author;
this.isAvailable = true;
}
public String getTitle() {
return title;
}
public String getAuthor() {
return author;
}
public boolean isAvailable() {
return isAvailable;
}
public void setAvailable(boolean available) {
isAvailable = available;
}
public void display() {
System.out.printf("\"%s\" — %s [%s]%n", title, author,
isAvailable ? "доступна" : "выдана");
}
}
// Файл: com/library/service/Library.java
package com.library.service;
import com.library.model.Book;
import java.util.List;
import java.util.ArrayList;
public class Library {
private String name;
private List<Book> books;
public Library(String name) {
this.name = name;
this.books = new ArrayList<>();
}
public void addBook(Book book) {
books.add(book);
System.out.println("Добавлена: " + book.getTitle());
}
public Book findBook(String title) {
for (Book book : books) {
if (book.getTitle().equalsIgnoreCase(title)) {
return book;
}
}
return null;
}
public boolean checkoutBook(String title) {
Book book = findBook(title);
if (book != null && book.isAvailable()) {
book.setAvailable(false);
System.out.println("Выдана: " + book.getTitle());
return true;
}
System.out.println("Книга не найдена или уже выдана");
return false;
}
public boolean returnBook(String title) {
Book book = findBook(title);
if (book != null && !book.isAvailable()) {
book.setAvailable(true);
System.out.println("Возвращена: " + book.getTitle());
return true;
}
System.out.println("Книга не найдена или уже в библиотеке");
return false;
}
public void listBooks() {
System.out.println("=== " + name + " ===");
for (Book book : books) {
book.display();
}
System.out.println("Всего книг: " + books.size());
}
}
// Файл: com/library/Main.java
package com.library;
import com.library.model.Book;
import com.library.service.Library;
public class Main {
public static void main(String[] args) {
Library lib = new Library("Городская библиотека");
lib.addBook(new Book("Война и мир", "Толстой"));
lib.addBook(new Book("Преступление и наказание", "Достоевский"));
lib.addBook(new Book("Мастер и Маргарита", "Булгаков"));
lib.listBooks();
lib.checkoutBook("Война и мир");
lib.listBooks();
lib.returnBook("Война и мир");
lib.listBooks();
}
}
Wildcard-импорты
Вместо импорта каждого класса по отдельности можно импортировать все классы из пакета через *:
// По одному:
import java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
// Или всё сразу:
import java.util.*;
Wildcard-импорты удобны, но имеют недостатки:
- Непонятно, какие именно классы используются
- Может возникнуть конфликт имён
- Немного замедляет компиляцию (но незначительно)
Рекомендация: импортируйте конкретные классы. Используйте * только для пакетов с множеством мелких классов (JUnit, Mockito).
Автоматический импорт
Некоторые пакеты импортируются автоматически:
java.lang.*— String, System, Math, Integer и др.java.lang.Object— базовый класс всех объектов
Поэтому вы можете писать System.out.println() без импорта — класс System в пакете java.lang.
Правила организации
- Один public-класс на файл
- Имя файла = имя public-класса +
.java - Пакет объявляется первой строкой
- Потом идут import-ы
- Потом класс
- Порядок в файле: package → import → class
// Порядок:
package com.company.model; // 1. Пакет
import java.util.List; // 2. Импорты
import java.util.ArrayList;
public class User { // 3. Класс
// тело класса
}
Типичные ошибки
// Ошибка 1: package не совпадает с папкой
// Файл: User.java (в корне)
package com.company.model; // Ошибка! Папка не com/company/model
// Ошибка 2: забыли импорт
package com.company.service;
// import com.company.model.User; // Забыли!
public class UserService {
User user; // Ошибка! User не импортирован
}
// Ошибка 3: неверный порядок
import java.util.List; // Ошибка! Сначала package
package com.company.service; // package должен быть первым
// Правильно:
package com.company.service;
import java.util.List;
Итоги урока
- Пакеты = папки для классов (организация + пространство имён).
package— первая строка в файле.importимпортирует классы из других пакетов.- FQN = полное имя класса включая пакет.
- Соглашение:
com.company.project.module. import staticимпортирует статические成员.- Структура: model, service, repository, controller, util.
- Порядок в файле: package → import → class.
Следующий модуль: Наследование и полиморфизм!
Тест: Пакеты и организация кода
10 вопросов