На предыдущем
занятии мы с вами рассмотрели модификаторы доступа у переменных класса и его
методов, используя ключевые слова 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 можно создавать
всюду без ограничений. Давайте теперь один из конструкторов сделаем приватным,
закрытым и посмотрим что из этого получится:
Смотрите, теперь
объект класса 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:
Она разрешает
использование всех классов, определенных в пакете example. И, далее, в функции main() мы можем
создать экземпляр класса MyClass:
MyClass c1 = new MyClass();
Если строчку с
импортом поместить в комментарии, то доступ к классу MyClass возможен только
с явным указанием пакета, в котором он расположен:
example.MyClass c1 = new example.MyClass();
Фактически,
директива import объединяет
классы пакета example с текущим файлом Main.java. Если же нам
нужно импортировать только какой-то один класс из пакета, то вместо звездочки
следует прописать этот класс:
Другие классы
(если они есть) импортированы не будут. Наконец, смотрите, если у класса MyClass убрать
модификатор public, то будет
применен модификатор доступа по умолчанию и это приведет к ошибке при импорте
этого класса в другом пакете. А вот класс Rect без
модификатора public импортируется
без проблем, т.к. находится в том же пакете, что и класс Main. Вот в этом
ключевое отличие модификатора public для классов от модификатора по
умолчанию.
Путь кодера
Подвиг 1. Объявите класс
для хранения информации по телевизорам с полями: марка, размер диагонали, год
производства, цена. Доступ к данным реализовать через сеттеры и геттеры.
Определить минимум два конструктора: первый должен только инициализировать
данные, другие – создавать новый объект с разными аргументами. Запретить
создавать объект без аргументов.
Подвиг 2. В отдельном
файле текущего пакета объявить класс для описания шкафов с полями: габариты, тип
шкафа, цена, производитель, год производства. Прописать геттеры и сеттеры для
доступа к полям класса. Добавить конструкторы для создания объектов. Запретить
создавать объект без указания аргументов. Добавить метод для вычисления объема
шкафа по его габаритам. В основном классе (в функции main()) создать
несколько экземпляров класса шкафов и вывести в консоль их габариты и объемы.
Подвиг 3. Создать новый
пакет и определить в нем класс Chair для описания стульев с полями:
габариты, цена, производитель, год производства. Доступ к данным реализовать
через сеттеры и геттеры. Добавить метод вычисления объема стула по его
габаритам. В основном классе проекта (в функции main()) импортировать
этот класс, создать несколько его экземпляров и вывести габариты и объемы
стульев в консоль.