"A hozzáférésmódosítókról fogok beszélni . Már egyszer beszéltem róluk, de az ismétlés a tanulás egyik pillére."

Szabályozhatja a többi osztály hozzáférését (láthatóságát) az osztály metódusaihoz és változóihoz. Egy hozzáférés-módosító válaszol a „Ki férhet hozzá ehhez a módszerhez/változóhoz?” kérdésre. Minden metódushoz vagy változóhoz csak egy módosítót adhat meg.

1) « nyilvános » módosító.

A nyilvános módosítóval jelölt változó, metódus vagy osztály a program bármely pontjáról elérhető. Ez a nyitottság legmagasabb foka: nincsenek korlátozások.

2) « privát » módosító.

A privát módosítóval megjelölt változó, metódus vagy osztály csak abban az osztályban érhető el, ahol deklarálva van. A megjelölt metódus vagy változó el van rejtve az összes többi osztály elől. Ez a legmagasabb szintű adatvédelem: csak az osztályod férhet hozzá. Az ilyen módszerek nem öröklődnek, és nem bírálhatók felül. Ezenkívül nem érhetők el leszármazott osztályból.

3)  « Alapértelmezett módosító».

Ha egy változó vagy metódus nincs megjelölve semmilyen módosítóval, akkor az "alapértelmezett" módosítóval megjelöltnek minősül. Az ezzel a módosítóval rendelkező változók és metódusok a csomag minden osztálya számára láthatók, ahol deklarálva vannak, és csak azok az osztályok. Ezt a módosítót " csomagnak " vagy " csomag privát hozzáférésnek" is nevezik , utalva arra, hogy a változókhoz és metódusokhoz való hozzáférés az osztályt tartalmazó teljes csomag számára nyitva áll.

4) " védett " módosító.

Ez a hozzáférési szint valamivel szélesebb, mint a csomag . A védett módosítóval megjelölt változó, metódus vagy osztály elérhető a csomagjából (mint például a "csomag"), és minden örökölt osztályból.

Ez a táblázat mindent elmagyaráz:

A láthatóság típusa Kulcsszó Hozzáférés
Osztályod A csomagod Leszármazott Minden osztály
Magán magán Igen Nem Nem Nem
Csomag (nincs módosító) Igen Igen Nem Nem
Védett védett Igen Igen Igen Nem
Nyilvános nyilvános Igen Igen Igen Igen

Van egy módja annak, hogy könnyen megjegyezze ezt a táblázatot. Képzeld el, hogy végrendeletet írsz. Az összes dolgait négy kategóriába sorolja. Ki használhatja a dolgaidat?

Kinek van hozzáférése Módosító Példa
Csak  én magán Személyes napló
Család (nincs módosító) Családi fotók
Család és örökösök védett Családi birtok
Mindenki nyilvános Emlékiratok

"Nagyon olyan, mintha azt képzelnénk, hogy az ugyanabban a csomagban lévő osztályok egy család részei."

– Szeretnék néhány érdekes árnyalatot is elmesélni a módszerek felülbírálásával kapcsolatban.

1) Absztrakt módszer implicit megvalósítása.

Tegyük fel, hogy a következő kóddal rendelkezik:

Kód
class Cat
{
 public String getName()
 {
  return "Oscar";
 }
}

És úgy döntött, hogy létrehoz egy Tiger osztályt, amely örökli ezt az osztályt, és hozzáad egy felületet az új osztályhoz

Kód
class Cat
{
 public String getName()
 {
   return "Oscar";
 }
}
interface HasName
{
 String getName();
 int getWeight();
}
class Tiger extends Cat implements HasName
{
 public int getWeight()
 {
  return 115;
 }

}

Ha csak megvalósítja az összes hiányzó metódust, amelyet az IntelliJ IDEA javasol, később hosszú időt tölthet a hibakereséssel.

Kiderült, hogy a Tiger osztály rendelkezik egy Cat-től örökölt getName metódussal, amely a HasName felület getName metódusának megvalósítása lesz.

– Nem látok ebben semmi szörnyűt.

"Nem túl rossz, valószínű helye a hibáknak."

De lehet még rosszabb is:

Kód
interface HasWeight
{
 int getValue();
}
interface HasSize
{
 int getValue();
}
class Tiger extends Cat implements HasWeight, HasSize
{
 public int getValue()
 {
  return 115;
 }
}

Kiderült, hogy nem mindig lehet több interfészről örökölni. Pontosabban örökölheted őket, de nem tudod helyesen megvalósítani. Nézd meg a példát. Mindkét interfész megköveteli a getValue() metódus megvalósítását, de nem világos, hogy mit kell visszaadnia: a súlyt vagy a méretet? Ezzel elég kellemetlen foglalkozni.

"Egyetértek. Szeretnél egy metódust implementálni, de nem tudod. Már örököltél egy azonos nevű metódust az alaposztályból. Elromlott."

– De van egy jó hír.

2) A láthatóság bővítése. Ha egy típust örököl, bővítheti a metódus láthatóságát. Így néz ki:

Java kód Leírás
class Cat
{
 protected String getName()
 {
  return "Oscar";
 }
}
class Tiger extends Cat
{
 public String getName()
 {
  return "Oscar Tiggerman";
 }
}
A módszer láthatóságát bővítettük ról protected-ra public.
Kód Miért "legális"
public static void main(String[] args)
{
 Cat cat = new Cat();
 cat.getName();
}
Minden nagyszerű. Itt nem is tudjuk, hogy egy leszármazott osztályban bővült a láthatóság.
public static void main(String[] args)
{
 Tiger tiger = new Tiger();
 tiger.getName();
}
Itt azt a módszert nevezzük, amelynek láthatósága kibővült.

Ha ez nem lehetséges, mindig deklarálhatnánk egy metódust a Tigerben:
public String getPublicName()
{
super.getName(); //a védett metódus meghívása
}

Más szóval, nem beszélünk semmilyen biztonsági megsértésről.

public static void main(String[] args)
{
 Cat catTiger = new Tiger();
 catTiger.getName();
}
Ha az alaposztály ( Cat ) metódusának meghívásához szükséges összes feltétel teljesül, akkor ezek minden bizonnyal teljesülnek a leszármazott típus ( Tiger ) metódus meghívásához. Mert a metódushívás korlátozásai gyengék voltak, nem erősek.

– Nem vagyok benne biztos, hogy teljesen megértettem, de emlékezni fogok rá, hogy ez lehetséges.

3) A visszatérési típus szűkítése.

Felülírt metódusban a visszatérési típust módosíthatjuk szűkített referenciatípusra.

Java kód Leírás
class Cat
{
 public Cat parent;
 public Cat getMyParent()
 {
  return this.parent;
 }
 public void setMyParent(Cat cat)
 {
  this.parent = cat;
 }
}
class Tiger extends Cat
{
 public Tiger getMyParent()
 {
  return (Tiger) this.parent;
 }
}
Felülírtuk a metódust getMyParent, és most egy objektumot ad vissza Tiger.
Kód Miért "legális"
public static void main(String[] args)
{
 Cat parent = new Cat();

 Cat me = new Cat();
 me.setMyParent(parent);
 Cat myParent = me.getMyParent();
}
Minden nagyszerű. Itt még azt sem tudjuk, hogy a getMyParent metódus visszatérési típusát kiszélesítették a leszármazott osztályban.

Hogyan működött és működik a „régi kód”.

public static void main(String[] args)
{
 Tiger parent = new Tiger();

 Tiger me = new Tiger();
 me.setMyParent(parent);
 Tiger myParent = me.getMyParent();
}
Itt azt a metódust nevezzük, amelynek visszatérési típusát szűkítettük.

Ha ez nem lehetséges, mindig deklarálhatnánk egy metódust a Tigerben:
public Tiger getMyTigerParent()
{
return (Tiger) this.parent;
}

Más szóval, nincsenek biztonsági megsértések és/vagy típus-öntvény-sértések.

public static void main(String[] args)
{
 Tiger parent = new Tiger();

 Cat me = new Tiger();
 me.setMyParent(parent);
 Cat myParent = me.getMyParent();
}
És itt minden jól működik, bár a változók típusát kiterjesztettük az alaposztályra (Cat).

A felülbírálás miatt a megfelelő setMyParent metódus kerül meghívásra.

A getMyParent metódus meghívásakor pedig nincs ok aggodalomra , mert a visszatérési érték, bár a Tiger osztályé, továbbra is gond nélkül hozzárendelhető az alaposztály (Cat) myParent változójához .

A tigris objektumok biztonságosan tárolhatók Tigris változókban és Cat változókban is.

"Igen. Értem. A metódusok felülbírálásakor tisztában kell lennie azzal, hogy mindez hogyan működik, ha objektumainkat olyan kódnak adjuk át, amely csak az alaposztályt tudja kezelni, és nem tud semmit az osztályunkról. "

"Pontosan! Akkor a nagy kérdés az, hogy miért nem tudjuk szűkíteni a visszatérési érték típusát egy metódus felülbírálásakor?"

"Nyilvánvaló, hogy ebben az esetben az alaposztály kódja leállna:"

Java kód A probléma magyarázata
class Cat
{
 public Cat parent;
 public Cat getMyParent()
 {
  return this.parent;
 }
 public void setMyParent(Cat cat)
 {
  this.parent = cat;
 }
}
class Tiger extends Cat
{
 public Object getMyParent()
 {
  if (this.parent != null)
   return this.parent;
  else
   return "I'm an orphan";
 }
}
Túlterheltük a getMyParent metódust, és leszűkítettük a visszatérési értékének típusát.

Itt minden rendben van.

public static void main(String[] args)
{
 Tiger parent = new Tiger();

 Cat me = new Tiger();
 Cat myParent = me.getMyParent();
}
Akkor ez a kód leáll.

A getMyParent metódus egy objektum bármely példányát visszaadhatja, mert valójában egy Tiger objektum hívja meg.

És nincs csekkünk a feladat előtt. Így teljesen lehetséges, hogy a Cat-típusú myParent változó egy String hivatkozást fog tárolni.

– Csodálatos példa, Amigo!

Java-ban a metódus meghívása előtt nincs ellenőrzés, hogy az objektum rendelkezik-e ilyen metódussal. Minden ellenőrzés futás közben történik. És egy hiányzó metódus [hipotetikus] hívása nagy valószínűséggel arra késztetné a programot, hogy megkíséreljen nem létező bájtkódot végrehajtani. Ez végül végzetes hibához vezetne, és az operációs rendszer erőszakkal bezárná a programot.

– Hú. Most már tudom.