Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии 


Тема 3.17 Пакеты и ограничение доступа




Пакет (package) – это некий контейнер, ко­торый используется для того, чтобы изолиро­вать имена классов. Например, вы можете со­здать класс List, заключить его в пакет и не думать после этого о возможных конфликтах, которые могли бы возникнуть, если бы кто-ни­будь еще создал класс с именем List.

Все идентификаторы, которые мы до сих пор использовали в наших примерах, располагались в одном и том же пространстве имен (name space). Это означает, что нам во избежание конфликтных ситуаций при­ходилось заботиться о том, чтобы у каждого класса было свое уникаль­ное имя. Пакеты – это механизм, который служит как для работы с пространством имен, так и для ограничения видимости. У каждого файла .java есть 4 одинаковых внутренних части, из которых мы до сих пор ис­пользовали только одну. Ниже приведена общая форма исходного файла Java.

одиночный оператор package (необязателен)

любое количество операторов import (необязательны)

одиночное объявление открытого (public) класса

любое количество закрытых (private) классов пакета (необязательны)

Первое, что может появиться в исходном файле Java – это оператор package, который сообщает транслятору, в каком пакете должны опре­деляться содержащиеся в данном файле классы. Пакеты задают набор раздельных пространств имен, в которых хранятся имена классов. Если оператор package не указан, классы попадают в безымянное пространст­во имен, используемое по умолчанию. Если вы объявляете класс, как принадлежащий определенному пакету, например,

package java.awt.image;

то и исходный код этого класса должен храниться в каталоге java/awt/image.

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

Представьте себе, что вы написали класс с именем PackTest в пакете test. Вы создаете каталог test, помещаете в этот каталог файл PackTest.Java и транслируете. Пока – все в порядке. Однако при попытке запустить его вы получаете от интерпретатора сообщение «can't find class PackTest» («He могу найти класс PackTest»). Ваш новый класс теперь хранится в пакете с именем test, так что теперь надо указывать всю иерархию пакетов, разделяя их имена точками - test.PackTest.

После оператора package, но до любого определения классов в исход­ном Java-файле, может присутствовать список операторов import. Паке­ты являются хорошим механизмом для отделения классов друг от друга, поэтому все встроенные в Java классы хранятся в пакетах. Общая форма оператора import такова:

import пакет1 [.пакет2].(имякласса);// один класс

import пакет1 [.пакет2].*; //все классы пакета

Здесь пакет1 – имя пакета верхнего уровня, пакет2 – это необя­зательное имя пакета, вложенного в первый пакет и отделенное точкой. И, на­конец, после указания пути в иерархии пакетов, указывается либо имя класса, либо метасимвол звездочка. Звездочка означает, что, если Java-транслятору потребуется какой-либо класс, для которого пакет не указан явно, он должен просмотреть все содержимое пакета со звездочкой вмес­то имени класса.

Но использовать без нужды форму записи оператора import с использованием звездочки не рекомендуется, т.к. этоможет значительно увеличить время трансляции кода (на скорость работы и размер программы это не влияет).

Все встроенные в Java классы, которые входят в комплект поставки, хранятся в пакете с именем java. Базовые функции языка хранятся во вложенном пакете java.lang. Весь этот пакет автоматически импортируется транслятором во все программы. Это эквивалентно размещению в начале каждой программы оператора

import java.lang.*;

Если в двух пакетах, подключаемых с помощью формы оператора im­port со звездочкой, есть классы с одинаковыми именами, однако вы их не используете, транслятор не отреагирует. А вот при попытке исполь­зовать такой класс, вы сразу получите сообщение об ошибке, и вам при­дется переписать операторы import, чтобы явно указать, класс какого пакета вы имеете ввиду.



Ограничение доступа

Java предоставляет несколько уровней защиты, обеспечивающих воз­можность тонкой настройки области видимости данных и методов. Из-за наличия пакетов Java должна уметь работать еще с четырьмя категориями видимости между элементами классов :

• Подклассы в том же пакете.

• Не подклассы в том же пакете.

• Подклассы в различных пакетах.

• Классы, которые не являются подклассами и не входят в тот же пакет.

В языке Java имеется три уровня доступа, определяемых ключевыми словами: private (закрытый), public (открытый) и protected (защищен­ный), которые употребляются в различных комбинациях. Содержимое ячеек таблицы определяет доступность переменной с данной комбинацией модификаторов (столбец) из указанного места (строка).

 

Таблица 3.2 – Уровни доступа

    private модификатор отсутствует private protected protected public
тот же класс да да да да да
подкласс в том же пакете нет да да да да
независимый класс в том же пакете нет да нет да да
подкласс в другом пакете нет нет да да да
независимый класс в другом пакете нет нет нет нет да

 

На первый взгляд все это может показаться чрезмерно сложным, но есть несколько правил, которые помогут вам разобраться. Элемент, объ­явленный public, доступен из любого места. Все, что объявлено private, доступно только внутри класса, и нигде больше. Если у элемента вообще не указан модификатор уровня доступа, то такой элемент будет виден из подклассов и классов того же пакета. Именно такой уровень доступа используется в языке Java по умолчанию. Если же вы хотите, чтобы элемент был доступен извне пакета, но только подклассам того класса, которому он принадлежит, вам нужно объявить такой элемент protected. И наконец, если вы хотите, чтобы элемент был доступен только под­классам, причем независимо от того, находятся ли они в данном пакете или нет, то используйте комбинацию private protected.

 

Тема 3.18 Внутренние классы

 

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

В Java можно определить (вложить) один класс внутри определения другого класса, что позволяет группировать классы, логически связанные друг с другом, и динамично управлять доступом к ним. С одной стороны, обоснованное использование в коде внутренних классов делает его более эффектным и понятным. С другой стороны, применение внутренних классов есть один из способов сокрытия кода, так как внутренний класс может быть абсолютно недоступен и не виден вне класса-владельца. Внутренние классы также исполь­зуются в качестве блоков прослушивания событий (глава «События»). Одной из важнейших причин использования внутренних классов является возможность независимого наследования внутренними классами. Фактически при этом реализуется множественное наследование со своими преимуществами и проблемами.

В качестве примеров можно рассмотреть взаимосвязи классов «Корабль», «Двигатель» и «Шлюпка». Объект класса «Двигатель» расположен внутри (невидим извне) объекта «Корабль» и его деятельность приводит «Корабль» в движение. Оба этих объекта неразрывно связаны, то есть запустить «Двигатель» можно только посредством использования объекта «Корабль», например, из машинного отделения. Таким образом, перед инициализацией объекта внутреннего класса «Двигатель» должен быть создан объект внешнего класса «Корабль».

Класс «Шлюпка» также является логической частью класса «Корабль», однако ситуация с его объектами проще по причине того, что данные объекты могут быть использованы независимо от наличия объекта «Корабль». Объект класса «Шлюпка» только использует имя (на борту) своего внешнего класса. Такой внутренний класс следует определять как static. Если объект «Шлюпка» используется без привязки к какому-либо судну, то класс следует определять как обычный независимый класс.

Вложенные классы могут быть статическими, объявляемыми с модификатором static, и нестатическими. Статические классы могут обращаться к членам включающего класса не напрямую, а только через его объект. Нестатические внутренние классы имеют доступ ко всем переменным и методам своего внешнего класса-владельца.

 

3.18.1 Внутренние (inner) классы

Нестатические вложенные классы принято называть внутренними (inner) классами. Доступ к элементам внутреннего класса возможен из внешнего класса только через объект внутреннего класса, который должен быть создан в коде метода внешнего класса. Объект внутреннего класса всегда ассоциируется (скрыто хранит ссылку) с создавшим его объектом внешнего класса – так называемым внешним (enclosing) объектом. Внешний и внутренний классы приведены в листинге 3.30.

Листинг 3.30

public class Ship {

// поля и конструкторы

// abstract, final, private, protected - допустимы

public class Engine { // определение внутреннего класса

// поля и методы

public void launch() {

System.out.println("Запуск двигателя");

}

} // конец объявления внутреннего класса

public void init() { // метод внешнего класса

// объявление объекта внутреннего класса

Engine eng = new Engine();

eng.launch();

}

}

При таком объявлении объекта внутреннего класса Engine в методе внешнего класса Ship нет реального отличия от использования какого-либо другого внешнего класса, кроме объявления внутри класса Ship. Использование объекта внутреннего класса вне своего внешнего класса возможно только при наличии доступа (видимости) и при объявлении ссылки в виде:

Ship.Engine obj = new Ship().new Engine();

Основное отличие от внешнего класса состоит в больших возможностях ограничения видимости внутреннего класса по сравнению с обычным внешним классом. Внутренний класс может быть объявлен как private, что обеспечивает его полную невидимость вне класса-владельца и надежное сокрытие реализации. В этом случае ссылку obj, приведенную выше, объявить было бы нельзя. Создать объект такого класса можно только в методах и логических блоках внешнего класса. Использование protected позволяет получить доступ к внутреннему классу для класса в другом пакете, являющегося суперклассом внешнего класса.

После компиляции объектный модуль, соответствующий внутреннему классу, получит имя Ship$Engine.class.

Методы внутреннего класса имеют прямой доступ ко всем полям и методам внешнего класса, в то же время внешний класс может получить доступ к содержимому внутреннего класса только после создания объекта внутреннего класса. Внутренние классы не могут содержать статические атрибуты и методы, кроме констант (final static). Внутренние классы имеют право наследовать другие классы, реализовывать интерфейсы и выступать в роли объектов наследования. Допустимо наследование следующего вида:

public class WarShip extends Ship {

protected class SpecialEngine extends Engine {

}

}

Если внутренний класс наследуется обычным образом другим классом (после extends указывается ИмяВнешнегоКласса.ИмяВнутреннегоКласса), то он теряет доступ к полям своего внешнего класса, в котором он был объявлен.

public class Motor extends Ship.Engine {

public Motor(Ship obj) {

obj.super();

}

}

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

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

При объявлении внутреннего класса могут использоваться модификаторы final, abstract, private, protected, public.

 

3.18.2Вложенные (nested) классы

Если не существует необходимости в связи объекта внутреннего класса с объектом внешнего класса, то есть смысл сделать такой класс статическим.

Вложенный класс логически связан с классом-владельцем, но может быть использован независимо от него.

При объявлении такого внутреннего класса присутствует служебное слово static, и такой класс называется вложенным (nested). Если класс вложен в интерфейс, то он становится статическим по умолчанию. Такой класс способен наследовать другие классы, реализовывать интерфейсы и являться объектом наследования для любого класса, обладающего необходимыми правами доступа. В то же время статический вложенный класс для доступа к нестатическим членам и методам внешнего класса долженсоздавать объект внешнего класса, а напрямую имеет доступ только к статическим полям и методам внешнего класса. Для создания объекта вложенного класса объект внешнего класса создавать нет необходимости. Подкласс вложенного класса не способен унаследовать возможность доступа к членам внешнего класса, которыми наделен его суперкласс.

Листинг 3.31

public class Ship {

private int id;

// abstract, final, private, protected - допустимы

public static class LifeBoat {

public static void down() {

System.out.println("шлюпки на воду!");

}

public void swim() {

System.out.println("отплытие шлюпки");

}

}

}

public class Main {

public static void main(String[] args) {

// вызов статического метода

Ship.LifeBoat.down();

// создание объекта статического класса

Ship.LifeBoatlf = newShip.LifeBoat();

// вызов обычного метода

lf.swim();

}

}

Статический метод вложенного класса вызывается при указании полного относительного пути к нему. Объектlf вложенного класса создается с использованием имени внешнего класса без вызова его конструктора.

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

 

3.18.3Анонимные (anonymous) классы

Анонимные (безымянные) классы применяются для придания уникальной функциональности отдельно взятому объекту для обработки событий, реализации блоков прослушивания и т.д. Можно объявить анонимный класс, который будет расширять другой класс или реализовывать интерфейс при объявлении одного, единственного объекта, когда остальным объектам этого класса будет соответствовать реализация метода, определенная в самом классе. Объявление анонимного класса выполняется одновременно с созданием его объекта посредством оператора new.

Анонимные классы эффективно используются, как правило, для реализации (переопределения) нескольких методов и создания собственных методов объекта. Этот прием эффективен в случае, когда необходимо переопределение метода, но создавать новый класс нет необходимости из-за узкой области (или одноразового) применения метода.

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

Листинг 3.32

public class TypeQuest {

private int id = 1;

public TypeQuest() {

}

public TypeQuest(int id) {

this.id = id;

}

public void addNewType() {

// реализация

System.out.println("добавленвопроснасоответствие");

}

}

public class Main {

public static void main(String[] args) {

TypeQuest unique = new TypeQuest() {// анонимный класс #1

publicvoidaddNewType() {

// новая реализация метода

System.out.println("добавлен вопрос со свободным ответом");

}

};// конец объявления анонимного класса

unique.addNewType();

new TypeQuest(71) {// анонимный класс #2

private String name = "Drag&Drop";

publicvoidaddNewType() {

// новая реализация метода #2

System.out.println("добавлен " + getName());

}

String getName() {

return name;

}

}.addNewType();

TypeQuest standard = new TypeQuest(35);

standard.addNewType();

}

}

В результате будет выведено:

добавлен вопрос со свободным ответом

добавлен Drag&Drop

добавлен вопрос на соответствие

При запуске приложения происходит объявление объекта uniquec применением анонимного класса, в котором переопределяется метод addNewType(). Вызов данного метода на объекте unique приводит к вызову версии метода из анонимного класса, который компилируется в объектный модуль с именем RunnerAnonym$1.

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

При помощи анонимных классов удобно реализовувать интерфейс. Пример реализации методов интерфейса приведен в листинге 3.33.

Листинг 3.33

// интерфейс OnOff

public interface OnOff {

public void on();

public void off();

}

 

public class Main {

public static void main(String[] args) {

// реализация методдов для объекта radio

OnOffradio =newOnOff() {

@Override

public void on() {

System.out.println("Радиовключено");

}

 

@Override

public void off() {

System.out.println("Радиовыключено");

}

};

// реализация методдов для объекта tv

OnOfftv =newOnOff() {

@Override

public void on() {

System.out.println("Телевизорвключен");

}

@Override

public void off() {

System.out.println("Телевизорвыключен");

}

};

radio.on();

radio.off();

tv.on();

tv.off();

}

}

В результате рабботы программы получим:

Радио включено

Радио выключено

Телевизор включен

Телевизор выключен

 

Выводы к главе:

· Наследование – один из трех базовых принципов объектно-ориентированного программирования.

· В Java нет множественного наследования классов.

· С помощью наследования можно сформировать общий класс, определяющий характерные особенности некоторого понятия.

· В языке Java наследуемый класс принято называть суперклассом. Его дочерние классы называются подклассами.

· Подкласс – это специализированная версия суперкласса. Он наследует все переменные и методы, определенные в суперклассе, и дополняет их своими элементами.

· Абстрактный метод – метод имеющий заголовок, но не имеющий реализации(тела).

· Если в классе есть хотя бы один абстрактный метод, то класс становится абстрактным.

· Интерфейс не содержит реализации ни одного метода. Он описывает, что должно быть сделано, но не поясняет, как.

· Если интерфейс определен, его можно реализовать в сколь угодно большом количестве классов.

· Один класс может реализовать любое количество интерфейсов.

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

· Пакет– это контейнер, позволяющий разделить пространство имен классов.

· В разных пакетах могут быть классы с одинаковыми именами, в одном – нет.

· Классы могут взаимодействовать друг с другом не только посредством наследования и использования ссылок, но и посредством организации логической структуры с определением одного класса в теле другого.

· Статический класс описанный в теле другого класса – вложенный.

· Нестатические вложенные классы принято называть внутренними (inner) классами.

· Анонимный класс расширяет другой класс или реализует интерфейс при объявлении одного, единственного объекта, когда остальным объектам этого класса будет соответствовать реализация методов, определенная в самом классе. Объявление анонимного класса выполняется одновременно с созданием его объекта посредством оператора new.

Задания к главе:

1). Разработать интерфейс Арифметика. Методы – сложение, сравнение.Класс Матрица (поле – двумерный массив) реализует интерфейс. Дополнительные методы:

создание матрицы (размерность и значения вводятся с консоли),

заполенение матрицы случайными числами,

вывод 1 значения из матрицы, по номеру ячейки,

масштабирование матрицы в меньшую сторону (поэлементное деление на число).

Класс Строки (поле – массив символов char) реализует интерфейс.Дополнительные методы:

создание строки (ввод с консоли),

вывод 1 значение из строки, по номеру.

Класс Вектор (как набор значений, поле – одномерный массив) реализует интерфейс. Дополнительные методы:

создание вектора (ввод с консоли),

сравнение длин двух векторов.

2). Создать интерфейс с методом площадь и реализовать его в классах: прямоугольник, круг, прямоугольный треугольник, трапеция со своими.

Для проверки определить массив ссылок на интерфейс, которым присваиваются различные объекты.

Площадь трапеции:S=(a+b)h/2

3). Создать интерфейс с методом нормаи реализовать его в классах: комплексные числа, вектор из 10 элементов, матрица (2х2). Определить метод нормы:

для комплексных чисел – модуль в квадрате,

для вектора – корень квадратный из суммы элементов по модулю,

для матрицы – максимальное значение по модулю.

4). Реализовать интерфейсы, а также наследование и полиморфизм для следующих классов:

4.1). interface Абитуриент <- abstractclass Студент <- class Студент-Заочник.

4.2).interface Сотрудник <- class Инженер <-class Руководитель.

4.3). interface Здание <- abstractclass Общественное Здание <- class Театр.

4.4). interface Корабль <- abstractclass Военный Корабль <- class Авианосец.

4.5). interface Корабль <- class Грузовой Корабль <- class Танкер.

4.6).interface Техника<- abstract class Плеер<- class Видеоплеер.

4.7). interface Транспортное Средство <- abstractclass Общественный Транспорт <- class Трамвай.





Рекомендуемые страницы:


Читайте также:



Последнее изменение этой страницы: 2016-05-30; Просмотров: 411; Нарушение авторского права страницы


lektsia.com 2007 - 2021 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.053 с.) Главная | Обратная связь