30 июн. 2015 г.

Пакеты в Java.

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

Зачем нужны пакеты в Java и что это такое

  • Задание пространства имен, предотвращение коллизий имен классов
  • Логическая группировка связанных классов
  • Инкапсуляция - сокрытие деталей реализации за счет модификаторов доступа

Платформа Java содержит пакеты, имена которых начинаются на com, java, javax и org. Посмотрите это в сырцах, не поленитесь. Основные классы языка входят в пакет java.lang. Различные вспомогательные классы находятся в java.util. Классы для ввода и вывода входят в java.io, а классы для работы в сети – в java.net. Некоторые их этих пакетов содержат подпакеты. Например, java.lang содержит два специализированных пакета java.lang.reflect и java.lang.ref, а java.util содержит подпакет java.util.zip, который в свою очередь содержит классы для работы с ZIPархивами.

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

В стандартную библиотеку Java API входят сотни классов. Каждый программист в ходе работы добавляет к ним десятки своих классов. Множество классов растет и становится необозримым. Уже давно принято отдельные классы, решающие какую-то одну определенную задачу, объединять в библиотеки классов. Но библиотеки классов, кроме стандартной библиотеки, не являются частью языка.

Разработчики Java включили в язык дополнительную конструкцию — пакеты (packages). Все классы Java распределяются по пакетам. Кроме классов пакеты могут содержать интерфейсы и вложенные подпакеты (subpackages). Образуется древовидная структура пакетов и подпакетов.

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

Каждый пакет создает одно пространство имен (namespace). Это означает, что все имена классов, интерфейсов и подпакетов в пакете должны быть уникальны. Имена в разных пакетах могут совпадать, но это будут разные программные единицы. Таким образом, ни один класс, интерфейс или подпакет не может оказаться сразу в двух пакетах. Если надо в одном месте программы использовать два класса с одинаковыми именами из разных пакетов, то имя класса уточняется именем пакета: пакет.Класс. Такое уточненное имя называется полным именем класса (fully qualified name).

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

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

Определение пакета

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

package pro.java.pkg001;

Все файлы с исходными текстами и тем-более откомпилированные файлы с байт-кодом (расширение .calss) должны находится в соответствующих под-каталогах, как, например в данном случае в подкаталоге pro\java\pkg001. Все современные IDE при создании пакета, сразу же создают соответствующую файловую структуру в каталоге проекта, так что об этом можно особо не беспокоиться, но знать необходимо.

Поскольку строка package имя; только одна и это обязательно первая строка файла, каждый класс попадает только в один пакет или подпакет.

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

Компилятор всегда создает для таких классов безымянный пакет (unnamed package), которому соответствует текущий каталог (current working directory) файловой системы.

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

Имя пакета может быть любым, главное чтобы оно было уникальным. Соглашение "Code Conventions" рекомендует записывать имена пакетов строчными буквами. Тогда они не будут совпадать с именами классов, которые, по соглашению, начинаются с прописной буквы. Кроме того, соглашение советует использовать в качестве имени пакета или подпакета доменное имя своего сайта, записанное в обратном порядке, например:

com.oracle.developer

Самое главное правило – это то, что имя пакета должно быть уникальным.

Одной из важных функций пакетов является разделение пространства имен Java и предотвращение конфликта имен между классами. Например, классы java.util.List и java.awt.List можно различить только по именам их пакетов. Однако важно, чтобы различались и имена самих пакетов.

Как работают пакеты

Использование класса из пакета:

  • Классы текущего пакета и пакета java.lang всегда видны
  • Классы других пакетов доступны по полному имени с пакетом, но если они имеют соответствующие модификаторы доступа
  • Можно использовать директиву import

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

А вот классы и их поля и методы принадлежащих другим пакетам, если у них нет ни каких модификаторов доступа, уже не доступны даже по полному имени пакета и класса.

Чтобы отдохнуть от теории и понять ее получше немного попрактикуемся и рассмотрим на примере первый пункт использование класса из пакета. Создадим пакет pro.java.pkg001 и в нем два класса:

P0001

P0002

Вывод у программы следующий:

P0003

Как видно у класса Print нет ни каких модификаторов, но все же его статические метод и переменная видны из класса Main и могут им использоваться.

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

Импорт классов и пакетов

Класс пакета pro.java.pkg001 может ссылаться на любой другой класс в pro.java.pkg001 по его простому имени. А поскольку классы пакета java.lang являются базовыми для языка Java, любой Java-код может ссылаться на любой класс этого пакета по его простому имени. Значит, всегда можно набирать String вместо java.lang.String. Однако по умолчанию для всех других классов нужно указывать полные имена. Поэтому, чтобы применить класс File пакета java.io, следует набрать java.io.File.

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

import java.io.File; // Теперь можно набирать File вместо java.io.File

Чтобы импортировать целый пакет классов, введите после import имя пакета, символы .* и точку с запятой. Например, чтобы использовать класс File вместе с несколькими другими классами пакета java.io, нужно просто импортировать целый пакет:

import java.io.*; // Теперь простые имена можно применять для всех классов из java.io

Этот синтаксис импорта пакета не относится к подпакетам. Даже если я импортирую пакет java.util, мне все еще придется обращаться к классу java.util.zip.ZipInputStream по его полному имени. Если два класса, импортированные из разных пакетов, называются одинаково, к ним нельзя обращаться по простому имени; во избежание двусмысленности к обоим классам следует обращаться по их полным именам.

Начиная с версии Java 5 в язык введена еще одна форма оператора import, предназначенная для импорта статических полей и методов класса — оператор import static. Например, можно написать оператор:

import static java.lang.Math.*;

После этого все статические поля и методы класса Math можно использовать без указания имени класса. Вместо записи:

double r = Math.cos(Math.PI * alpha);

можно записать просто

double r = cos(PI * alpha);

Теперь разгаданы и другие строчки, которые были в наших исходниках, например:

import static pro.java.util.Print.*;
import static pro.java.util.Strings.*;

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

Директивы import позволяют компилятору получить полные имена всех используемых классов, полей и методов по их коротким именам. В class-файл попадают полные имена, подстановка содержимого не происходит. Поэтому при запуске программы все используемые классы должны присутствовать в classpath.

Структура файла Java

Теперь можно описать структуру исходного файла с текстом программы на языке Java.

  • В первой строке файла может быть необязательный оператор package.
  • В следующих строках могут быть необязательные операторы import.
  • Далее идут описания классов и интерфейсов (интерфейсы будут рассмотрены позже).

Данные элементы могут перемежаться с комментариями, но они должны идти именно в такой последовательности.

Директив import может быть сколько угодно.

Существует еще несколько важных ограничений по файлам Java. Во-первых, каждый файл может содержать максимум один класс, объявленный как public. Такой класс предназначен для использования другими классами в других пакетах. Более подробно public классы и связанные с ними модификаторы мы рассмотрим в следующей статье. Данное ограничение по открытым классам относится только к классам верхнего уровня; как вы скоро узнаете, класс может содержать любое количество вложенных, или внутренних, классов, объявленных как public.

Второе ограничение касается имени файла в Java. Если файл Java содержит класс public, то имя файла должно представлять собой имя public класса, к которому присоединено расширение .java. Например, если Point является public классом, его исходный код должен находиться в файле Point.java.

Отсюда следует, что если в проекте есть несколько открытых классов, то они должны находиться в разных файлах.

Соглашение "Code Conventions" рекомендует открытый класс, если он имеется в файле, описывать первым.

Даже если ваши классы не являются public, полезно определять только по одному классу на файл и давать файлу имя класса. Для технологии Java характерно записывать исходный текст каждого класса в отдельном файле. В конце концов, компилятор всегда создает class-файл для каждого класса.

Исполнение классов в пакетах и CLASSPATH

GrabliС запуском нашей простой программы из Eclipse, что была в самом начале, нет ни каких проблем, а вот при запуске из командной строки они могут возникнуть.

Дело в том, что как я уже говорил ранее, в откомпилированный  class файл попадает полное имя класса, то есть имя_пакета.имя_класса.

Давайте запустим и посмотрим (обращаем внимание на пути файлов):

P0004

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

javap спасет отца русской демократии! Давайте посмотрим что там у нас скомпилилось?

P0005

Как видим наш класс сейчас называется pro.java.pkg001.Main, то есть наш класс содержит полное имя, включая имя пакета. И кстати обратите внимание на то как объявлен массив String[].

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

P0006

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

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

Давайте попробуем запустить наш класс Main по полному имени и посмотрим что получиться.

 

P0007

Как видим у нас опять ошибка.

Чтобы наша программа запустилась, должно выполняться одно из следующих двух условий. Либо программа должна выполняться из каталога, расположенного непосредственно над каталогом pro, то есть в нашем случае запуск должен происходить из каталога bin, либо переменная среды CLASSPATH должна содержать путь к каталогу pro, либо параметр –classpath должен указывать путь к каталогу pro во время выполнения программы с помощью java.

P0008

P0009

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

Ну и на последок можно глянуть хорошее видео по этой теме

Пакеты (pagkages) в Java

Комментариев нет:

Отправить комментарий