Для сохранения прогресса вступите в курс. Войти или зарегистрироваться.

alt text Это базовая тема. Двигаться дальше, к следующему уроку, стоит только после вдумчивого понимания этой темы. Смелее задавайте вопросы в обсуждениях и не ленитесь читать уже заданные вопросы - возможно там уже есть для вас ответ или подсказка :)


Что такое пакеты?

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

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

Как аналогию, можно привести пример с адресами.

Представим себя в роли некого класса, который выполняет некие функции для этого мира. Для того что бы этот мир к нам обратился - нам нужно иметь конкретный адрес, путь по которому можно будет взаимодействовать с нами. Пускай это будет адрес, например:

package   страна.область.город.район.улица.дом
         //кореневой пакет/подпакет/подпакет/подпакет/...

Далее мы рассмотрим как работает этот механизм в мире java.

как создавать директори в терминале

Необходимо использовать команду mkdir. О ее ключах можно почитать с помощью этой же команды с таким ключом: mkdir --h. В интернетах можно найти более деталые примеры применения этой команды.

Пример поэтапного создния папок:

UserName BASH ~
$ mkdir projectName
$ cd projectName
UserName BASH ~/projectName/
$ mkdir src
$ mkdir out
$ cd src
UserName BASH ~/projectName/src
$ mkdir io
$ cd io
UserName BASH ~/projectName/src/io
$ mkdir hexlet
$ cd hexlet
UserName BASH ~/projectName/src/io/hexlet
$ mkdir xo
$ cd xo
UserName BASH ~/projectName/src/io/hexlet/xo

И так далее, пока не будет создана необходимая структура папок для классов.

Или просто в одну строку команда:

mkdir -p projectName/{src,out}/io/hexlet/xo/{controllers,model,view}

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

Что такое пакет для класса

Для класса его пакет -- это местополежение его в проекте относительно других классов (адрес). Что влияет на доступность полей и методов между классами не только за счет модификаторов доступа private, default, protected, public но и благодаря "расстоянию" между классами. Под расстоянием, в данном случае, подразумевается какой путь по папкам нужно пройти от одного класса к другому.

Как создать класс внутри пакета?

По сути нам нужно создать текстовый файл с расширением java в нужной папке.

Сначала пишем имя любимого редактора в терминале, потом существующий путь, а потом имя будущего текстового файла. Пример для редактора nano:

UserName BASH ~/projectName/src
$ nano -f /io/hexlet/xo/view/ConsoleView.java

Создать сам файл можно и любым другим удобным для вас способом.

Далее уже наполняем файл кодом и сохраняем.

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

  • класс должен лежать внутри папки, в которой он нужен
  • внутри класса должно использоваться ключевое слово package
  • ключевое слово package должно быть в первой строке кода
  • после package нужно указать полное имя пакета, с корня до пакета(папки) в которой размещен класс.

Корень пакетов

А как же задать корневой каталог (папку) как основу пакетов, в котором уже и происходит ветвление этих всех подпакетов (подпапок)? Почему, например, папка io является корнем для классов проекта, а папка src и выше -- не является (смотри пример выше)?

Все очень просто: корневым пакетом считается тот который первым прописан в каждом java-файле после слова package. А в самом проекте по мимо папки src на ее уровне и выше -- могут быть много различных папок и файлов для работы с проектом в целом. Но это не делает их корневым пакетом, или вообще частью пакетов java в этом проекте, если они не включены в имена пакетов в классах после ключевого слова package. Пример структуры папок и папкетов:

-/projectName // Папка проекта. Может содержать кучу всякой всячины. 
            README.md // файл с описанием. В пакет никак не входит, но входит в проект.
            +/out // Папка для скомпилированных файлов *.class    
            -/src // Папка для исходных файлов *.java
                -/io
                    StartClass.java // package io;
                   -/hexlet
                           OneMoreClass.java // package io.hexlet;
                          -/xo
                             -/controllers
                                 ControllerOne.java // package io.hexlet.xo.controllers;
                                 Controller2.java   // package io.hexlet.xo.controllers;  
                             -/model
                                 ModelClaas1.java  // package io.hexlet.xo.model;
                                 ModelClass2.java  // package io.hexlet.xo.model;
                             -/view
                                 SomeView.java  // package io.hexlet.xo.view;

Пример содержимого класса OneMoreClass.java:

package io.hexlet;

class OneMoreClass {
    // тут некий код, переменные или просто пустота.
}

Пример содержимого класса SomeView.java:

package io.hexlet.xo.view;

class SomeView {
    // просто метод для дальнейших примеров.
    public void message() {
        System.out.println("Some very important message!");
    }
}

Как видно из примеров выше -- любой класс в этом проекте всегда содержит, в имени пакета на первом месте, папку io. Это и будет корневым пакетом проекта. На месте папки io могла быть папка com или любое другое имя. Важно что бы все классы проекта указывали на эту папку как на первую после ключевого слова package. Если хоть у одного класса в этом проекте папка io не будет прописана -- то такой класс как бы и не в проекте, а просто мусор в папке.

package класса всегда:

  1. должен начинаться с одинакового имени каталога, общего для всех классов проекта
  2. точно соответствует пути по которому лежит класс, начиная с имени корневого пакета
  3. не сожержит в себе имя самого класса и любого другого класса.

Имена пакетов

  • имя состоит из одного слова (желательно)
  • без заглавных букв (желательно)

Совокупность имен подпакетов делает проект уникальным, не похожим на миллионы других. Даже если в каждом проекте в мире 100% будет класс Main.java -- то они маловероятно пересекутся и не помешают друг-другу. Но даже если пересекуться - это решаемо, рассмотрим это в дальнейших примерах.


Применение пакетов

Как уже было сказано выше - пакет можно сравнить с адресом. Воспользуемся все тем же примером ветвления папок(пакетов). И попробуем применить к задачке.

Допустим в классе StartClass.java нам нужно создать экземпляр сласса SomeView.java, для использования его метода. Как это можно реализовать:

package io;

class StartClass {
    // создаем объект класса SomeView
    io.hexlet.xo.view.SomeView someView = new io.hexlet.xo.view.SomeView();

    public static void main(String... args) {
        neededController.message() // на экран выведется: "Some very important message!"
    }
}

Только благодаря прописанному пути к класу SomeView, класс StartClass может вообще узнать о его существовании.

Но прописывание таких длинных путей -- просто не удобно. Представляете если бы нам приходилось создавать подобный объект раз так 10 в этом коде?! Ужас. И по этому в java есть такое понятие как импортирование.

Импортирование пакетов

Для того что бы не писать длинные пути к каждому конкретному классу при каждом его использовании -- нужно сделать импорт только один раз этого класса в нуждающемся классе, в которм мы собираемся применять импортируемый класс. Для этого применяется ключевое слово import. Импорты пишутся между строчкой package ... и строчкой объявления класса. Перепишем предыдущий пример с применением ключевого слова import:

package io;

import io.hexlet.xo.view.SomeView; // Расширение файла в конце не пишем!!!

class StartClass {
    // создаем объект класса SomeView
    SomeView someView = new SomeView(); // имя класса доступно без имени пакета

    public static void main(String... args) {
        neededController.message() // на экран выведется: "Some very important message!"
    }
}

Представим, что в пакете io.hexlet.xo.view очень много разных классов. И они нам все нужны тут в классе StartClass, как их все разом заимпортировать?

Решение приходит со звездочкой *.

Перепишем предыдущий пример с импортированием любых классов из пакета io.hexlet.xo.view:

package io;

import io.hexlet.xo.view.*; // все классы из пакета импортированы

class StartClass {
    // создаем объект класса SomeView
    SomeView someView = new SomeView(); // имя класса доступно без имени пакета

    public static void main(String... args) {
        neededController.message() // на экран выведется: "Some very important message!"
    }
}

Стоит обратить внимание, что когда в классе много импортов не только из текущего проекта, но и из других проектов и библиотек -- могут возникнуть коллизии в обращении к импортированным классам. Например, если в разных библиотеках есть свой класс SomeView и они оба заимпортированны "за компанию" массово через * -- то Java не будет знать какой класс использовать при вызове эго по имени... Их же больше одного.

В такой ситуации придется прописывать явный полный путь при каждом обращении к имени класса или поштучно импортировать все необходимые классы без применения импорта всего пакета посредством *.

Импортирование по умолчанию

Не зависимо от того, написал-ли программист вообще слово import перед определением своего класса, -- java всегда автоматически не явно импортирует в текущий класс два пакета:

  1. java.lang.*;. В ручную в коде это можно прописать так: import java.lang.*; Это одна из стандартных библиотек java.
  2. Текущий пакет, в котором находится класс. В ручную это выглядело бы так: import . или import ./.

Обратите внимание на строку System.out.println("Some very important message!"); в классе SomeView:

package io.hexlet.xo.view;

class SomeView {
    // просто метод для дальнейших примеров.
    public void message() {
        System.out.println("Some very important message!");
    }
}

Класс SomeView видит класс System и его открытые методы благодаря стандартному импорту java.lang.*;, т.к. System входит в пакет java.lang.


Полезная дополнительная информация

Некоторые ключи к командам javac и java

  • -sourcepath -- этот ключ для javaс. За этим ключем следует указать путь к файлу с расширением .java. Или к множеству таких файлов.
  • -d -- этот ключ необходим для javaс команды. За этим ключем следует указать путь, куда будут сложены скомпилированные файлы. Если этот ключ и путь не указать компилятору - то он сложит все файлы компиляции рядом с исходными файлами.
  • -cp -- этот ключ нужен как для java так и для javac. За этим ключем следует указать путь к скомпилированным файлам с расширением .class. Для javac этот ключ необходим для подгрузки во время компиляции уже скомпилированных файлов из другого пректа.

Полезные ссылки:

Документация:

Группы для общения:

Мы учим программированию с нуля до стажировки и работы. Попробуйте наш бесплатный курс «Введение в программирование» или полные программы обучения по Node, PHP, Python и Java.

Хекслет

Подробнее о том, почему наше обучение работает →