„O să vă povestesc despre „ modificatorii de acces ”. Am mai povestit despre ei o dată, dar repetarea este un pilon al învățării.”
Puteți controla accesul (vizibilitatea) pe care îl au alte clase la metodele și variabilele clasei dvs. Un modificator de acces răspunde la întrebarea „Cine poate accesa această metodă/variabilă?”. Puteți specifica un singur modificator pentru fiecare metodă sau variabilă.
1) modificator „ public ”.
O variabilă, o metodă sau o clasă marcată cu modificatorul public poate fi accesată de oriunde în program. Acesta este cel mai înalt grad de deschidere: nu există restricții.
2) modificatorul „ privat ”.
O variabilă, o metodă sau o clasă marcată cu modificatorul privat poate fi accesată numai în clasa în care este declarată. Metoda sau variabila marcată este ascunsă de toate celelalte clase. Acesta este cel mai înalt grad de confidențialitate: accesibil doar de către clasa dvs. Astfel de metode nu sunt moștenite și nu pot fi înlocuite. În plus, acestea nu pot fi accesate într-o clasă descendentă.
3) « Modificator implicit ».
Dacă o variabilă sau o metodă nu este marcată cu niciun modificator, atunci este considerată a fi marcată cu modificatorul „implicit”. Variabilele și metodele cu acest modificator sunt vizibile pentru toate clasele din pachetul în care sunt declarate și numai pentru acele clase. Acest modificator se mai numește acces „ pachet ” sau „ pachet privat ”, indicând faptul că accesul la variabile și metode este deschis întregului pachet care conține clasa.
4) modificator „ protejat ”.
Acest nivel de acces este puțin mai larg decât pachetul . O variabilă, metodă sau clasă marcată cu modificatorul protejat poate fi accesată din pachetul său (cum ar fi „pachet”) și din toate clasele moștenite.
Acest tabel explică totul:
Tip de vizibilitate | Cuvânt cheie | Acces | |||
---|---|---|---|---|---|
Clasa ta | Pachetul dvs | Descendent | Toate clasele | ||
Privat | privat | da | Nu | Nu | Nu |
Pachet | (fără modificator) | da | da | Nu | Nu |
Protejat | protejat | da | da | da | Nu |
Public | public | da | da | da | da |
Există o modalitate de a vă aminti cu ușurință acest tabel. Imaginează-ți că scrii un testament. Împărți toate lucrurile în patru categorii. Cine poate să-ți folosească lucrurile?
Cine are acces | Modificator | Exemplu |
---|---|---|
Doar eu | privat | Jurnal personal |
Familie | (fără modificator) | Fotografii de familie |
Familia și moștenitorii | protejat | Moșie de familie |
Toata lumea | public | Memorii |
„Este mult ca să-ți imaginezi că clasele din același pachet fac parte dintr-o singură familie.”
„Vreau să vă spun și câteva nuanțe interesante despre metodele de suprapunere.”
1) Implementarea implicită a unei metode abstracte.
Să presupunem că aveți următorul cod:
class Cat
{
public String getName()
{
return "Oscar";
}
}
Și ați decis să creați o clasă Tiger care moștenește această clasă și să adăugați o interfață noii clase
class Cat
{
public String getName()
{
return "Oscar";
}
}
interface HasName
{
String getName();
int getWeight();
}
class Tiger extends Cat implements HasName
{
public int getWeight()
{
return 115;
}
}
Dacă doar implementați toate metodele lipsă pe care IntelliJ IDEA vă spune să le implementați, mai târziu s-ar putea să petreceți mult timp căutând o eroare.
Se pare că clasa Tiger are o metodă getName moștenită de la Cat, care va fi luată ca implementare a metodei getName pentru interfața HasName.
— Nu văd nimic groaznic în asta.
„Nu este prea rău, este un loc probabil în care să se strecoare greșeli.”
Dar poate fi și mai rău:
interface HasWeight
{
int getValue();
}
interface HasSize
{
int getValue();
}
class Tiger extends Cat implements HasWeight, HasSize
{
public int getValue()
{
return 115;
}
}
Se pare că nu puteți moșteni întotdeauna de la mai multe interfețe. Mai exact, le poți moșteni, dar nu le poți implementa corect. Uita-te la exemplu. Ambele interfețe necesită să implementați metoda getValue(), dar nu este clar ce ar trebui să returneze: greutatea sau dimensiunea? Este destul de neplăcut să ai de-a face cu asta.
"Sunt de acord. Doriți să implementați o metodă, dar nu puteți. Ați moștenit deja o metodă cu același nume din clasa de bază. Este ruptă."
— Dar sunt vești bune.
2) Extinderea vizibilității. Când moșteniți un tip, puteți extinde vizibilitatea unei metode. Așa arată:
Cod Java | Descriere |
---|---|
|
|
|
Am extins vizibilitatea metodei de protected la la public . |
Cod | De ce este „legal” |
---|---|
|
Totul e grozav. Aici nici nu știm că vizibilitatea a fost extinsă într-o clasă descendentă. |
|
Aici numim metoda a cărei vizibilitate a fost extinsă.
Dacă acest lucru nu ar fi posibil, am putea declara întotdeauna o metodă în Tiger: Cu alte cuvinte, nu vorbim despre nicio încălcare a securității. |
|
Dacă sunt îndeplinite toate condițiile necesare pentru apelarea unei metode într-o clasă de bază ( Cat ), atunci cu siguranță sunt îndeplinite pentru apelarea metodei pe tipul descendent ( Tiger ) . Pentru că restricțiile privind apelul metodei au fost slabe, nu puternice. |
„Nu sunt sigur că am înțeles complet, dar îmi voi aminti că acest lucru este posibil”.
3) Restrângerea tipului de returnare.
Într-o metodă suprascrisă, putem schimba tipul de returnare la un tip de referință restrâns.
Cod Java | Descriere |
---|---|
|
|
|
Am suprasolicitat metoda getMyParent și acum returnează un Tiger obiect. |
Cod | De ce este „legal” |
---|---|
|
Totul e grozav. Aici nici măcar nu știm că tipul de returnare al metodei getMyParent a fost extins în clasa descendentă.
Cum a funcționat și cum funcționează „codul vechi”. |
|
Aici numim metoda al cărei tip de returnare a fost restrâns.
Dacă acest lucru nu ar fi posibil, am putea declara întotdeauna o metodă în Tiger: Cu alte cuvinte, nu există încălcări de securitate și/sau încălcări de tip casting. |
|
Și totul funcționează bine aici, deși am extins tipul variabilelor la clasa de bază (Cat).
Din cauza suprascrierii, este apelată metoda corectă setMyParent. Și nu este nimic de care să vă faceți griji atunci când apelați metoda getMyParent , deoarece valoarea returnată, deși a clasei Tiger, poate fi totuși atribuită variabilei myParent a clasei de bază (Cat) fără probleme. Obiectele Tiger pot fi stocate în siguranță atât în variabilele Tiger, cât și în variabilele Cat. |
„Da. Am înțeles. Când suprascrieți metodele, trebuie să fiți conștienți de cum funcționează toate acestea dacă ne transmitem obiectele unui cod care poate gestiona doar clasa de bază și nu știe nimic despre clasa noastră. ”
"Exact! Atunci marea întrebare este de ce nu putem restrânge tipul valorii returnate atunci când suprascriem o metodă?"
„Este evident că în acest caz codul din clasa de bază ar înceta să funcționeze:”
Cod Java | Explicația problemei |
---|---|
|
|
|
Am supraîncărcat metoda getMyParent și am restrâns tipul valorii returnate.
Totul este bine aici. |
|
Atunci acest cod nu va mai funcționa.
Metoda getMyParent poate returna orice instanță a unui Object, deoarece este de fapt apelată pe un obiect Tiger. Și nu avem o verificare înainte de misiune. Astfel, este cu totul posibil ca variabila myParent de tip Cat să stocheze o referință String. |
— Exemplu minunat, Amigo!
În Java, înainte de apelarea unei metode, nu se verifică dacă obiectul are o astfel de metodă. Toate verificările au loc în timpul execuției. Și un apel [ipotetic] la o metodă lipsă ar determina cel mai probabil programul să încerce să execute bytecode inexistent. Acest lucru ar duce în cele din urmă la o eroare fatală, iar sistemul de operare ar închide forțat programul.
"Uau. Acum știu."
GO TO FULL VERSION