Помните, что файл 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 позволяет изменять члены класса, не опасаясь, что это отразится на другом классе данного пакета.
Предлагаемый по умолчанию доступ в пределах пакета часто оказывается достаточен для сокрытия данных; напомню, что такой член класса недоступен пользователю пакета. Это удобно, так как обычно используется именно такой уровень доступа (даже в том случае, когда вы просто забудете добавить спецификатор доступа). Таким образом, доступ public чаще всего используется тогда, когда вы хотите сделать какие-либо члены класса доступными для программи-ста-клиента. Может показаться, что спецификатор доступа private применяется редко и можно обойтись и без него. Однако разумное применение private очень важно, особенно в условиях многопоточного программирования (см. далее).
Пример использования private:
II- access/IceCream.java
// Демонстрация ключевого слова private.
class Sundae {
private SundaeO {} static Sundae makeASundaeO { return new SundaeO;
}
}
public class IceCream {
public static void main(String[] args) { III Sundae x = new SundaeO; Sundae x = Sundae makeASundae();
}
} ///-
Перед вами пример ситуации, в которой private может быть очень полезен: предположим, вы хотите контролировать процесс создания объекта, не разрешая посторонним вызывать конкретный конструктор (или любые конструкторы). В данном примере запрещается создавать объекты Sundae с помощью конструктора; вместо этого пользователь должен использовать метод makeASundae().
Все «вспомогательные» методы классов стоит объявить как private, чтобы предотвратить их случайные вызовы в пакете; тем самым вы фактически запрещаете изменение поведения метода или его удаление.
То же верно и к private-полям внутри класса. Если только вы не собираетесь предоставить доступ пользователям к внутренней реализации (а это происходит гораздо реже, чем можно себе представить), объявляйте все поля своих классов со спецификатором private.
protected
Чтобы понять смысл спецификатора доступа protected, необходимо немного забежать вперед. Сразу скажу, что понимание этого раздела не обязательно до знакомства с наследованием (глава 7). И все же для получения цельного представления здесь приводится описание protected и примеры его использования.
Читать дальше