Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
41
Добавлен:
12.05.2015
Размер:
624.64 Кб
Скачать

Управление доступом

Напомним, что инкапсуляция связывает данные с кодом, который манипу­лирует ими. Однако инкапсуляция обеспечивает другой важный атрибут:

управление доступом. Через инкапсуляцию можно управлять доступом раз­личных частей программы к членам класса и предотвращать неправильное использование таких членов. Например, разрешая доступ к данным только через хорошо определенный набор методов, есть возможность предотвраще­ния неверного использования этих данных. Таким образом, при правильной реализации класс создает для использования "черный ящик", но внутренняя работа которого не доступна для вмешательства. Однако представленные ранее классы не полностью удовлетворяют этой цели. Например, рассмот­рим класс 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 можно расширить область действия переменных экземпляра за пределы своего класса и, следователь­но, получить возможность работать с ними не только через их собственные методы. Значительная часть классов в этой книге ради простоты создава­лась вообще без управления доступом экземплярных переменных. Однако в большинстве реальных классов нужно разрешать операции на данных толь­ко через собственные методы. Следующая глава вернется к теме управления доступом. Как вы увидите, это особенно важно, когда нужно пользоваться наследованием.

Соседние файлы в папке JavaLit