KnigaRead.com/

Брюс Эккель - Философия Java3

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Брюс Эккель, "Философия Java3" бесплатно, без регистрации.
Перейти на страницу:

Конфликт действительно произойдет при попытке создать 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

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

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

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

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

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

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

• Объявить член класса открытым (public), то есть доступным для кого угодно и откуда угодно.

• Сделать член доступным в пакете, не указывая другие спецификаторы доступа, и разместить другие классы в этом же пакете.

• Как вы увидите в главе 7, где рассказывается о наследовании, производный класс может получить доступ к защищенным (protected) членам базового класса вместе с открытыми членами public (но не к приватцым членам private). Такой класс может пользоваться доступом в пределах пакета только в том случае, если второй класс принадлежит тому же пакету (впрочем, пока на наследование и доступ protected можно не обращать внимания).

• Предоставить «методы доступа», то есть методы для чтения и модификации значения. С точки зрения ООП этот подход является предпочтительным, и именно он используется в технологии JavaBeans.

public

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

// access/dessert/Cookie.java 11 Создание библиотеки, package access.dessert.

public class Cookie { public CookieO {

System.out.println("Конструктор Cookie");

}

void biteO { System out printlnCbite"); } } /// ~

Помните, что файл Cookie.java должен располагаться в подкаталоге dessert каталога с именем access (соответствующем данной главе книги), а последний должен быть включен в переменную CLASSPATH. Не стоит полагать, будто Java всегда начинает поиск с текущего каталога. Если вы не укажете символ . (точка) в переменной окружения CLASSPATH в качестве одного из путей поиска, то Java и не заглянет в текущий каталог.

Если теперь написать программу, использующую класс Cookie:

// access/Dinner java // Использование библиотеки import access.dessert *;

public class Dinner {

public static void main(Stnng[] args) { Cookie x = new CookieO: //! x.biteO; // Обращение невозможно

}

} /* Output: Конструктор Cookie */// ~

то можно создать объект Cookie, поскольку конструктор этого класса объявлен открытым (public) и сам класс также объявлен как public. (Понятие открытого класса мы позднее рассмотрим чуть подробнее.) Тем не менее метод bite() этого класса недоступен в файле Dinner.java, поскольку доступ к нему предоставляется только в пакете dessert. Так компилятор предотвращает неправильное использование методов.

Пакет по умолчанию

С другой стороны, следующий код работает, хотя на первый взгляд он вроде бы нарушает правила:

//• access/Cake java

// Обращение к классу из другого компилируемого модуля

class Cake {

public static void mainCString[] args) { Pie x = new PieO, x f():

}

} /* Output. Pie f() */// ~

Второй файл в том же каталоге:

//• access/Pie.java

// Другой класс

class Pie { void f() { System.out.pnntlnC'Pie.fO"): }

} ///:-

Вроде бы эти два файла не имеют ничего общего, и все же в классе Cake можно создать объект Pie и вызвать его метод f()! (Чтобы файлы компилировались, переменная CLASSPATH должна содержать символ точки.) Естественно было бы предположить, что класс Pie и метод f() имеют доступ в пределах пакета и поэтому закрыты для Cake. Они действительно обладают доступом в пределах пакета — здесь все верно. Однако их доступность в классе Cake.java объясняется тем, что они находятся в одном каталоге и не имеют явно заданного имени пакета. Java по умолчанию включает такие файлы в «пакет по умолчанию» для текущего каталога, поэтому они обладают доступом в пределах пакета к другим файлам в этом каталоге.

private

Ключевое слово private означает, что доступ к члену класса не предоставляется никому, кроме методов этого класса. Другие классы того же пакета также не могут обращаться к private-членам. На первый взгляд вы вроде бы изолируете класс даже от самого себя. С другой стороны, вполне вероятно, что пакет создается целой группой разработчиков; в этом случае private позволяет изменять члены класса, не опасаясь, что это отразится на другом классе данного пакета.

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*