Спецификаторите на достъпа в Java public, protected и private се слагат пред всяка дефиниция на всеки член в класовете, бил той член-данни или метод. Всеки спецификатор управлява достъпа до само тази конкретна дефиниция. Това е значителен контраст спрямо C++, където спецификаторът на достъпа управлява всички следващи дефиниции докато не се появи нов спецификатор на достъпа.
По един или друг начин за всяко нещо е определен вид достъп. В следващите секции ще научите всичко за достъпа, започвайки с този по подразбиране.
“Приятелски”
Какво става ако въобще не дадете спецификатор на достъпа както във всичките примери досега от тази глава? Достъпът по подразбиране няма ключова дума но често се споменава като “приятелски.” Това значи че всички други класове от пакета имат достъп до приятелския член, но всички класове извън пакета нямат достъп. Тъй като компилационната единица – файл – може да принадлежи само на един пакет, всичките класове в компилационна единица са автоматично приятелски един на друг. Така приятелските елементи още казваме че имат пакетен достъп.
Приятелският достъп позволява да се групират класовете в пакет така че да имат възможност лесно да си взаимодействат. Като сложите класове заедно в пакет (давайки с това изключителен достъп до техните приятелски членове; т.е. правейки ги “приятели”) вие “владеете” кода в пакета. Има смисъл само кода който владеете да има достъп до код който вие владеете. Би могло да се каже, че приятелският достъп дава обяснение или причина за групиране на класовете във файлове. В много езици начинът на организиране на файловете е щеш-нещеш, но в Java сте задължени да го направите по смислен признак. В добавка вие вероятно бихте предпочели да изключите (от файла - бел.пр.) класа който не трябва да има достъп до класовете във файла.
Важен въпрос във всяка зависимост е “Кой има достъп до моята private реализация?” Класът контролира кой код има достъп до неговите членове. Няма магически начин да се “пробие;” някой в друг клас не може да дефинира нов клас и да каже, “Ей, аз съм приятелски на класа на Bob!” и да очаква да види protected, приятелски, и private членове на Bob. Единствения начин да се даде достъп до член е да:
-
Се направи члена public. Тогава всеки и навсякъде има достъп до него.
-
Се направи члена приятелски, като се остави без спецификатор на достъпа и да се сложат другите класове в същия пакет. Тогава другите класове имат достъп до члена.
-
Както ще видите в по-късна глава където се въвежда наследяването, наследникът има достъп до protected член а също и до public член (но не private членове). Той има достъп до приятелските членове само ако двата класа са в един пакет. Но не се занимавайте с това сега.
-
Се даде “accessor/mutator” методи (известни още като “get/set” методи) които четат/променят стойността. Това е най-цивилизованият подход в термините на ООП и той е основен за Java Beans, както ще видите в глава 13.
public: интерфейсен достъп
Като се използва ключовата дума public това значи, че членът който непосредствено я следва е достъпен за всеки, в частност за клиент-програвиста, който използва библиотеката. Нека дефинираме пакет dessert съдържащ следната компилационна единица: (Виж страница 89 ако има проблеми с пускането на програмата.)
//: c05:dessert:Cookie.java
// Creates a library
package c05.dessert;
public class Cookie {
public Cookie() {
System.out.println("Cookie constructor");
}
void foo() { System.out.println("foo"); }
} ///:~
Помнете, Cookie.java трябва да е в поддиректория с име dessert, в директория под C05 (индициращо глава 5 на тази книга) която трябва да е под една от CLASSPATH директориите. Не правете грешката да мислите, че Java винаги ще гледа на текущата директория като на една от началните точки на търсенето. Ако няма ‘.’ Като един от пътищата във въшия CLASSPATH Java няма да търси там.
Ако сега създадем програма която използва Cookie:
//: c05:Dinner.java
// Uses the library
import c05.dessert.*;
public class Dinner {
public Dinner() {
System.out.println("Dinner constructor");
}
public static void main(String[] args) {
Cookie x = new Cookie();
//! x.foo(); // Can't access
}
} ///:~
Може да създадете Cookie обект, тъй като конструкторът му е public и класът е public. (По-късно ще разгледаме публичните класове още.) Обаче foo( ) членът е недостъпен вътре в Dinner.java понеже foo( ) е приятелски само в пакета dessert.
Пакетът по подразбиране
Може да сте изненадани че следният код се компилира, макар че сякаш нарушава правилата:
//: c05:Cake.java
// Accesses a class in a separate
// compilation unit.
class Cake {
public static void main(String[] args) {
Pie x = new Pie();
x.f();
}
} ///:~
Във втория файл, в същата директория:
//: c05:Pie.java
// The other class
class Pie {
void f() { System.out.println("Pie.f()"); }
} ///:~
На пръв поглед може да ви се сторят напълно чужди файлове и все пак Cake може да създаде Pie обект и да извика неговия f( ) метод! Типично се очаква че Pie и f( ) са приятелски и затова недостъпни за Cake. Те са приятелски – тази част е точна. Причината да са достъпни в Cake.java че са в една директория и нямат явно пакетно име. Java третира файлове като тези неявно като от “пакет по подразбиране” за нея директория, следователно като приятелски на всички файлове в нея директория.
private: не може да пипате това!
Ключовата дума private означава, че никой няма право на достъп освен конкретния клас, вътре в методите на класа. Други класове в пакета нямат достъп до private членовете, каточели класът е изолиран и от самия себе си. От друга страна, не е невероятно класът да е създаден от няколко души работещи заедно, така че private позволява членът да се променя без да се засягат други класове в пакета. Подразбиращия се “приятелски” достъп в пакета е често достатъчна степен на скриване; запомнете, “приятелски” член е недостъпен за потребител на пакета. Това е добре, той като нормално се използва достъпът по подразбиране. Така типично членовете, които ще са достъпни за клиента-програмист ще са public и като резултат първоначално може да считате че няма много да използвате private тъй като сносно може да се разминете без тази ключова дума. (Това е значителен контраст със C++.) Оказва се обаче че смисленото използване на private е много важно, специално когато се прави многонишковост. (Както ще видите в глава 14.)
Ето пример за използване на private:
//: c05:IceCream.java
// Demonstrates "private" keyword
class Sundae {
private Sundae() {}
static Sundae makeASundae() {
return new Sundae();
}
}
public class IceCream {
public static void main(String[] args) {
//! Sundae x = new Sundae();
Sundae x = Sundae.makeASundae();
}
} ///:~
Това е пример където private се оказва удобно: бихте могли да искате да управлявате създаването на обект и да не допуснете някой да използва даден конструктор (или всичките). В примера по-горе не може да се създаде Sundae обект чрез неговия конструктор; трябва да се извика вместо това makeASundae( ) за да го създаде.3
Всеки метод за който сте сигурни че е спомагателен в класа може да бъде направен private за да се осигури неупотребата му на друго място в класа и по този начин да се остави възможност за замяната му. Правенето на метода private това. (Обаче това че манипулаторът е private не значи, че други обекти не могат да имат public манипулатор към същия обект. Виж глава 12 за въпросите на алиасинга (псевдонимите).)
protected: “вид приятелски”
Спецификаторът на достъп protected изисква скок напред за да бъде разбран. Първо, ще бъдете предупредени, че разбирането на въпроса не е наложително докато не разберете наследяването. Но за пълнота тук има кратко описание и примери с protected.
Ключовата дума protected се преплита с концепцията за наследяване, която взема съществуващ клас и добавя членове без да се пипа съществуващия клас, който ще наричаме базов клас. Може също да се промени поведението на съществуващ член на клас. За да наследите от съществуващ клас казвате че новият extends (разширява - бел.пр.) съществуващ клас, както тук:
class Foo extends Bar {
Останалата част от дефиницията на класа изглежда по същия начин.
Ако създавате нов пакет и наследявате от друг пакет, единствените членове до които имате достъп са public членовете на оригиналния пакет. (Разбира се, ако наследявате в същия пакет ще имате нормалния пакетен достъп до всички “приятелски” членове.) Понякога създателят на клас иска да вземе отделни членове и да даде достъп до тях само на наслидниците на класа, а не на целия свят. Това е, което прави protected. Ако погледнете пак Cookie.java на стр. 192, следващият клас няма достъп до “приятелския” член:
//: c05:ChocolateChip.java
// Can't access friendly member
// in another class
import c05.dessert.*;
public class ChocolateChip extends Cookie {
public ChocolateChip() {
System.out.println(
"ChocolateChip constructor");
}
public static void main(String[] args) {
ChocolateChip x = new ChocolateChip();
//! x.foo(); // Can't access foo
}
} ///:~
Едно от интересните неща за наследяването е че ако методът foo( ) съществува в клас Cookie, той също съществува и във всеки клас, наследен от Cookie. Но доколкото foo( ) е “приятелски” във външен пакет, той е недостъпен за нас в този. Разбира се, бихте могли да го направите public, но тогава всеки би имал достъп и това може да не е което искате. Ако променим класа Cookie така:
public class Cookie {
public Cookie() {
System.out.println("Cookie constructor");
}
protected void foo() {
System.out.println("foo");
}
}
тогава foo( ) още има “приятелски” достъп в пакета dessert, но също е достъпен за всеки, който наследява от Cookie. Обаче не е public.
Сподели с приятели: |