1. Initialisering af variabler

Som du allerede ved, kan du erklære flere variabler i din klasse, og ikke bare erklære dem, men også straks initialisere dem med deres begyndelsesværdier.

Og de samme variabler kan også initialiseres i en konstruktør. Det betyder, at disse variable i teorien kan tildeles værdier to gange. Eksempel

Kode Bemærk
class Cat
{
   public String name;
   public int age = -1;

   public Cat(String name, int age)
   {
     this.name = name;
     this.age = age;
   }

   public Cat()
   {
     this.name = "Nameless";
   }
}



Variablen agetildeles en startværdi




Startværdien overskrives


Aldersvariablen gemmer dens startværdi.
 Cat cat = new Cat("Whiskers", 2);
Dette er tilladt: den første konstruktør vil blive kaldt
 Cat cat = new Cat();
Dette er tilladt: den anden konstruktør vil blive kaldt

Dette er, hvad der sker, når Cat cat = new Cat("Whiskers", 2);det udføres:

  • Et Catobjekt oprettes
  • Alle instansvariabler initialiseres med deres begyndelsesværdier
  • Konstruktøren kaldes, og dens kode udføres.

Med andre ord får variablerne først deres startværdier, og først derefter udføres konstruktørens kode.


2. Rækkefølge for initialisering af variabler i en klasse

Variabler initialiseres ikke blot, før konstruktøren kører - de initialiseres i en veldefineret rækkefølge: den rækkefølge, de erklæres i i klassen.

Lad os se på noget interessant kode:

Kode Bemærk
public class Solution
{
   public int a = b + c + 1;
   public int b = a + c + 2;
   public int c = a + b + 3;
}

Denne kode vil ikke kompilere, da på det tidspunkt, hvor variablen aoprettes, er der ingen b og c variabler endnu. Men du kan skrive din kode som følger - denne kode vil kompilere og vil køre fint.

Kode Bemærk
public class Solution
{
   public int a;
   public int b = a + 2;
   public int c = a + b + 3;
}


0
0+2
0+2+3

Men husk, at din kode skal være gennemsigtig for andre udviklere. Det er bedre ikke at bruge teknikker som denne, da det forringer kodens læsbarhed.

Her skal vi huske, at før variabler tildeles en værdi, har de en standardværdi . For inttypen er dette nul.

Når JVM initialiserer avariablen, vil den blot tildele standardværdien for int-typen: 0.

Når den når b, vil a-variablen allerede være kendt og have en værdi, så JVM vil tildele den værdien 2.

Og når den når cvariablen, vil variablerne aog ballerede være initialiseret, så JVM vil nemt beregne startværdien for c: 0+2+3.

Hvis du opretter en variabel inde i en metode, kan du ikke bruge den, medmindre du tidligere har tildelt en værdi til den. Men dette er ikke sandt for variablerne i en klasse! Hvis en startværdi ikke er tildelt en variabel i en klasse, tildeles den en standardværdi.


3. Konstanter

Mens vi analyserer, hvordan objekter skabes, er det værd at berøre initialiseringen af ​​konstanter, dvs. variable med modifikatoren final.

Hvis en variabel har finalmodifikatoren, skal den tildeles en startværdi. Du ved det allerede, og der er ikke noget overraskende ved det.

Men hvad du ikke ved er, at du ikke behøver at tildele startværdien med det samme, hvis du tildeler den i konstruktøren. Dette vil fungere fint for en endelig variabel. Det eneste krav er, at hvis du har flere konstruktører, så skal en endelig variabel tildeles en værdi i hver konstruktør.

Eksempel:

public class Cat
{
   public final int maxAge = 25;
   public final int maxWeight;

   public Cat (int weight)
   {
     this.maxWeight = weight; // Assign an initial value to the constant
   }
}


4. Kode i en konstruktør

Og et par vigtige bemærkninger om konstruktører. Senere, mens du fortsætter med at lære Java, vil du støde på ting som arv, serialisering, undtagelser osv. De påvirker alle konstruktørernes arbejde i forskellig grad. Det giver ingen mening at dykke dybt ned i disse emner nu, men vi er forpligtet til i det mindste at berøre dem.

For eksempel, her er en vigtig bemærkning om konstruktører. I teorien kan du skrive kode af enhver kompleksitet i en konstruktør. Men gør ikke dette. Eksempel:

class FilePrinter
{
   public String content;

   public FilePrinter(String filename) throws Exception
   {
      FileInputStream input = new FileInputStream(filename);
      byte[] buffer = input.readAllBytes();
      this.content = new String(buffer);
   }

   public void printFile()
   {
      System.out.println(content);
   }
}






Åbn en fillæsestrøm
Læs filen ind i et byte-array
Gem byte-arrayet som en streng




Vis filens indhold på skærmen

I FilePrinter-klassekonstruktøren åbnede vi straks en byte-stream på en fil og læste dens indhold. Dette er kompleks adfærd og kan resultere i fejl.

Hvad hvis der ikke var en sådan fil? Hvad hvis der var problemer med at læse filen? Hvad hvis den var for stor?

Kompleks logik indebærer en høj sandsynlighed for fejl, og det betyder, at koden skal håndtere undtagelser korrekt.

Eksempel 1 — Serialisering

I et standard Java-program er der masser af situationer, hvor du ikke er den, der opretter objekter i din klasse. Antag for eksempel, at du beslutter dig for at sende et objekt over netværket: i dette tilfælde vil Java-maskinen selv konvertere dit objekt til et sæt bytes, sende det og genskabe objektet fra sættet af bytes.

Men antag så, at din fil ikke findes på den anden computer. Der vil være en fejl i konstruktøren, og ingen vil håndtere den. Og det er ganske i stand til at få programmet til at afslutte.

Eksempel 2 — Initialisering af felter i en klasse

Hvis din klassekonstruktør kan kaste kontrollerede undtagelser, dvs. er markeret med throws nøgleordet, så skal du fange de angivne undtagelser i den metode, der opretter dit objekt.

Men hvad hvis der ikke er en sådan metode? Eksempel:

Kode  Bemærk
class Solution
{
   public FilePrinter reader = new FilePrinter("c:\\readme.txt");
}
Denne kode vil ikke kompilere.

Klassekonstruktøren FilePrinterkan kaste en markeret undtagelse , hvilket betyder, at du ikke kan oprette et FilePrinterobjekt uden at pakke det ind i en try-catch-blok. Og en try-catch blok kan kun skrives i en metode



5. Basisklassekonstruktør

I tidligere lektioner diskuterede vi arv lidt. Desværre er vores fulde diskussion af arv og OOP forbeholdt niveauet dedikeret til OOP, og nedarvning af konstruktører er allerede relevant for os.

Hvis din klasse arver en anden klasse, vil et objekt fra den overordnede klasse blive indlejret i et objekt i din klasse. Hvad mere er, har den overordnede klasse sine egne variabler og sine egne konstruktører.

Det betyder, at det er meget vigtigt for dig at vide og forstå, hvordan variable initialiseres, og konstruktører kaldes, når din klasse har en overordnet klasse, og du arver dens variabler og metoder.

Klasser

Hvordan ved vi i hvilken rækkefølge variable initialiseres og konstruktører kaldes? Lad os starte med at skrive koden til to klasser. Den ene vil arve den anden:

Kode Bemærk
class ParentClass
{
   public String a;
   public String b;

   public ParentClass()
   {
   }
}

class ChildClass extends ParentClass
{
   public String c;
   public String d;

   public ChildClass()
   {
   }
}










Klassen ChildClass arver ParentClassklassen.

Vi skal bestemme i hvilken rækkefølge variable initialiseres og konstruktører kaldes. Logning vil hjælpe os med dette.

Logning

Logning er processen med at registrere handlinger udført af et program, mens det kører, ved at skrive dem til konsollen eller en fil.

Det er ganske enkelt at fastslå, at konstruktøren er blevet kaldt: Skriv en besked til konsollen i konstruktørens krop. Men hvordan kan du se, om en variabel er blevet initialiseret?

Faktisk er dette heller ikke særlig svært: skriv en speciel metode, der returnerer den værdi, der blev brugt til at initialisere variablen, og log initialiseringen. Sådan kan koden se ud:

Endelig kode

public class Main
{
   public static void main(String[] args)
   {
      ChildClass obj = new ChildClass();
   }

   public static String print(String text)
   {
      System.out.println(text);
      return text;
   }
}

class ParentClass
{
   public String a = Main.print("ParentClass.a");
   public String b = Main.print("ParentClass.b");

   public ParentClass()
   {
      Main.print("ParentClass.constructor");
   }
}

class ChildClass extends ParentClass
{
   public String c = Main.print("ChildClass.c");
   public String d = Main.print("ChildClass.d");

   public ChildClass()
   {
      Main.print("ChildClass.constructor");
   }
}




Opret et ChildClassobjekt


Denne metode skriver den overførte tekst til konsollen og returnerer den også.





Erklær ParentClassklassen

Vis tekst og initialiser også variablerne med den.




Skriv en besked om, at konstruktøren er blevet kaldt. Ignorer returværdien.


Erklær ChildClassklassen

Vis tekst og initialiser også variablerne med den.




Skriv en besked om, at konstruktøren er blevet kaldt. Ignorer returværdien.

Hvis du udfører denne kode, vil tekst blive vist på skærmen som følger:

Konsol output af metodenMain.print()
ParentClass.a
ParentClass.b
ParentClass.constructor
ChildClass.c
ChildClass.d
ChildClass.constructor

Så du kan altid personligt sikre dig, at variablerne i en klasse initialiseres, før konstruktøren kaldes. En basisklasse initialiseres fuldt ud før initialiseringen af ​​den nedarvede klasse.