Зарегистрируйтесь, чтобы продолжить обучение

Пакеты Java: Введение в ООП

Реальные программы на Java состоят из сотен и тысяч классов! Часть этих классов добавляется программистами проекта, но многие приходят вместе с библиотеками, используемыми внутри. Например, только библиотека Apache Commons Lang включает в себя около сотни классов.

Если быть точными, то речь идет и про классы и интерфейсы, но чтобы постоянно не повторяться, в этом уроке будут упоминаться только классы, хотя все это относится и к интерфейсам тоже.

При таких размерах легко возникают ситуации, когда разные программисты создают классы с одинаковыми именами. Если два таких класса окажутся внутри одного проекта, то он перестанет компилироваться. Иногда мы можем просто переименовать один из классов, но что если классы пришли из библиотек, которые мы используем? Здесь уже без вариантов.

Проблема конфликта имен актуальна для всех языков программирования и решение у всех примерно одинаковое. Для этого вводится система модулей, которая позволяет изолировать классы разных проектов, даже если у них одинаковые имена. В Java такая система называется пакетами.

Использование пакетов

Пакеты позволяют группировать похожие классы или даже отдельные проекты. Даже стандартная библиотека Java это не просто набор классов, это набор пакетов, внутри которых уже находятся классы. Например, встроенный пакет java.time содержит классы для работы с датой и временем:

// Импорт позволяет обращаться по прямому имени
import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.ChronoUnit;

public class App {
    public static void main(String[] args) {
        LocalDate dateFrom = LocalDate.of(2017, Month.MAY, 24);
        LocalDate dateTo = LocalDate.of(2017, Month.JULY, 29);
        long noOfDaysBetween = ChronoUnit.DAYS.between(dateFrom, dateTo);
        System.out.println(noOfDaysBetween);
    }
}

В коде выше импортируется три класса из пакета java.time. Причем один из них из вложенного пакета java.time.temporal. Импортирование позволяет обращаться к классу по его имени, иначе пришлось бы писать полное имя (fully qualified): java.time.LocalDate.of(...). Так тоже можно, но код получается захламленным, его сложнее читать.

Существует еще один способ импорта – с помощью *. Тогда код будет выглядеть немного по-другому:

import java.time.*;
import java.time.temporal.*;

public class App {
    public static void main(String[] args) {
        var dateFrom = LocalDate.of(2017, Month.MAY, 24);
        var dateTo = LocalDate.of(2017, Month.JULY, 29);
        var noOfDaysBetween = ChronoUnit.DAYS.between(dateFrom, dateTo);
        System.out.println(noOfDaysBetween);
    }
}

* импортирует весь пакет целиком, то есть внутри класса мы можем обратиться ко всем классам импортируемого пакета напрямую. Может показаться, что такой способ удобнее всего, так как не нужно импортировать каждый класс по отдельности. Но тогда возникает две проблемы:

  1. Импортируемые классы из разных мест могут пересечься по именам
  2. Непонятно, кто и откуда был импортирован

Поэтому актуальны все способы и конкретный вариант импорта зависит от ситуации. Правда, обычно об этом думать не надо, так как импорты проставляются редактором автоматически.

Помимо java.time в Java еще есть множество других пакетов, например java.nio, java.util, java.net и другие. А, поскольку язык развивается, то какие-то пакеты и классы устаревают и не рекомендуются к использованию. Не удивляйтесь наличию примерно одинаковых классов, которые делают примерно одно и тоже. Если какой-то класс или пакет устарел, об этом обязательно укажут в документации.

Встроенные пакеты легко распознать по имени, они все начинаются со слова java. Но пакеты бывают и пользовательские. Фактически весь остальной код приложений и библиотек находится внутри своих пакетов. И мы снова приходим к проблеме конфликтов имен, но здесь уже не все так страшно. Пакетов значительно меньше чем классов и, как правило, они содержат какой-то префикс, который закреплен за конкретной компанией или продуктом. Например, пакеты организации Apache начинаются с org.apache, то есть это имя домена в обратном порядке. Повторить такое название технически можно, но никто не будет сам себе стрелять в ногу.

org.apache это пакет apache вложенный в пакет org. Как не трудно догадаться имя org может быть использовано разными компаниями, например org.w3c. Такая ситуация допустима, так как классы лежат дальше, уже внутри вложенных пакетов, которые точно уникальны.

// Класс генерирующий рандомные числа из пакета org.apache.commons.lang3
// Установку этого пакета в систему мы рассматриваем в другом курсе
import org.apache.commons.lang3.RandomUtils;

public class App {
    public static void main(String[] args) {
        var x = RandomUtils.nextInt(1, 1000);
    }
}

Помимо разрешения конфликтов имен пакеты выполняют еще одну функцию — контроль доступа. По умолчанию все классы внутри пакета доступны только классам этого же пакета. Для доступа снаружи их нужно помечать публичными.

Определение пакетов

Структура пакетов связана со структурой директорий. Если пакет называется org.apache.commons.lang3, то путь к нему будет таким org/apache/commons/lang3. Имя пакета всегда соответствует директории, если пакет вложенный, то и директория тоже вложенная. В конце цепочки всегда файлы, в которых находятся классы. Их легко определить по названию, имена пакетов всегда идут с маленькой буквы, тогда как классов с заглавной. Пример создания пакета:

// io/hexlet/Course.java
package io.hexlet;

public class Course {
    // Тут логика
}

// com/google/App.java
package com.google;

import io.hexlet.Course;

public class App {
    public static void main(String args[]) {
        var course = new Course("Java");
    }
}

Классы находящиеся в одном пакете видят друг друга без импортов:

// io/hexlet/Lesson.java
package io.hexlet;

public class Lesson {
    private Course course;

    // остальной код
}

Здесь может возникнуть вопрос, а по какому принципу раскладывается код по пакетам и вложенным пакетам? Классы стараются группировать по смыслу, но в реальности все зависит от представлений о прекрасном у конкретного разработчика, который этим занимается. Со временем и у вас разовьется это чувство, когда появится насмотренность.

Статический импорт

Java позволяет импортировать и использовать статические методы и свойства без указания самого класса:

import static java.lang.System.*;

class App {
   public static void main(String args[]) {
      // Можем опустить System
      out.println("GeeksforGeeks");
   }
}

Дополнительные материалы

  1. Пакеты. Официальная документация

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
от 25 000 ₸ в месяц
Разработка приложений на языке Java
10 месяцев
с нуля
Старт 26 декабря

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»