Пакеты, модификаторы конструкторов и классов

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

Начнем с конструкторов. Если не указан ни один из модификаторов, то, как мы говорили, используется модификатор по умолчанию и конструктор доступен из любого другого класса в пределах пакета (о пакетах мы еще будем говорить). Если же нам нужно описать класс так, чтобы его можно было создавать и в других пакетах, то перед его именем обязательно следует прописать модификатор public:

class Rect {
    private int x1, y1;
    private int x2, y2;
 
    public Rect() {}
 
    public Rect(int x1, int y1, int x2, int y2) {
        this.x1 = x1; this.y1 = y1;
        this.x2 = x2; this.y2 = y2;
    }
}

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

private Rect() {}

Смотрите, теперь объект класса Rect можно создать только через второй публичный конструктор, передавая четыре аргумента. Первый конструктор больше недоступен и создать экземпляр класса без аргументов уже нельзя. Это часто используется на практике, когда один из конструкторов используется исключительно для начальной обработки данных и разработчик класса предполагает его вызов только из других конструкторов этого же класса. Например, так:

    public Rect(int x1, int y1, int x2, int y2) {
        this(); // вызов приватного конструктора Rect()
        this.x1 = x1; this.y1 = y1;
        this.x2 = x2; this.y2 = y2;
    }

Вот так можно управлять способом создания объектов через модификаторы доступа у конструкторов.

Давайте теперь посмотрим, как влияют модификаторы при определении классов. Опять же, если перед классом нет никакого модификатора, то используется модификатор по умолчанию. И в нашем примере класс Rect имеет такой модификатор. Попробуем для начала прописать модификатор private. Смотрите, при выполнении программы выдается сообщение, что такой модификатор для внешнего класса недопустим. И действительно, ключевое слово private означает сокрытие данных внутри класса, а у нас класс записан как самостоятельная конструкция. Но, если бы класс был вложен в другой класс, например, вот так:

public class Main {
    …
    private class Point {
        int x, y;
    }
}

то такой модификатор бы уже имел смысл и обращение к Point было бы возможно только внутри класса Main.

Давайте теперь у класса Rect пропишем модификатор public:

public class Rect {
...
}

Снова возникает ошибка. Она связана с тем, что согласно спецификации языка Java каждый публичный (открытый) класс должен быть объявлен в своем отдельном файле. Причем, название файла должно совпадать с названием класса. Создадим новый файл с именем Rect.java в том же каталоге javacourse, что и файл Main.java и перенесем туда определение класса Rect. Теперь, как видите, все работает без проблем. Вот это следует иметь в виду при объявлении новых классов. Как правило, каждый из них прописывается в своем отдельном файле с модификатором public, а затем, используется в любом другом классе текущего пакета. Кстати, в нашем случае, модификатор public  у класса Rect можно убрать и все тоже будет работать, т.к. оба файла: Main.java и Rect.java находятся в одном пакете.

Пакеты

Я уже много раз произнес это слово «пакет» и пора конкретизировать, что это такое и как с ними работать. Как правило, пакет – это файловый каталог, где располагаются файлы с определениями классов языка Java. Например, текущая программа относительно каталога src расположена в подкаталоге:

com/javacourse/

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

com.javacourse

Именно это указывается после директивы package в каждом файле этого пакета:

package com.javacourse;

То есть, эта строчка говорит компилятору, что текущий файл (и все что в нем определено) принадлежит пакету com.javacourse. Поэтому в нашем проекте оба класса Main и Rect относятся к одному и тому же пакету.

Ну, хорошо, но зачем все-таки нужны эти пакеты? Почему бы просто не хранить все файлы в одном каталоге проекта и не «заморачиваться» с этими пакетами? По сути, они решают одну важную задачу: устранения конфликтов имен между классами, интерфейсами и другими возможными конструкциями языка Java. Что это за конфликт имен? Смотрите, если какой-то большой проект выполняют несколько программистов независимо друг от друга, то с высокой вероятностью возможна ситуация, когда они будут использовать одинаковые имена классов. И потом, то привело бы к ошибке при сборке всего проекта. Но этого легко избежать, если каждый программист будет выполнять работу в рамках своего пакета, имя которого задаст ответственный исполнитель. Это основная причина применения пакетов в Java.

Давайте в рамках нашего проекта создадим еще один пакет. С помощью интегрированной среды сделать это очень просто. Я щелкну правой кнопкой мыши на каталоге src и в меню выберу опцию «Package». В появившемся окне введу название пакета, допустим, example, в результате чего появится новый каталог с таким именем. Далее, я щелкну снова правой кнопкой мыши уже по этому каталогу и выберу пункт «New -> Java Class» для создания нового класса внутри этого пакета. Введу имя класса, допустим, MyClass и среда автоматически создаст файл MyClass.java с начальным определением класса MyClass. Причем, вначале этого файла будет записана директива

package example;

указывающая, что файл принадлежит пакету example.

Чтобы теперь воспользоваться классом MyClass из пакета example, в файле Main.java пакета com.javacourse следует воспользоваться директивой import:

import example.*;

Она разрешает использование всех классов, определенных в пакете example. И, далее, в функции main() мы можем создать экземпляр класса MyClass:

MyClass c1 = new MyClass();

Если строчку с импортом поместить в комментарии, то доступ к классу MyClass возможен только с явным указанием пакета, в котором он расположен:

example.MyClass c1 = new example.MyClass();

Фактически, директива import объединяет классы пакета example с текущим файлом Main.java. Если же нам нужно импортировать только какой-то один класс из пакета, то вместо звездочки следует прописать этот класс:

import example.MyClass;

Другие классы (если они есть) импортированы не будут. Наконец, смотрите, если у класса MyClass убрать модификатор public, то будет применен модификатор доступа по умолчанию и это приведет к ошибке при импорте этого класса в другом пакете. А вот класс Rect без модификатора public импортируется без проблем, т.к. находится в том же пакете, что и класс Main. Вот в этом ключевое отличие модификатора public для классов от модификатора по умолчанию.

Путь кодера

Подвиг 1. Объявите класс для хранения информации по телевизорам с полями: марка, размер диагонали, год производства, цена. Доступ к данным реализовать через сеттеры и геттеры. Определить минимум два конструктора: первый должен только инициализировать данные, другие – создавать новый объект с разными аргументами. Запретить создавать объект без аргументов.

Подвиг 2. В отдельном файле текущего пакета объявить класс для описания шкафов с полями: габариты, тип шкафа, цена, производитель, год производства. Прописать геттеры и сеттеры для доступа к полям класса. Добавить конструкторы для создания объектов. Запретить создавать объект без указания аргументов. Добавить метод для вычисления объема шкафа по его габаритам. В основном классе (в функции main()) создать несколько экземпляров класса шкафов и вывести в консоль их габариты и объемы.

Подвиг 3. Создать новый пакет и определить в нем класс Chair для описания стульев с полями: габариты, цена, производитель, год производства. Доступ к данным реализовать через сеттеры и геттеры. Добавить метод вычисления объема стула по его габаритам. В основном классе проекта (в функции main()) импортировать этот класс, создать несколько его экземпляров и вывести габариты и объемы стульев в консоль.

Видео по теме