Выбрать главу

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, поэтому директива im­port java.util.* становится избыточной (по крайней мере, если вам не потребуют­ся другие классы из этого пакета).

Пользовательские библиотеки

Полученные знания позволяют вам создавать собственные библиотеки, сокра­щающие или полностью исключающие дублирование кода. Для примера можно взять уже знакомый псевдоним для метода System.out.println(), сокращающий количество вводимых символов. Его можно включить в класс Print:

//. net/mi ndvi ew/uti1/Print.java

// Методы-печати, которые могут использоваться

// без спецификаторов, благодаря конструкции

// Java SE5 static import.

package net.mindview.util;

import java.io *;

public class Print {

// Печать с переводом строки: public static void print(Object obj) { System.out.println(obj);

}

// Перевод строки: public static void print(S) { System, out. pri ntlnO;

}

// Печать без перевода строки public static void printnb(Object obj) { System.out.print(obj);

}

// Новая конструкция Java SE5 printfO (из языка С): public static PrintStream printf(String format, Object... args) {

return System.out.printf(format, args):

}

} ///-

Новые методы могут использоваться для вывода любых данных с новой строки (print()) или в текущей строке (printnb()).

Как нетрудно предположить, файл должен располагаться в одном из катало­гов, указанных в переменной окружения CLASSPATH, по пути net/mindview. После компиляции методы static print() и printnb() могут использоваться где угодно, для чего в программу достаточно включить директиву import static:

//: access/PrintTest.java

// Использование статических методов печати из Print.java. import static net.mindview.util.Print.*:

public class PrintTest {

public static void main(String[] args) {

print("Теперь это стало возможно!"): print(lOO): print(lOOL), print(3.14159):

}

} /* Output:

Теперь это стало возможно! 100 100

3.14159

Теперь, когда бы вы ни придумали новый интересный инструмент, вы всегда можете добавить его в свою библиотеку.

Предостережение при работе с пакетами

Помните, что создание пакета всегда неявно сопряжено с определением струк­туры каталогов. Пакет обязан находиться в одноименном каталоге, который, в свою очередь, определяется содержимым переменной CLASSPATH. Первые эксперименты с ключевым словом package могут оказаться неудачными, пока вы твердо не усвоите правило «имя пакета — его каталог». Иначе компилятор будет выводить множество сообщений о загадочных ошибках выполнения, о не­возможности найти класс, который находится рядом в этом же каталоге. Если у вас возникают такие ошибки, попробуйте закомментировать директиву package; если все запустится, вы знаете, где искать причины.

Спецификаторы доступа Java

В Java спецификаторы доступа public, protected и private располагаются перед определением членов классов — как полей, так и методов. Каждый специфика­тор доступа управляет только одним отдельным определением.

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

Доступ в пределах пакета

Во всех рассмотренных ранее примерах спецификаторы доступа не указыва­лись. Доступ по умолчанию не имеет ключевого слова, но часто его называют доступом в пределах пакета (package access, иногда «дружественным»). Это зна­чит, что член класса доступен для всех остальных классов текущего пакета, но для классов за пределами пакета он воспринимается как приватный (private). Так как компилируемый модуль — файл — может принадлежать лишь одному пакету, все классы одного компилируемого модуля автоматически открыты друг для друга в границах пакета.

Доступ в пределах пакета позволяет группировать взаимосвязанные классы в одном пакете, чтобы они могли легко взаимодействовать друг с другом. Раз­мещая классы в одном пакете, вы берете код пакета под полный контроль. Та­ким образом, только принадлежащий вам код будет обладать пакетным досту­пом к другому, принадлежащему вам же коду — и это вполне логично. Можно сказать, что доступ в пределах пакета и является основной причиной для груп­пировки классов в пакетах. Во многих языках определения в классах организу­ются совершенно произвольным образом, но в Java придется привыкать к более жесткой логике структуры. Вдобавок классы, которые не должны иметь доступ к классам текущего пакета, следует просто исключить из этого пакета.

Класс сам определяет, кому разрешен доступ к его членам. Не существует волшебного способа «ворваться» внутрь него. Код из другого пакета не может запросто обратиться к пакету и рассчитывать, что ему вдруг станут доступны все члены: protected, private и доступные в пакете. Получить доступ можно лишь несколькими «законными» способами: