8. Пакеты и интерфейсыtarasov/javalectures/tarasovvljavaand...8. Пакеты и...

32
8. Пакеты и интерфейсы Пакеты — это контейнеры для классов, которые используются для сохранения пространства имен классов. Например, класс с именем List, можно хранить в отдельном пакете, не опасаясь, что он столкнется с некоторым другим классом с именем List, хранящимся где-то в другом месте. Пакеты сохраняются иерархическим способом и явно импортируются в определения новых классов. Методы класса определяют интерфейс к данным в классе. С помощью ключевого слова interface Java позволяет полностью отделить интерфейс от его реализации. Используя интерфейс, можно определить набор методов, которые могут быть реализованы одним или несколькими классами. Сам интерфейс в действительности не определяет никакой реализации. Хотя интерфейсы подобны абстрактным классам, они имеют дополнительную возможность: класс может реализовывать более одного интерфейса. В противоположность этому класс может наследовать только один суперкласс (абстрактный или другой). Пакеты и интерфейсы — это два основных компонента Java- программы. В общем случае исходный файл Java может содержать любую (или все) из следующих четырех внутренних частей: □ одиночный package-оператор (не обязательно); □ любое число import-операторов (не обязательно); □ одиночное объявление общего класса (требуется); □ любое число частных классов пакета (не обязательно). До сих пор в примерах использовалась только одна из этих частей — одиночное объявление класса public. В этой главе рассмотрим остальные части. 8.1. Пакеты В примерах предшествующих глав имя каждого класса выбиралось из одного и того же пространства имен. Это означает, что во избежание коллизии имен для каждого класса нужно использовать уникальное имя. Кроме того, без некоторого способа управления пространством имен, можно быстро исчерпать удобные, дескриптивные имена для индивидуальных классов. Нужно также позаботиться, чтобы выбранное для класса имя было разумно уникально и не вступало в противоречие с именами классов, выбранными другими программистами. (Вообразите небольшую группу программистов, борющихся за право использования имени "Foobar" в качестве имени класса. Или вообразите все Internet- сообщество, спорящее, кто первым назвал класс "Espresso".) Java

Upload: others

Post on 07-Sep-2020

26 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

8. Пакеты и интерфейсы Пакеты — это контейнеры для классов, которые используются для

сохранения пространства имен классов. Например, класс с именем List, можно хранить в отдельном пакете, не опасаясь, что он столкнется с некоторым другим классом с именем List, хранящимся где-то в другом месте. Пакеты сохраняются иерархическим способом и явно импортируются в определения новых классов.

Методы класса определяют интерфейс к данным в классе. С помощью ключевого слова interface Java позволяет полностью отделить интерфейс от его реализации. Используя интерфейс, можно определить набор методов, которые могут быть реализованы одним или несколькими классами. Сам интерфейс в действительности не определяет никакой реализации. Хотя интерфейсы подобны абстрактным классам, они имеют дополнительную возможность: класс может реализовывать более одного интерфейса. В противоположность этому класс может наследовать только один суперкласс (абстрактный или другой).

Пакеты и интерфейсы — это два основных компонента Java-программы. В общем случае исходный файл Java может содержать любую (или все) из следующих четырех внутренних частей:

□ одиночный package-оператор (не обязательно); □ любое число import-операторов (не обязательно); □ одиночное объявление общего класса (требуется); □ любое число частных классов пакета (не обязательно). До сих пор в примерах использовалась только одна из этих частей —

одиночное объявление класса public. В этой главе рассмотрим остальные части.

8.1. Пакеты В примерах предшествующих глав имя каждого класса выбиралось

из одного и того же пространства имен. Это означает, что во избежание коллизии имен для каждого класса нужно использовать уникальное имя. Кроме того, без некоторого способа управления пространством имен, можно быстро исчерпать удобные, дескриптивные имена для индивидуальных классов. Нужно также позаботиться, чтобы выбранное для класса имя было разумно уникально и не вступало в противоречие с именами классов, выбранными другими программистами. (Вообразите небольшую группу программистов, борющихся за право использования имени "Foobar" в качестве имени класса. Или вообразите все Internet-сообщество, спорящее, кто первым назвал класс "Espresso".) Java

Page 2: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

обеспечивает специальный механизм для разделения пространства имен классов на управляемые части. Этот механизм называется "пакеты" (packages). Пакет является механизмом как именования, так и управления видимостью. Можно определять внутри пакета классы, которые недоступны кодам вне этого пакета. Возможно также определять члены класса, доступные только другим членам того же самого пакета. Это позволяет классам близко знать друг друга, но не предъявлять это знание остальной части мира.

Определение пакета Пакет создается включением оператора package в начало исходного

файла Java. Любые классы, объявленные в пределах этого файла, будут принадлежать указанному пакету. Оператор package определяет пространство имен, в котором сохраняются классы. Если опустить инструкцию package, имена класса помещаются в пакет по умолчанию (default package), который не имеет никакого имени. В то время как пакет по умолчанию хорош для коротких примеров программ, он неадекватен для реальных приложений.

Общая форма инструкции package:

package pkg;

Здесь pkg — имя пакета. Например, следующая инструкция создает пакет c именем MyPackage. package MyPackage;

Чтобы хранить пакеты, Java использует каталоги файловой системы. Например, class-файлы для любых классов, которые объявляются как часть пакета MyPackage, должны быть сохранены в каталоге с именем MyPackage.

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

Одну и ту же package-инструкцию могут включать несколько файлов. Она просто указывает, какому пакету принадлежат классы, определенные в файле. Это не исключает принадлежности других классов в других файлах к тому же самому пакету. Большинство реальных пакетов содержат много файлов.

Можно создавать иерархию пакетов. Для этого необходимо просто отделить каждое имя пакета от стоящего выше при помощи операции "точка". Общая форма инструкции многоуровневого пакета: package pkg1[.pkg2[.pk3]];

Иерархия пакетов должна быть отражена в файловой системе системы разработки Java-программ. Например, пакет, объявленный как

Page 3: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

package java.awt.image;

должен быть сохранен в каталоге java/awt/image, java\awt\image или java:awt:image файловой системы UNIX, Windows или Macintosh, соответственно. Следует тщательно выбирать имена пакетов. Нельзя переименовывать пакет без переименования каталога, в котором хранятся классы.

Использование CLASSPATH Обсудим переменную окружения (environmental variable)

CLASSPATH. Происходит это потому, что размещением корня любой иерархии пакетов в файловой системе компьютера управляет специальная переменная окружения CLASSPATH. До сих пор мы сохраняли все классы в одном и том же неименованном пакете (который используется по умолчанию). Это позволяет просто компилировать исходный код и запускать интерпретатор Java, указывая (в качестве его параметра) имя класса на командной строке. Данный механизм работал, потому что заданный по умолчанию текущий рабочий каталог (.) обычно указывается в переменной окружения CLASSPATH, определяемой для исполнительной (run-time) системы Java по умолчанию. Однако все становится не так просто, когда включаются пакеты.

Предположим, что создается класс с именем PackTest в пакете с именем test. Так как структура каталогов должна соответствовать пакетам, следует создать каталог с именем test и разместить исходный файл PackTest.java внутри этого каталога. Затем назначаем test текущим каталогом и компилируем PackTest.java. Это приводит к сохранению результата компиляции (файла PackTest.class) в каталоге test, как это и должно быть. Когда мы попробуем выполнить этот файл с помощью интерпретатора Java, то он выведет сообщение об ошибке "can't find class PackTest" (невозможно найти класс PackTest). Это происходит потому, что класс теперь сохранен в пакете с именем test. Мы больше не можем обратиться к нему просто как к PackTest. Нужно обращаться к классу, перечисляя иерархию его пакетов и разделяя пакеты точками. Этот класс должен теперь назваться test.PackTest. Однако если мы попробуем использовать test.PackTest, то будем все еще получать сообщение об ошибке "can't find class test/PackTest" (невозможно найти класс test/PackTest).

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

В этот момент у нас имеется две возможности: перейти по каталогам вверх на один уровень и испытать команду java test.PackTest

Page 4: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

или добавить вершину иерархии классов в переменную окружения CLASSPATH. Тогда будет возможно использовать команду java test.PackTest из любого каталога, и Java найдет правильный class-файл. Например, если мы работаем над исходным кодом в каталоге C:\myjava, то в CLASSPATH надо установить следующие пути: .;С:\myjava;C:\java\classes

Создание пакета в среде Eclipse Создадим в среде Eclipse новый проект с именем, например,

Progr38_AccountBalance. Выполнив команду File, New, Package, создадим в составе этого проекта пакет MyPack (рис.1).

Рис. 1. Ввод имени пакета

Затем создадим новый класс AccountBalance (рис.2). Чтобы класс вошел в состав пакета, в поле Package должно быть указано имя пакета.

Page 5: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

Рис. 2. Создание класса в составе пакета

В файле AccountBalance.java наберем код, приведенный в следующей программе.

Программа 38. Пример пакета // Простой пакет package MyPack; class Balance{ String name; double bal; Balance(String n, double b){ name = n; bal = b; } void show(){ if(bal < 0)

Page 6: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

System.out.print ("-->> ") ; System.out.println(name + ": $ " + bal); } } class AccountBalance { public static void main(String args[]){ Balance current[] = new Balance[3]; current[0] = new Balance("K. J. Fielding", 123.23); current[1] = new Balance("Will Tell", 157.02); current[2] = new Balance("Tom Jackson", -12.33); for(int i = 0; i < 3; i++) current[i].show(); } }

При добавлении в проект пакета создается папка с именем, совпадающим с именем пакета, вложенная в папку исходного кода src. В папке пакета помещается файл с исходным кодом класса. При компиляции в папке bin создается папка пакета с файлами классов (рис.3).

Рис. 3. Папки проекта и пакета

Из среды Eclipce класс, входящий в состав пакета, выполняется обычным образом.

При запуске из командной строки нужно учитывать что полное имя класса начинается с имени пакета и точки: MyPack.Accountbalance. Пример командной строки, запускающей класс, приведен на рис. 4.

Рис. 4. Запуск класса из командной строки

Page 7: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

В параметре –classpath нужно указывать не полный путь до class-файла, а только путь до папки (bin), в которой расположена папка пакета (MyPack). Затем указывается полное имя класса (MyPack.AccoutBalance) без расширения .class. Имя класса надо набирать с учетом регистра. Если, например, написать myPack.AccoutBalance, то нужный класс найден не будет и возникнет ошибка.

При установке системы Java на компьютер, создается переменная окружения CLASSPATH, содержащая пути к каталогам. Ее можно настроить так, чтобы можно было запускать Java-програмы из командной строки без параметра –classpath.

Текущее значение переменной CLASSPATH можно увидеть (рис.5), выполнив команду set CLASSPATH

Рис. 5. Значение переменной окружения CLASSPATH

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

Выполним в командной строке команду: set CLASSPATH=%CLASSPATH%;.;

для добавления к переменной CLASSPATH текущего каталога (рис. 6)

Рис. 6. Добавление к переменной CLASSPATH текущего каталога

Теперь перейдем в папку bin и выполним команду java Mypack.AccountBalance

В результате класс, входящий в состав пакета, будет выполнен (рис.7),

Page 8: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

Рис. 7. Запуск класса из пакета из охватывающей папки

Защита доступа Как уже известно, доступ к private-члену класса предоставляется

только другим членам того же класса. Пакеты добавляют еще одно измерение к управлению доступом.

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

□ подклассы в том же пакете; □ неподклассы в том же пакете; □ подклассы в различных пакетах; □ классы, которые не находятся в том же пакете и не являются

подклассами. Большое разнообразие уровней защиты доступа для этих категорий

обеспечивают три спецификатора доступа: private, public и protected. Табл. 9 подводит итог соответствующим взаимодействиям.

Таблица 9. Доступ к членам классов Private Без модифика-

тора Protected Public

Тот же класс Yes Yes Yes Yes

Подкласс того же пакета No Yes Yes Yes

Неподкласс того же пакета

No Yes Yes Yes

Другой подкласс другого пакета

No No Yes Yes

Page 9: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

Другой неподкласс другого пакета

No No No Yes

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

Все, объявленное как public, может быть доступно отовсюду. Все, объявленное как private, не может быть видимо извне своего

класса. Когда элемент не имеет явных спецификаций доступа, он видим в

подклассах, также как в других классах в том же самом пакете. Это — доступ, заданный по умолчанию.

Если нужно позволить элементу быть видимым извне текущего пакета, но только в классах, являющихся прямыми подклассами класса, то этот элемент объявляется protected.

Табл. 9 применяется только к членам классов. Сам класс имеет лишь два возможных уровня доступа — по умолчанию и общий (public). Когда класс объявлен как public, он доступен из любых других кодов. Если класс имеет доступ по умолчанию, то к нему возможен доступ только из кодов того же пакета.

Программа 39. Пример управления доступом Следующий пример показывает все комбинации модификаторов

управления доступом, используя два пакета и пять классов. Помните, что классы для двух различных пакетов нужно сохранять в каталогах, названных именами соответствующих им пакетов — в данном случае p1 и р2.

В первом исходном пакете определено три класса: Protection, Derived и samePackage. В первом классе определено четыре переменных int в каждом из допустимых режимов защиты. Переменная n объявлена с защитой, заданной по умолчанию, n_pri объявлена как private, njoro — как protected и n_pub —- как public.

Каждый последующий класс в этом примере будет пытаться обратиться к переменным в экземпляре этого класса. Строки, которые не будут компилироваться из-за ограничений доступа, прокомментированы при помощи однострочного комментария (//...). Перед каждой из этих строк есть комментарий, перечисляющий области, где данный уровень защиты разрешил бы доступ.

Второй класс, Derived, является подклассом Protection в том же пакете pi. Это предоставляет Derived доступ к каждой переменной в Pro-tection за исключением n_pri, поскольку она private. Третий класс, Same-Package, — не подкласс Protection, но он находится в том же пакете и также имеет доступ ко всем переменным суперкласса, кроме n_pri.

Итак, файл Protection.java:

Page 10: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

package p1;

// Тот же класс

public class Protection { int n = 1; private int n_pri = 2; protected int n_pro = 3; public int n_pub = 4; public Protection(){ System.out.println("Конструктор Protection"); System.out.println("n = " + n); System.out.println("n_pri = " + n_pri); System.out.println("n_pro = " + n_pro); System.out.println("n_pub = " + n_pub); } }

Файл Derived.java: package p1;

// Подкласс того же пакета

class Derived extends Protection{ Derived(){ System.out.println("Конструктор Derived"); System.out.println("n = " + n); // Только класс // System.out.println("n_pri - " + n_pri); System.out.println("n_pro = " + n_pro); System.out.println("n_pub = " + n_pub) ; } }

Файл SamePackage.java: package p1;

// Неподкласс того же пакета

public class SamePackage { SamePackage(){ System.out.println("Конструктор класса SamePackage из пакета p1.\n" + "Создаю объект класса Protection"); Protection p = new Protection(); System.out.println("Продолжение работы конструктора SamePackage.\n" + "Работаю с объектом класса Protection"); System.out.println("n = " + p.n); // Только класс // System.out.println("n_pri = " + p.n_pri) ; System.out.println("n_pro = " + p.n_pro); System.out.println("n_pub = " + p.n_pub); } }

Page 11: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

Далее следует исходный код (текст) для пакета р2. Два класса, определенных в р2, охватывают два других условия, на которые влияет управление доступом. Первый класс, Protection2, является подклассом p1.Protection. Он предоставляет доступ ко всем переменным класса p1.Protection, кроме n_pri (потому что она — private) и n (т. к. она объявлена с защитой, заданной по умолчанию). Помните, умолчание разрешает доступ только изнутри класса или пакета, не из подклассов внешних пакетов. Наконец, класс OtherPackage имеет доступ только к одной переменной, n_pub, которая была объявлена как public.

Файл Protection2.java: package p2;

// Другой подкласс другого пакета

class Protection2 extends p1.Protection { Protection2(){ System.out.println("Производный конструктор другого пакета"); // только класс или пакет // System.out.println("n = " + n); // только класс // System.out.println("n_pri = " + n_pri); System.out.println("n_pro = " + n_pro); System.out.println("n_pub = " + n_pub); } }

Файл OtherPackage.java: package p2;

// Другой неподкласс другого пакета

public class OtherPackage { OtherPackage(){ p1.Protection p = new p1.Protection(); System.out.println("Конструктор другого пакета"); // только класс или пакет // System.out.println("n = " + p.n); // только класс // System, out .println ("n_pri = " + p.n_pri) ; // только класс, подкласс или пакет // System.out.println("njpro = " + p.n_pro); System.out.println("n_pub = " + p.n_pub); } }

Для тестирования пакета p1 создадим класс Demo_p1 (рис.8)

Page 12: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

Рис. 8. Создание тестирующего класса для пакета p1

// Файл Demo_p1.java // Тестирование пакета p1. package p1; public class Demo_p1 { // Создает объекты различных классов из p1. public static void main(String args[]){ System.out.println("main(). Тестирование пакета p1"); System.out.println("main(). Создание объекта класса Protection"); Protection ob1 = new Protection(); System.out.println("main(). Создание объекта класса Derived"); Derived ob2 = new Derived(); System.out.println("main(). Создание объекта класса SamePackage из пакета p1"); SamePackage ob3 = new SamePackage(); } }

Page 13: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

Данная программа выводит: main(). Тестирование пакета p1 main(). Создание объекта класса Protection Конструктор Protection n = 1 n_pri = 2 n_pro = 3 n_pub = 4 main(). Создание объекта класса Derived Конструктор Protection n = 1 n_pri = 2 n_pro = 3 n_pub = 4 Конструктор Derived n = 1 n_pro = 3 n_pub = 4 main(). Создание объекта класса SamePackage из пакета p1 Конструктор класса SamePackage из пакета p1. Создаю объект класса Protection Конструктор Protection n = 1 n_pri = 2 n_pro = 3 n_pub = 4 Продолжение работы конструктора SamePackage. Работаю с объектом класса Protection n = 1 n_pro = 3 n_pub = 4

Для тестирования пакета p2 создадим следующий файл Demo_p2.java: // Файл Demo_p2.java // Тестирование пакета p2 package p2; //Создает объекты различных классов из р2. public class Demo_p2 { public static void main(String args[]){ System.out.println("main(). Тестирование пакета p2"); System.out.println("main(). Создание объекта класса Protection2"); Protection2 ob1 = new Protection2(); System.out.println("main(). Создание объекта OtherPackage пакета p2"); OtherPackage ob2 = new OtherPackage(); } }

Состав созданного проекта показан в окне Package Explorer на рис.9. Для запуска нужного класса нужно выбрать его в окне Project Explorer и выполнить команду меню Run, Run (комбинация клавиш Ctrl+F11).

Page 14: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

Рис. 9. Состав проекта с двумя пакетами

Программа из класса Demo_p2 выводит: main(). Тестирование пакета p2 main(). Создание объекта класса Protection2 Конструктор Protection n = 1 n_pri = 2 n_pro = 3 n_pub = 4 Конструктор Protection2, производного от Protection n_pro = 3 n_pub = 4 main(). Создание объекта OtherPackage пакета p2 Конструктор класса OtherPackage пакета p2 Создаю объект класса Protection Конструктор Protection n = 1 n_pri = 2 n_pro = 3 n_pub = 4 Работаю с объектом класса Protection n_pub = 4

8.2. Импорт пакетов Все встроенные классы Java хранятся в пакетах. В неименованном

пакете умолчания нет классов ядра Java, все стандартные классы хранятся в нескольких именованных пакетах. Так как классы в пакетах должны быть полностью квалифицированы именем (или именами) их пакета, весьма утомительно печатать длинное, разделенное точками полное пакетное имя каждого класса, который нужно использовать.

Page 15: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

Чтобы обеспечить видимость некоторых классов или полных пакетов Java, используется оператор import. После импортирования на класс можно ссылаться прямо, используя только его имя. Для записи законченной программы в операторе import нет технической необходимости, но он удобен для программиста. Если программа обращается к нескольким десяткам классов, оператор import сохранит массу усилий и времени при вводе кода.

В исходном файле Java оператор import следует после оператора package (если он используется) и перед любыми определениями класса. Общая форма оператора import:

import pkg1[.ркд2].(classname | *) ;

Здесь pkg1 — имя пакета верхнего уровня, pkg2 — имя подчиненного пакета внутри внешнего пакета (имена разделяются точкой). Нет никакого практического предела глубине пакетной иерархии, за исключением того, что обусловлен файловой системой. Наконец, определяется или явное имя класса, или звездочка (*), которая указывает, что компилятор Java должен импортировать полный пакет. Следующий кодовый фрагмент показывает использование обеих форм: import java.util.Date; // Импорт класса Date import java.io.*; // Импорт всех классов пакета io

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

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

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

Page 16: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

Везде, где мы используем имя класса, можно применять полное составное имя, включающее всю иерархию его пакетов. Например, следующий фрагмент использует оператор import: import java.util.*; class MyDate extends Date { … }

Тот же пример без оператора import выглядит так:

class MyDate extends java.util.Date { … }

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

Программа 40. Открытый класс в пакете Создадим проект с названием Progr40_PublicPacketClass. При

содании проекта создается папка с тем же именем, что и имя проекта, в которой создается структура каталогов, в частности, каталог src для исходных файлов и каталог bin для откомпилированных классов.

В составе этого проекта создадим пакет MyPack, что приведет к появлению папки с таким именем.

Затем в составе пакета MyPack создадим класс Balance, что приведет к созданию файла Balance.java. package MyPack;

// Файл Balance.java /* Теперь класс Balance, его конструктор и метод show() * являются общими (public). Это означает, их может использовать * код неподкласса, находящийся вне их пакета. */ public class Balance { String name; double bal; public Balance(String n, double b){ name = n; bal = b; } public void show(){ if(bal < 0) System.out.print(" -->> "); else System.out.println(name + ": $" + bal); }

Page 17: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

}

Теперь класс Balance — с общим (public) доступом. То же можно сказать о его конструкторе и методе show (). Это означает, что они доступны любым типам кода вне пакета МуРаск. Например, в следующей программе класс TestBalance импортирует пакет МуРаск и получает возможность использовать класс Balance:

// Файл TestBalance.java import MyPack.*; class TestBalance { public static void main(String args[]){ /* Из-за того, что Balance теперь public, можно использовать класс Balance и вызывать его конструктор. */ Balance test = new Balance("J. J. Jaspers", 99.88); test.show(); // Можно также вызвать show () } }

Если удалить спецификатор public из класса Balance то при компиляции TestBalance взникнет ошибка.

8.3. Интерфейсы Применяя ключевое слово interface, можно полностью освободить

интерфейс класса от его реализации. То есть, используя interface, мы указваем, что класс должен делать, но не как он это делает. Интерфейсы синтаксически подобны классам, но в них нет экземплярных переменных, и их методы объявляются без тела. Практически, это означает, что интерфейсы определяются без предположений относительно того, как они реализованы. Как только интерфейс определен, то реализовать его может любое число классов. И наоборот, один класс может реализовать любое число интерфейсов.

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

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

Page 18: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

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

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

Определение интерфейса Определение интерфейса во многом подобно определению класса.

Общая форма интерфейса выглядит так: access interface name {

return-type method-name1(parameter-list) ; return-type method-name2(parameter-list) ; type final-varname1 = value; type final-varname2 = value; //... return-type method-nameN(parameter-list); type final-varnameN = value;

}

Здесь access — спецификатор доступа (или public или не используется). Если никакой спецификатор доступа не включен, тогда используется доступ по умолчанию, и интерфейс доступен только другим членам пакета, в котором он объявлен. При объявлении с public интерфейс может использоваться любым другим кодом, name— имя интерфейса, им может быть любой допустимый идентификатор. Объявленные методы не имеют тел и заканчиваются точкой с запятой после списка параметров. Это, по существу, абстрактные методы. В пределах интерфейса для них нет никаких умалчиваемых реализаций. Каждый класс, который включает интерфейс, должен реализовать все его методы.

Внутри объявлений интерфейсов можно объявлять переменные. Они неявно считаются final и static (это означает, что они не могут быть изменены реализующим классом), а также должны быть инициализированы постоянными значениями. Все методы и переменные интерфейсов — неявно общие (public), если интерфейс сам не объявлен как public.

Пример определения интерфейса: interface Callback {

void callback(int param); }

Page 19: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

Здесь объявлен простой интерфейс, содержащий один метод с именем callback(), который имеет единственный целый параметр.

Реализация интерфейсов Когда интерфейс определен, он может реализовываться одним или

несколькими классами. Для реализации интерфейса в определение класса включают предложение с ключевым словом implements и затем создают методы, определенные в интерфейсе. Общая форма класса, который включает implements предложение, выглядит примерно так: access class classname [extends superclass]

[implements interface [,interface...] ] { // тело-класса

}

Здесь access — спецификатор доступа (public или не используется). Если класс реализует более одного интерфейса, они разделяются запятой. Если класс реализует два интерфейса, которые объявляют один и тот же метод, то клиенты любого интерфейса будут использовать один и тот же метод. Методы, которые реализуют интерфейс, должны быть объявлены как public. Кроме того, сигнатура типа реализующего метода должна точно соответствовать сигнатуре типа, указанной в определении интерфейса.

Пример небольшого класса, который реализует интерфейс Callback, показанный ранее: class Client implements Callback { // Реализация Callback-интерфейса

public void callback(int p){ System.out.println("callback вызван с аргументом " + p);

} }

Обратите внимание, что callback() объявлен со спецификатором доступа public.

При реализации метода интерфейса он должен быть объявлен как public.

Классы, реализующие интерфейсы, могут определять дополнительные собственные члены. Например, следующая версия класса Client реализует метод callback() и добавляет метод nonIfaceMeth(): class Client implements Callback{ // Реализация Callback интерфейса

public void callback(int p){ System.out.println("callback вызван с аргументом " + p);

} void nonIfaceMeth(){

Page 20: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

System.out.println("Классы, реализующие интерфейсы " + "могут также определять другие члены.");

} }

Реализации доступа через интерфейсные ссылки Можно объявлять переменные как объектные ссылки, которые

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

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

В следующем примере вызов метода callback() выполняется через ссылочную переменную интерфейса: class TestIface {

public static void main(String args[]){ Callback с = new Client(); c.callback(42);

} }

Программа 41. Пример использования интерфейса Соберем приведенный выше код в работающую программу. Создадим проект с именем Progr41_Interface. Командой File, New, Interface (рис. 10) создадим интерфейс Callback

Page 21: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

Рис. 10. Создание интерфейса

В результате в папке src создается файл Callback.java. Поместим в этот файл код интерфейса: // Файл Callback.java interface Callback { void callback(int param); }

Далее добавляем в проект класс Client со следующим кодом: // Файл Client.java class Client implements Callback{ // Реализация Callback интерфейса public void callback(int p){ System.out.println("callback вызван с аргументом " + p); } void nonIfaceMeth(){ System.out.println("Классы, реализующие интерфейсы " + "могут также определять другие члены."); } }

И, наконец, добавляем в проект тестирующий класс: // Файл TestIface.java

Page 22: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

class TestIface { public static void main(String args[]){ Callback c = new Client(); // c – ссылка типа интерфейс, c.callback(42); // ссылается на объект класса, реализующего интерфейс // c.nonIfaceMeth(); // Нельзя } }

Вывод этой программы: callback вызван с аргументом 42

Обратите внимание, что переменной c, объявленной с типом интерфейса Callback, был назначен экземпляр класса Client. Хотя разрешается использовать с для обращения к методу callback(), она не может обращаться к любым другим членам класса Client. Переменная интерфейсной ссылки обладает знаниями только методов, объявленных в соответствующей интерфейсной декларации. Таким образом, c нельзя использовать для обращения к методу nonIfaceMeth(), т. к. он определен классом Client, а не интерфейсом Callback.

Хотя предыдущий пример отражает чисто технически, как переменная интерфейсной ссылки может обращаться к объекту реализации, он не демонстрирует полиморфную мощь такой ссылки. Чтобы показать это, сначала создадим другую реализацию callback, как показано ниже:

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

Первый выполняет еще одну реализацию интерфеса Callback: // Файл AnotherClient.java //Другая реализация Callback. class AnotherClient implements Callback { // Implement Callback's interface public void callback(int p){ System.out.println("Другая версия callback"); System.out.println("Квадрат p равен " + (p * p)); } }

Второй класс выполняет тестирование двух реализаций интерфайса: // Файл TestIface2.java class TestIface2 { public static void main(String args[]){ Callback c = new Client(); AnotherClient ob = new AnotherClient(); c.callback(42); c = ob; // с теперь ссылается на объект AnotherClient c.callback(42); }

Page 23: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

}

Вывод этой программы: callback вызван с аргументом 42 Другая версия callback Квадрат p равен 1764

Здесь видно, что вызываемая версия callback() определяется типом объекта, к которому обращается c во время выполнения.

Частичные реализации Если класс включает интерфейс, но полностью не реализует

методы, определенные этим интерфейсом, то этот класс должен быть объявлен как abstract (абстрактный). Например: abstract class Incomplete implements Callback { int a, b; void show(){

System, out. println (a + “ " + b) ; } // ... }

Здесь класс incomplete не реализует callback() и должен быть объявлен как абстрактный. Любой класс, который наследует Incomplete, должен реализовать callback() или объявить себя как abstract.

8.4. Применения интерфейсов Чтобы понять мощь интерфейсов, рассмотрим более практический

пример. Ранее мы разработали класс с именем stack, который реализовал простой стек фиксированного размера. Однако существуют и другие способы представления стека. Например, стек может иметь фиксированный размер, или быть "растущим". Стек может также содержаться в массиве, связном списке, двоичном дереве и т. д. Независимо от того, как стек реализован, интерфейс стека остается тем же самым. То есть методы push() и pop() определяют интерфейс к стеку независимо от подробностей реализации. Поскольку интерфейс к стеку отделен от его реализации, то легко определить интерфейс стека, оставляя за каждой реализацией определение специфики

Программа 43. Интерфейс стека Ниже показан интерфейс, определяющий целый стек. Поместим его

в файле с именем IntStack.java. //Файл IntStack.java // Определение интерфейса целого стека. interface IntStack{

Page 24: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

void push (int item); // Запомнить элемент int pop(); // Извлечь элемент }

Следующая программа создает класс с именем Fixedstack, который реализует версию целого стека фиксированной длины: // Файл FixedStack.java // Реализация IntStack, которая использует фиксированную память. class FixedStack implements IntStack { private int stck[]; private int tos; // Выделить память и инициализировать стек FixedStack(int size){ stck = new int[size]; tos = -1; } // Поместить элемент в стек public void push(int item){ if(tos == stck.length - 1) // Использовать переменную length System.out.println("Стек заполнен."); else stck[++tos] = item; } // Извлечь элемент из стека public int pop(){ if(tos < 0){ System.out.println("Стек пуст.") ; return 0; } else return stck[tos--]; } }

class IFTest { public static void main(String args[]){ FixedStack mystack1 = new FixedStack(5); FixedStack mystack2 = new FixedStack(8); // Поместить в стек несколько чисел for(int i = 0; i < 5; i++) mystack1.push(i); for (int i = 0; i < 8; i++) mystack2.push(i); // Извлечь из стека эти числа System.out.println("Стек в mystack1:"); for(int i = 0; i < 5; i++) System.out.println(mystack1.pop()) ; System.out.println("Стек в mystack2:"); for(int i = 0; i < 8; i++) System.out.println(mystack2.pop()); } }

При запуске получаем:

Page 25: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

Стек в mystack1: 4 3 2 1 0 Стек в mystack2: 7 6 5 4 3 2 1 0

Теперь создадим другую реализацию интерфейса IntStack, которая создает динамический стек. В ней каждый стек создается в виде массива с некоторой исходной длиной. Если эта исходная длина превышена, то стек увеличивается в размере в два раза. // Файл DynStack.java // Реализует "растущий" стек. class DynStack implements IntStack { private int stck[]; private int tos; // Выделить память и инициализировать стек DynStack(int size){ stck = new int[size]; tos = -1; } // Поместить элемент в стек public void push(int item){ // Если стек заполнен, увеличить его размер if (tos == stck.length - 1){ int temp[] = new int[stck.length * 2]; // Двойной размер for(int i = 0; i < stck.length; i++) temp[i] = stck[i]; stck = temp; stck[++tos] = item; } else stck[++tos] = item; } // Извлечь элемент из стека public int pop(){ if(tos < 0){ System.out.println("Стек пуст,"); return 0; } else return stck[tos--]; } }

Page 26: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

class IFTest2 { public static void main(String args[]){ DynStack mystack1 = new DynStack(5); DynStack mystack2 = new DynStack(8); // Эти циклы заставляют каждый стек расти for(int i = 0; i < 12; i++) mystack1.push(i); for(int i = 0; i < 20; i++) mystack2.push(i); System.out.println("Стек в mystack1:"); for(int i = 0; i < 12; i++) System.out.print(mystackl.pop() + " "); System.out.println("\nСтек в mystack2:"); for(int i = 0; i < 20; i++) System.out.println(mystack2.pop() + " "); } }

В данной программе, как и в программа 42, есть два класса, включающие метод main(), которые, следовательно, можно направлять на выполнение. В программе 42 классы с методом main() находятся в файлах названия которых (TestIface и TestIface2) совпадают с именами классов (рис. 11), поэтому для их запуска нужно выбрать в окне Package Explorer соответствующий файл и выполнить команду Run, Run или нажать Ctrl + F11.

Page 27: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

Рис. 11. Состав проектов

В рассматриваемой программе имена классов с методом main() отличаются от имен соответствующих файлов, поэтому для запуска нужного класса нужно выделить в окне Package Explorer не файл, а сам класс, затем в контекстном меню, вызываемом правой кнопкой мыши, выбрать команду Run As, Java Application (рис 12).

Page 28: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

Рис. 12. Запуск класса из файла с отличающимся именем

При запуске класса IFTest2 получаем вывод: Стек в mystack1: 11 10 9 8 7 6 5 4 3 2 1 0 Стек в mystack2: 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

Следующий класс использует обе реализации. DynStack и FixedStack через интерфейсную ссылку. Это означает, что обращение к push() и pop() осуществляется во время выполнения, а не во время компиляции. // Файл IFTest3.java /* Создать интерфейсную переменную и обратиться к стекам через нее. */ class IFTest3{ public static void main(String args[]){ IntStack mystack; // Создать ссылочную переменную интерфейса DynStack ds = new DynStack(5); FixedStack fs = new FixedStack(8); mystack = ds; // Загрузить динамический стек // Поместить несколько чисел в стек for(int i = 0; i < 12; i++) mystack.push(i); mystack = fs; // Загрузить фиксированный стек for(int i = 0; i < 8; i++) mystack.push(i); mystack = ds; System.out.println("Значения динамического стека:"); for(int i = 0; i < 12; i++) System.out.print(mystack.pop() + " ") ; mystack = fs; System.out.println("\nЗначения фиксированного стека:"); for(int i = 0; i < 8; i++) System.out.print(mystack.pop() + " "); } }

Программа выводит: Значения динамического стека: 11 10 9 8 7 6 5 4 3 2 1 0

Page 29: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

Значения фиксированного стека: 7 6 5 4 3 2 1 0

В этой программе mystack — ссылка на интерфейс IntStack. Таким образом, когда она обращается к ds, то использует версии push() и pop(), определенные реализацией DynStack. Когда она обращается к fs, то использует версии push() и pop(), определенные в FixedStack. Напомним, что эти определения делаются во время выполнения. Доступ к множественным реализациям интерфейса через интерфейсную ссылочную переменную — это мощное средство, с помощью которого Java достигает полиморфизма времени выполнения.

Переменные в интерфейсах Можно использовать интерфейсы для импорта разделяемых

констант во множественные классы просто объявлением интерфейса, который содержит переменные, инициализированные желательными значениями. Когда этот интерфейс реализуется классом все имена указанных переменных окажутся в области их видимости как константы. Это подобно использованию файла заголовка в C/C++, который создает большое количество констант #defined или const-объявлений. Если интерфейс не содержит методов, то любой класс, который включает такой интерфейс, фактически не реализует ничего. Это выглядит так, как если бы данный класс импортировал постоянные переменные в пространство имен класса как final-переменные. Следующий пример использует эту методику, чтобы реализовать автоматизированное "средство принятия решений".

Программа 44. Переменные в интерфейсах В этой программе интерфейс и все классы расположены в одном

файле. // Файл AskMe.java import java.util.Random; interface SharedConstants { int NO=0; int YES = 1; int MAYBE = 2; int LATER = 3; int SOON = 4; int NEVER = 5; } class Question implements SharedConstants { Random rand = new Random(); int ask(){ int prob = (int)(100 * rand.nextDouble()); if (prob < 30) return NO; // 30%

Page 30: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

else if (prob < 60) return YES; // 30% else if (prob < 75) return MAYBE; // 15% else if (prob < 90) return LATER; // 15% else if (prob < 98) return SOON; // 8% else return NEVER; // 2% } } class AskMe implements SharedConstants { static void answer(int result){ switch(result){ case NO: System.out.println("Нет"); break; case YES: System.out.println("Да"); break; case MAYBE: System.out.println("Возможно") ; break; case LATER: System.out.println("Позже"); break; case SOON: System.out.println("Вскоре"); break; case NEVER: System.out.println("Никогда"); break; } } public static void main(String args[]){ Question q = new Question(); for(int i = 0; i < 10; i++) answer(q.ask()); } }

Компилятор разместит интерфейс и все классы в отдельных файлах (рис 13).

Page 31: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

Рис. 13. Файлы откомпилированных классов

Два класса программы — Question и AskMe, реализуют интерфейс SharedConstants, где определены константы NO, YES, MAYBE, SOON, LATER и NEVER. Внутри каждого класса код обращается к ним так, будто каждый класс прямо определил или унаследовал эти константы.

Данная программа использует один из стандартных классов Java — Random. Этот класс генерирует псевдослучайные числа. Он содержит несколько методов, которые позволяют получать случайные числа в различных формах. В представленном выше примере используется метод nextDoubie(). Он возвращает равномерно распределенные случайные числа в диапазоне от 0.0 до 1.0. Используя эти числа создается целое случайное число prob, принимающее значения от 0 до 99. Затем число prob используется для фомирования результата, возвращаемого методом ask(). В 30 случаях из 100 ask() возвращает NO и YES, в 15 случаях из 100 возвращает MAYBE и LATER, в 8 случаях - SOON и в 2 случаях NEVER.

Вывод, выполненный этой программой: Да Позже Нет Нет Нет Вскоре Да Вскоре Да Позже

Результаты различны для каждого прогона программы.

Расширение интерфейсов Один интерфейс может наследовать другой при помощи ключевого

слова extends. Синтаксис — тот же самый, что для наследования классов. При реализации интерфейса, который наследует другой интерфейс, класс должен обеспечить реализацию для всех методов, определенных в цепочке наследования интерфейса. Например:

Программа 45. Расширение интерфейсов // Файл IFExtend.java // Один интерфейс расширяет другой. interface А { void meth1(); void meth2(); } //В теперь включает meth1() и meth2(), а сам он добавляет meth3().

Page 32: 8. Пакеты и интерфейсыtarasov/JavaLectures/TarasovVLJavaAnd...8. Пакеты и интерфейсы Пакеты — это контейнеры для классов,

interface В extends А { void meth3(); } // Этот класс должен реализовать все из А и В. class MyClass implements В { public void meth1() { System.out.println("Реализует meth1()."); } public void meth2() { System.out.println("Реализует meth2()."); } public void meth3() { System.out.println("Реализует meth3()."); } } class IFExtend { public static void main (String arg[]) { MyClass ob = new MyClass (); ob.meth1(); ob.meth2(); ob.meth3(); } }

В качестве эксперимента можно было бы удалить реализацию meth1() из MyClass. Это вызовет ошибку во время компиляции. Как было заявлено ранее, любой класс, который реализует интерфейс, должен реализовать все методы, определенные этим интерфейсом, включая те, что унаследованы от других интерфейсов.

Программа выводит: Реализует meth1(). Реализует meth2(). Реализует meth3().

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