
- •Основы классов
- •Общая форма класса
- •Простой класс
- •Объявление объектов
- •Операция new
- •Представление методов
- •Добавление метода к классу Box
- •Возврат значений
- •Добавление метода с параметрами
- •Конструкторы
- •Параметризованные конструкторы
- •Ключевое слово this
- •Скрытие переменной экземпляра
- •Сборка "мусора"
- •Метод finalize()
- •Класс Stack
- •Перегрузка конструкторов
- •Использование объектов в качестве параметров
- •Передача аргументов
- •Возврат объектов
- •Рекурсия
- •Управление доступом
- •Статические элементы
- •Спецификатор final
- •Ревизия массивов
- •Вложенные и внутренние классы
- •Класс String
- •Использование аргументов командной строки
- •Наследование
- •Основы наследования
- •11 Тело класса }
- •Доступ к элементам и наследование
- •Практический пример
- •Переменная суперкласса может ссылаться на объект подкласса
- •Использование ключевого слова super
- •Вызов конструктора суперкласса с помощью первой формы super
- •Создание многоуровневой иерархии
- •II построить клон объекта
- •Когда вызываются конструкторы
- •Переопределение методов
- •Динамическая диспетчеризация методов
- •Зачем нужны переопределенные методы?
- •Применение переопределения методов
- •Использование абстрактных классов
- •Void meth() { // ошибка! Нельзя переопределять.
- •Класс Object
Управление доступом
Напомним, что инкапсуляция связывает данные с кодом, который манипулирует ими. Однако инкапсуляция обеспечивает другой важный атрибут:
управление доступом. Через инкапсуляцию можно управлять доступом различных частей программы к членам класса и предотвращать неправильное использование таких членов. Например, разрешая доступ к данным только через хорошо определенный набор методов, есть возможность предотвращения неверного использования этих данных. Таким образом, при правильной реализации класс создает для использования "черный ящик", но внутренняя работа которого не доступна для вмешательства. Однако представленные ранее классы не полностью удовлетворяют этой цели. Например, рассмотрим класс stack, показанный в конце главы 6. Хотя верно, что методы push о и pop () обеспечивают управляемый интерфейс к стеку, интерфейс этот не предписан. То есть, другая часть программы может обойти указанные методы и получить прямой доступ к стеку. Конечно, в неумелых руках это может привести к неприятностям. В данном разделе вашему вниманию будет представлен механизм, с помощью которого можно точно управлять доступом к различным членам класса.
Способ доступа к элементу определяет спецификатор доступа, который модифицирует его объявление, и Java поставляет их богатый набор. Некоторые аспекты управления доступом относятся главным образом к наследованию или пакетам. (Пакет – это, по существу, группировка классов.) Данные части механизма управления доступом Java будут обсуждены позже. Здесь начнем с рассмотрения управления доступом в применении к отдельному классу. Как только вы поймете основные принципы управления доступом, остальное будет просто.
Спецификаторы доступа Java: public (общий), private (частный) и protected (защищенный). Java также определяет уровень доступа, заданный по умолчанию (default access level). Спецификатор protected применяется только при использовании наследования.
Начнем с определения спецификаторов public и private. Когда элемент класса модифицирован спецификатором public, то к этому элементу возможен доступ из любой точки программы. Если член класса определен как private, к нему могут обращаться только члены этого класса. Теперь вы можете понять, почему методу main о всегда предшествовал спецификатор public. Он вызывается кодом, который находится вне программы – т. е. исполняющей системой Java. Когда никакой спецификатор доступа не используется, по умолчанию элемент класса считается public в пределах своего собственного пакета, но к нему нельзя обращаться извне этого пакета. (Пакеты обсуждаются в следующей главе.)
В классах, разработанных до этого момента, все их члены использовали заданный по умолчанию режим доступа, который является по существу общим (public). Однако это не то, что вам обычно будет нужно. Скорее всего, вы захотите ограничить доступ к компонентам данных класса, разрешая доступ только через методы. Возможно, наступит время, когда вы захотите определить методы, которые являются частными (private) для класса.
Спецификатор доступа предшествует остальной части описания типа элемента (члена) класса. То есть он должен начинать предложение объявления такого элемента. Например:
public int i;
private double j ;
private int myMethod(int a, char b) ; {
. . .
}
Чтобы понимать специфику общего и частного доступа, рассмотрим следующую программу:
// Эта программа демонстрирует различие между методами доступа public и private.
class Test {
int a; // доступ по умолчанию (public)
public // общий (public) доступ
int b ;
private // частный (private) доступ
int с ;
// методы для доступа к переменной с
void setc(int i) { // установить значение с
с = I ;
} int getc() { // получить значение с
return с ;
}
}
class AccessTest {
public static void main(String args[]) {
Test ob = new Test();
.
ob.a = 10;
ob.b = 20; // верно, к переменным а и b возможен прямой доступ
// ob.c = 100; // неверно и вызовет ошибку
ob.setc(l00) ; // верно; доступ к с через метод
System.out.println("a, b, и с: " + ob.a + " " + ob.b т " " + ob.getcO) ;
}
}
Внутри класса Test используется доступ по умолчанию, который для этого примера эквивалентен указанию public. Член ь явно определен как public.
Для члена с задан доступ private. Это означает, что к нему нельзя обращаться из кода, находящегося вне его класса. Так внутри класса AccessTest переменная с не может использоваться прямо. К ней нужно обращаться через ее public-методы setc () и getc (). Если бы вы удалили символ комментария в начале следующей строки, то не смогли бы откомпилировать эту программу из-за нарушения правил доступа:
// ob.c = 100; // Ошибка!
Чтобы увидеть, как управление доступом может применяться в более практическом примере, рассмотрим следующую улучшенную версию класса stack, показанного в конце главы 6.
// Этот класс определяет целый стек, который может содержать 10 значений.
class Stack {
private int stck [ ] = new int[10];
private int tos;
/* Теперь как stck, так и tos есть private. Это значит, что они не могут быть случайно или
намеренно изменены опасным для стека способом. */
Stack () { // инициализировать вершину стека
tos = -1;
}
void push(int item) { // поместить элемент в стек
if (tos==9)
System.out.println ("Стек заполнен.")
else
stck[++tos] = item;
}
int pop () { // вытолкнуть элемент из стека
if (tos < 0) {
System.out.println("Стек пуст,
return 0;
}
else
return stck[tos–];
}
}
Теперь переменная stck, которая содержит стек, и tos, которая является индексом вершины стека, определены как private. Это означает, что к ним нельзя обращаться или изменять их, кроме как через методы push () и pop ().
Делая tos частной, мы, например, предохраняем другие части программы от неосторожной установки в ней значения, которое находится вне границ массива stck.
Следующая программа демонстрирует улучшенный класс stack. Попробуйте удалить закомментированные строки для доказательства (самим себе), что члены stck и tos действительно недоступны.
class TestStack {
public static void main (String args[j) {
Stack mystackl = new Stack ();
Stack mystack2 = new Stack ();
// поместить несколько чисел в стек
for (int i=0; i<10; i++) mystackl .push (i) ;
For (int i=10; i<20; i++) mystack2 .push (i);
// вытолкнуть эти числа из стека
System.out.println("Стек в mystackl:"); for (int 1=0; К10; i++)
System.out.println(mystackl.pop ());
System.out.println("Стек в mystack2:"); for(int i=0; i<10; i++)
System.out.println(mystack2.pop'O );
// эти операторы не верны
// mystackl.tos = -2;
// mystack2.stck[3] = 100;
}
}
Доступ к данным, определенным в классе со спецификатором private, выполняется только через методы этого класса. Но иногда (и по разным причинам) возникает необходимость обойти эти ограничения. Например, с помощью спецификатора общего доступа . public можно расширить область действия переменных экземпляра за пределы своего класса и, следовательно, получить возможность работать с ними не только через их собственные методы. Значительная часть классов в этой книге ради простоты создавалась вообще без управления доступом экземплярных переменных. Однако в большинстве реальных классов нужно разрешать операции на данных только через собственные методы. Следующая глава вернется к теме управления доступом. Как вы увидите, это особенно важно, когда нужно пользоваться наследованием.