"Jeg vil fortælle dig om " adgangsmodifikatorer ". Jeg fortalte om dem en gang før, men gentagelse er en søjle for læring."

Du kan kontrollere den adgang (synlighed), som andre klasser har til din klasses metoder og variable. En adgangsmodifikator besvarer spørgsmålet «Hvem kan få adgang til denne metode/variabel?». Du kan kun angive én modifikator for hver metode eller variabel.

1) " offentlig " modifikator.

En variabel, metode eller klasse markeret med den offentlige modifikator kan tilgås fra hvor som helst i programmet. Dette er den højeste grad af åbenhed: Der er ingen begrænsninger.

2) « privat » modifikator.

En variabel, metode eller klasse markeret med den private modifikator kan kun tilgås i den klasse, hvor den er erklæret. Den markerede metode eller variabel er skjult fra alle andre klasser. Dette er den højeste grad af privatliv: kun tilgængelig for din klasse. Sådanne metoder nedarves ikke og kan ikke tilsidesættes. Derudover kan de ikke tilgås i en efterkommerklasse.

3)  « Standardmodifikator ».

Hvis en variabel eller metode ikke er markeret med nogen modifikator, anses den for at være markeret med "standard" modifikatoren. Variabler og metoder med denne modifikator er synlige for alle klasser i pakken, hvor de er erklæret, og kun for disse klasser. Denne modifikator kaldes også " pakke " eller " pakke privat " adgang, hvilket antyder, at adgang til variabler og metoder er åben for hele pakken, der indeholder klassen.

4) « beskyttet » modifikator.

Dette adgangsniveau er lidt bredere end pakken . En variabel, metode eller klasse markeret med den beskyttede modifikator kan tilgås fra dens pakke (som "pakke") og fra alle nedarvede klasser.

Denne tabel forklarer det hele:

Type synlighed Søgeord Adgang
Din klasse Din pakke Descendent Alle klasser
Privat privat Ja Ingen Ingen Ingen
Pakke (ingen modifikator) Ja Ja Ingen Ingen
Beskyttet beskyttet Ja Ja Ja Ingen
Offentlig offentlig Ja Ja Ja Ja

Der er en måde, hvorpå du nemt kan huske denne tabel. Forestil dig, at du skriver et testamente. Du deler alle dine ting op i fire kategorier. Hvem skal bruge dine ting?

Hvem har adgang Modifikator Eksempel
Bare  mig privat Personlig dagbog
Familie (ingen modifikator) Familiebilleder
Familie og arvinger beskyttet Familiegods
Alle offentlig Erindringer

"Det er meget som at forestille sig, at klasser i samme pakke er en del af en familie."

"Jeg vil også fortælle dig nogle interessante nuancer om overordnede metoder."

1) Implicit implementering af en abstrakt metode.

Lad os sige, at du har følgende kode:

Kode
class Cat
{
 public String getName()
 {
  return "Oscar";
 }
}

Og du besluttede at oprette en Tiger-klasse, der arver denne klasse, og tilføje en grænseflade til den nye klasse

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

}

Hvis du bare implementerer alle de manglende metoder, som IntelliJ IDEA fortæller dig at implementere, kan du senere ende med at bruge lang tid på at søge efter en fejl.

Det viser sig, at Tiger-klassen har en getName-metode, der er arvet fra Cat, som vil blive taget som implementering af getName-metoden til HasName-grænsefladen.

"Jeg kan ikke se noget forfærdeligt ved det."

"Det er ikke så slemt, det er et sandsynligt sted for fejl at snige sig ind."

Men det kan være endnu værre:

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

Det viser sig, at du ikke altid kan arve fra flere grænseflader. Mere præcist kan du arve dem, men du kan ikke implementere dem korrekt. Se på eksemplet. Begge grænseflader kræver, at du implementerer getValue()-metoden, men det er ikke klart, hvad det skal returnere: vægten eller størrelsen? Dette er ret ubehageligt at have med at gøre.

"Jeg er enig. Du vil implementere en metode, men du kan ikke. Du har allerede arvet en metode med samme navn fra basisklassen. Den er ødelagt."

"Men der er gode nyheder."

2) Udvidelse af synlighed. Når du arver en type, kan du udvide synligheden af ​​en metode. Sådan ser det ud:

Java kode Beskrivelse
class Cat
{
 protected String getName()
 {
  return "Oscar";
 }
}
class Tiger extends Cat
{
 public String getName()
 {
  return "Oscar Tiggerman";
 }
}
Vi har udvidet metodens synlighed fra protectedtil public.
Kode Hvorfor dette er "lovligt"
public static void main(String[] args)
{
 Cat cat = new Cat();
 cat.getName();
}
Alt er fantastisk. Her ved vi ikke engang, at sigtbarheden er blevet udvidet i en efterkommerklasse.
public static void main(String[] args)
{
 Tiger tiger = new Tiger();
 tiger.getName();
}
Her kalder vi metoden, hvis synlighed er blevet udvidet.

Hvis dette ikke var muligt, kunne vi altid erklære en metode i Tiger:
public String getPublicName()
{
super.getName(); //kald den beskyttede metode
}

Med andre ord, vi taler ikke om nogen sikkerhedsbrud.

public static void main(String[] args)
{
 Cat catTiger = new Tiger();
 catTiger.getName();
}
Hvis alle de nødvendige betingelser for at kalde en metode i en basisklasse ( Cat ) er opfyldt , så er de helt sikkert opfyldt for at kalde metoden på den descendente type ( Tiger ) . Fordi begrænsningerne på metodekaldet var svage, ikke stærke.

"Jeg er ikke sikker på, at jeg helt har forstået det, men jeg vil huske, at det er muligt."

3) Indsnævring af returtypen.

I en tilsidesat metode kan vi ændre returtypen til en indsnævret referencetype.

Java kode Beskrivelse
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;
 }
}
Vi tilsidesatte metoden getMyParent, og nu returnerer den et Tigerobjekt.
Kode Hvorfor dette er "lovligt"
public static void main(String[] args)
{
 Cat parent = new Cat();

 Cat me = new Cat();
 me.setMyParent(parent);
 Cat myParent = me.getMyParent();
}
Alt er fantastisk. Her ved vi ikke engang, at getMyParent-metodens returtype er blevet udvidet i descendant-klassen.

Hvordan den «gamle kode» fungerede og virker.

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

 Tiger me = new Tiger();
 me.setMyParent(parent);
 Tiger myParent = me.getMyParent();
}
Her kalder vi metoden, hvis returtype er blevet indsnævret.

Hvis dette ikke var muligt, kunne vi altid erklære en metode i Tiger:
public Tiger getMyTigerParent()
{
return (Tiger) this.parent;
}

Der er med andre ord ingen sikkerhedsbrud og/eller type casting brud.

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

 Cat me = new Tiger();
 me.setMyParent(parent);
 Cat myParent = me.getMyParent();
}
Og alt fungerer fint her, selvom vi udvidede variablenes type til basisklassen (Cat).

På grund af tilsidesættelse kaldes den korrekte setMyParent-metode.

Og der er intet at bekymre sig om, når man kalder getMyParent-metoden , fordi returværdien, selvom Tiger-klassen er, stadig kan tildeles myParent-variablen i basisklassen (Cat) uden problemer.

Tiger-objekter kan sikkert opbevares både i Tiger-variabler og Cat-variabler.

"Jep. Okay. Når du tilsidesætter metoder, skal du være opmærksom på, hvordan alt dette fungerer, hvis vi sender vores objekter til kode, der kun kan håndtere basisklassen og ikke ved noget om vores klasse. "

"Nøjagtigt! Så er det store spørgsmål, hvorfor kan vi ikke indsnævre returværdiens type, når vi tilsidesætter en metode?"

"Det er indlysende, at i dette tilfælde ville koden i basisklassen holde op med at virke:"

Java kode Forklaring af problemet
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";
 }
}
Vi overbelastede getMyParent-metoden og indsnævrede typen af ​​dens returværdi.

Alt er fint her.

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

 Cat me = new Tiger();
 Cat myParent = me.getMyParent();
}
Så holder denne kode op med at virke.

GetMyParent-metoden kan returnere enhver forekomst af et objekt, fordi den faktisk kaldes på et Tiger-objekt.

Og vi har ikke et tjek før opgaven. Det er således fuldt ud muligt, at Cat-type myParent-variablen gemmer en String-reference.

"Fantastisk eksempel, Amigo!"

I Java, før en metode kaldes, er der ingen kontrol af, om objektet har en sådan metode. Alle kontroller finder sted under kørsel. Og et [hypotetisk] kald til en manglende metode vil højst sandsynligt få programmet til at forsøge at udføre ikke-eksisterende bytekode. Dette ville i sidste ende føre til en fatal fejl, og operativsystemet ville tvangslukke programmet.

"Hvaa. Nu ved jeg det."