Философия Java3
Шрифт:
//: access/QualifiedMyClass java
public class QualifiedMyClass {
public static void main(String[] args) { access mypackage.MyClass m =
new access mypackage.MyClass;
}
} ///:-
С ключевым словом import решение выглядит гораздо аккуратнее:
II: access/ImportedMyClass.java
import access.mypackage.*:
public class ImportedMyClass {
public static void main(String[] args) { MyClass m = new MyClassO:
}
} ///
Ключевые слова package и import позволяют разработчику библиотеки организовать логическое деление глобального пространства имен, предотвращающее конфликты имен независимо от того, сколько
Создание уникальных имен пакетов
Вы можете заметить, что, поскольку пакет на самом деле никогда не «упаковывается» в единый файл, он может состоять из множества файлов .class, что способно привести к беспорядку, может, даже хаосу. Для предотвращения проблемы логично было бы разместить все файлы .class конкретного пакета в одном каталоге, то есть воспользоваться иерархической структурой файловой системы. Это первый способ решения проблемы нагромождения файлов в Java; о втором вы узнаете при описании утилиты jar.
Размещение файлов пакета в отдельном каталоге решает две другие задачи: создание уникальных имен пакетов и обнаружение классов, потерянных в «дебрях» структуры каталогов. Как было упомянуто в главе 2, проблема решается «кодированием» пути файла в имени пакета. По общепринятой схеме первая часть имени пакета должна состоять из перевернутого доменного имени разработчика класса. Так как доменные имена Интернета уникальны, соблюдение этого правила обеспечит уникальность имен пакетов и предотвратит конфликты. (Только если ваше доменное имя не достанется кому-то другому, кто начнет писать программы на Java под тем же именем.) Конечно, если у вас нет собственного доменного имени, для создания уникальных имен пакетов придется придумать комбинацию с малой вероятностью повторения (скажем, имя и фамилия). Если же вы решите публиковать свои программы на Java, стоит немного потратиться на получение собственного доменного имени.
Вторая составляющая — преобразование имени пакета в каталог на диске компьютера. Если программе во время исполнения понадобится загрузить файл .class (что делается динамически, в точке, где программа создает объект определенного класса, или при запросе доступа к статическим членам класса), она может найти каталог, в котором располагается файл .class.
Интерпретатор Java действует по следующей схеме. Сначала он проверяет переменную окружения CLASSPATH (ее значение задается операционной системой, а иногда программой установки Java или инструментарием Java). CLASSPATH содержит список из одного или нескольких каталогов, используемых в качестве корневых при поиске файлов .class. Начиная с этих корневых каталогов, интерпретатор берет имя пакета и заменяет точки на слеши для получения полного пути (таким образом, директива package foo.bar.baz преобразуется в foo\bar\baz, foo/bar/baz или что-то еще в зависимости от вашей операционной системы). Затем полученное имя присоединяется к различным элементам CLASSPATH. В указанных местах ведется поиск файлов .class, имена которых совпадают с именем создаваемого программой класса. (Поиск также ведется в стандартных каталогах, определяемых местонахождением интерпретатора Java.)
Чтобы понять все сказанное, рассмотрим мое доменное имя: MindView.net. Обращая его, получаем уникальное глобальное имя для моих классов: net. mindview. (Расширения com, edu, org и другие в пакетах Java прежде записывались в верхнем регистре, но начиная с версии Java 2 имена пакетов записываются только строчными буквами.) Если потребуется создать библиотеку с именем simple, я получаю следующее имя пакета:
package net.mindview.simple,
Теперь полученное
имя пакета можно использовать в качестве объединяющего пространства имен для следующих двух файлов://: net/mindview/simple/Vector.java
// Создание пакета
package net mindview simple,
public class Vector { public VectorО {
System out printlnCnet mindview.simple.Vector");
}
} ///:-
Как упоминалось ранее, директива package должна находиться в первой строке исходного кода. Второй файл выглядит почти так же:
//: net/mindview/simple/List java
// Создание пакета
package net.mindview simple;
public class List { public ListO {
System.out pri ntln("net.mi ndvi ew.si mple.Li st"),
}
} /// ~
В моей системе оба файла находятся в следующем подкаталоге:
С \DOC\JavaT\net\mindview\simple
Если вы посмотрите на файлы, то увидите имя пакета net.mindview.simple, но что с первой частью пути? О ней позаботится переменная окружения CLAS-SPATH, которая на моей машине выглядит следующим образом:
CLASSPATH= ,D \JAVA\LIB,C.\D0C\JavaT
Как видите, CLASSPATH может содержать несколько альтернативных путей для поиска.
Однако для файлов JAR используется другой подход. Вы должны записать имя файла JAR в переменной CLASSPATH, не ограничиваясь указанием пути к месту его расположения. Таким образом, для файла JAR с именем grape.jar переменная окружения должна выглядеть так:
CLASSPATH= .D.\JAVAXLIB.С \flavors\grape.jar
После настройки CLASSPATH следующий файл можно разместить в любом каталоге:
// access/LibTest.java // Uses the library, import net mindview simple *;
public class LibTest {
public static void main(String[] args) { Vector v = new VectorO. List 1 = new ListO:
}
} /* Output:
net.mindview simple Vector net mindview.simple List */// -
Когда компилятор встречает директиву import для библиотеки simple, он начинает поиск в каталогах, перечисленных в переменной CLASSPATH, найдет каталог net/mindview/simple, а затем переходит к поиску компилированных файлов с подходящими именами (Vector.class для класса Vector и List.class для класса List). Заметьте, что как классы, так и необходимые методы классов Vector и List должны быть объявлены со спецификатором public.
Конфликты имен
Что происходит при импортировании конструкцией * двух библиотек, имеющих в своем составе идентичные имена? Предположим, программа содержит следующие директивы:
import net mindview simple *; import java.util.*,
Так как пакет java,util.* тоже содержит класс с именем Vector, это может привести к потенциальному конфликту. Но, пока вы не начнете писать код, вызывающий конфликты, все будет в порядке — и это хорошо, поскольку иначе вам пришлось бы тратить лишние усилия на предотвращение конфликтов, которых на самом деле нет.
Конфликт действительно произойдет при попытке создать Vector:
Vector v = new VectorO;
К какому из классов Vector относится эта команда? Этого не знают ни компилятор, ни читатель программы. Поэтому компилятор выдаст сообщение об ошибке и заставит явно указать нужное имя. Например, если мне понадобится стандартный класс Java с именем Vector, я должен явно указать этот факт:
java util.Vector v = new java util,VectorO;
Данная команда (вместе с переменной окружения CLASSPATH) полностью описывает местоположение конкретного класса Vector, поэтому директива import java.util.* становится избыточной (по крайней мере, если вам не потребуются другие классы из этого пакета).