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.* становится избыточной (по крайней мере, если вам не потребуются другие классы из этого пакета).
Полученные знания позволяют вам создавать собственные библиотеки, сокращающие или полностью исключающие дублирование кода. Для примера можно взять уже знакомый псевдоним для метода 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 спецификаторы доступа public, protected и private располагаются перед определением членов классов — как полей, так и методов. Каждый спецификатор доступа управляет только одним отдельным определением.
Если спецификатор доступа не указан, используется «пакетный» уровень доступа. Получается, что в любом случае действует та или иная категория доступа. В нескольких ближайших подразделах описаны разные уровни доступа.
Во всех рассмотренных ранее примерах спецификаторы доступа не указывались. Доступ по умолчанию не имеет ключевого слова, но часто его называют доступом в пределах пакета (package access, иногда «дружественным»). Это значит, что член класса доступен для всех остальных классов текущего пакета, но для классов за пределами пакета он воспринимается как приватный (private). Так как компилируемый модуль — файл — может принадлежать лишь одному пакету, все классы одного компилируемого модуля автоматически открыты друг для друга в границах пакета.
Доступ в пределах пакета позволяет группировать взаимосвязанные классы в одном пакете, чтобы они могли легко взаимодействовать друг с другом. Размещая классы в одном пакете, вы берете код пакета под полный контроль. Таким образом, только принадлежащий вам код будет обладать пакетным доступом к другому, принадлежащему вам же коду — и это вполне логично. Можно сказать, что доступ в пределах пакета и является основной причиной для группировки классов в пакетах. Во многих языках определения в классах организуются совершенно произвольным образом, но в Java придется привыкать к более жесткой логике структуры. Вдобавок классы, которые не должны иметь доступ к классам текущего пакета, следует просто исключить из этого пакета.
Класс сам определяет, кому разрешен доступ к его членам. Не существует волшебного способа «ворваться» внутрь него. Код из другого пакета не может запросто обратиться к пакету и рассчитывать, что ему вдруг станут доступны все члены: protected, private и доступные в пакете. Получить доступ можно лишь несколькими «законными» способами: