1. Variabelen initialiseren

Zoals je al weet, kun je verschillende variabelen in je klasse declareren, en ze niet alleen declareren, maar ze ook meteen initialiseren met hun beginwaarden.

En dezelfde variabelen kunnen ook in een constructor worden geïnitialiseerd. Dit betekent dat deze variabelen in theorie twee keer een waarde kunnen krijgen. Voorbeeld

Code Opmerking
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";
   }
}



De agevariabele krijgt een beginwaarde toegewezen.




De beginwaarde wordt overschreven.


De leeftijdsvariabele slaat zijn beginwaarde op.
 Cat cat = new Cat("Whiskers", 2);
Dit is toegestaan: de eerste constructor wordt aangeroepen
 Cat cat = new Cat();
Dit is toegestaan: de tweede constructor wordt aangeroepen

Dit is wat er gebeurt wanneer Cat cat = new Cat("Whiskers", 2);wordt uitgevoerd:

  • Er wordt een Catvoorwerp gemaakt
  • Alle instantievariabelen worden geïnitialiseerd met hun beginwaarden
  • De constructor wordt aangeroepen en de code wordt uitgevoerd.

Met andere woorden, de variabelen krijgen eerst hun beginwaarden en pas daarna wordt de code van de constructor uitgevoerd.


2. Volgorde van initialisatie van variabelen in een klasse

Variabelen worden niet alleen geïnitialiseerd voordat de constructor wordt uitgevoerd - ze worden geïnitialiseerd in een goed gedefinieerde volgorde: de volgorde waarin ze in de klasse worden gedeclareerd.

Laten we eens kijken naar wat interessante code:

Code Opmerking
public class Solution
{
   public int a = b + c + 1;
   public int b = a + c + 2;
   public int c = a + b + 3;
}

Deze code wordt niet gecompileerd, aangezien er op het moment dat de variabele wordt gemaakt nog geen  en  variabelen azijn . Maar u kunt uw code als volgt schrijven: deze code wordt gecompileerd en werkt prima.bc

Code Opmerking
public class Solution
{
   public int a;
   public int b = a + 2;
   public int c = a + b + 3;
}


0
0+2
0+2+3

Maar vergeet niet dat uw code transparant moet zijn voor andere ontwikkelaars. Het is beter om dergelijke technieken niet te gebruiken, aangezien dit de leesbaarheid van de code schaadt.

Hier moeten we onthouden dat voordat variabelen een waarde krijgen, ze een standaardwaarde hebben . Voor het inttype is dit nul.

Wanneer de JVM de avariabele initialiseert, wijst deze eenvoudigweg de standaardwaarde voor het int-type toe: 0.

Wanneer het bereikt b, is de variabele a al bekend en heeft deze een waarde, dus zal de JVM deze de waarde 2 toewijzen.

En wanneer het de cvariabele bereikt, zijn de variabelen aen bal geïnitialiseerd, dus de JVM berekent gemakkelijk de beginwaarde voor c: 0+2+3.

Als u een variabele binnen een methode maakt, kunt u deze niet gebruiken tenzij u er eerder een waarde aan hebt toegekend. Maar dit geldt niet voor de variabelen van een klasse! Als er geen beginwaarde is toegewezen aan een variabele van een klasse, wordt er een standaardwaarde aan toegewezen.


3. Constanten

Terwijl we analyseren hoe objecten worden gemaakt, is het de moeite waard om in te gaan op de initialisatie van constanten, dwz variabelen met de finalmodifier.

Als een variabele de finalmodifier heeft, moet er een beginwaarde aan worden toegewezen. U weet dit al en er is niets verrassends aan.

Maar wat u niet weet, is dat u de beginwaarde niet meteen hoeft toe te wijzen als u deze toewijst in de constructor. Dit werkt prima voor een laatste variabele. De enige vereiste is dat als u meerdere constructors heeft, een laatste variabele een waarde moet krijgen in elke constructor.

Voorbeeld:

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. Codeer in een constructor

En nog een paar belangrijke opmerkingen over constructeurs. Later, als je doorgaat met het leren van Java, zul je dingen tegenkomen als overerving, serialisatie, uitzonderingen, enz. Ze beïnvloeden allemaal in verschillende mate het werk van constructeurs. Het heeft geen zin om nu diep op deze onderwerpen in te gaan, maar we zijn verplicht ze in ieder geval aan te snijden.

Hier is bijvoorbeeld een belangrijke opmerking over constructors. In theorie kun je code van elke complexiteit in een constructor schrijven. Maar doe dit niet. Voorbeeld:

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);
   }
}






Open een bestand Lees stroom
Lees het bestand in een byte-array
Bewaar de byte-array als een tekenreeks




Toon de inhoud van het bestand op het scherm

In de klasseconstructor FilePrinter hebben we onmiddellijk een bytestroom op een bestand geopend en de inhoud ervan gelezen. Dit is complex gedrag en kan leiden tot fouten.

Wat als er geen dergelijk dossier was? Wat als er problemen waren met het lezen van het bestand? Wat als het te groot was?

Complexe logica houdt een grote kans op fouten in en dat betekent dat de code uitzonderingen correct moet afhandelen.

Voorbeeld 1 — Serialisatie

In een standaard Java-programma zijn er tal van situaties waarin u niet degene bent die objecten van uw klasse maakt. Stel dat u besluit een object via het netwerk te verzenden: in dit geval zal de Java-machine zelf uw object omzetten in een set bytes, het verzenden en het object opnieuw maken uit de set bytes.

Maar stel dat uw bestand niet op de andere computer staat. Er zal een fout in de constructor zijn en niemand zal het afhandelen. En dat is heel goed in staat om het programma te beëindigen.

Voorbeeld 2 — Velden van een klasse initialiseren

Als uw klasseconstructor aangevinkte uitzonderingen kan genereren, dwz is gemarkeerd met het sleutelwoord throws, dan moet u de aangegeven uitzonderingen opvangen in de methode die uw object maakt.

Maar wat als er geen dergelijke methode is? Voorbeeld:

Code  Opmerking
class Solution
{
   public FilePrinter reader = new FilePrinter("c:\\readme.txt");
}
Deze code wordt niet gecompileerd.

De FilePrinterklassenconstructor kan een gecontroleerde uitzondering genereren , wat betekent dat u geen FilePrinterobject kunt maken zonder het in een try-catch-blok te wikkelen. En een try-catch-blok kan alleen in een methode worden geschreven



5. Constructor voor basisklassen

In vorige lessen hebben we het een beetje gehad over overerving. Helaas is onze volledige bespreking van overerving en OOP gereserveerd voor het niveau dat aan OOP is gewijd, en overerving van constructors is al relevant voor ons.

Als uw klasse een andere klasse erft, wordt een object van de bovenliggende klasse ingebed in een object van uw klasse. Bovendien heeft de ouderklasse zijn eigen variabelen en zijn eigen constructors.

Dat betekent dat het erg belangrijk voor u is om te weten en te begrijpen hoe variabelen worden geïnitialiseerd en constructors worden aangeroepen wanneer uw klasse een bovenliggende klasse heeft en u de variabelen en methoden erft.

Klassen

Hoe weten we de volgorde waarin variabelen worden geïnitialiseerd en constructors worden aangeroepen? Laten we beginnen met het schrijven van de code voor twee klassen. De een erft de ander:

Code Opmerking
class ParentClass
{
   public String a;
   public String b;

   public ParentClass()
   {
   }
}

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

   public ChildClass()
   {
   }
}










De ChildClass klasse erft de ParentClassklasse.

We moeten de volgorde bepalen waarin variabelen worden geïnitialiseerd en constructors worden aangeroepen. Logging helpt ons daarbij.

Loggen

Loggen is het proces van het vastleggen van acties die worden uitgevoerd door een programma terwijl het wordt uitgevoerd, door ze naar de console of een bestand te schrijven.

Het is vrij eenvoudig om vast te stellen dat de constructor is aangeroepen: schrijf in de body van de constructor een bericht naar de console. Maar hoe weet je of een variabele is geïnitialiseerd?

Eigenlijk is dit ook niet zo moeilijk: schrijf een speciale methode die de waarde retourneert die is gebruikt om de variabele te initialiseren, en log de initialisatie. Dit is hoe de code eruit zou kunnen zien:

Definitieve code

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");
   }
}




Maak een ChildClassobject


Deze methode schrijft de doorgegeven tekst naar de console en retourneert deze ook.





Declareer de ParentClassklasse

Displaytekst en initialiseer ook de variabelen ermee.




Schrijf een bericht dat de constructor is aangeroepen. Negeer de retourwaarde.


Declareer de ChildClassklasse

Displaytekst en initialiseer ook de variabelen ermee.




Schrijf een bericht dat de constructor is aangeroepen. Negeer de retourwaarde.

Als u deze code uitvoert, wordt de tekst als volgt op het scherm weergegeven:

Console-uitvoer van de methodeMain.print()
ParentClass.a
ParentClass.b
ParentClass.constructor
ChildClass.c
ChildClass.d
ChildClass.constructor

U kunt er dus altijd persoonlijk voor zorgen dat de variabelen van een klasse worden geïnitialiseerd voordat de constructor wordt aangeroepen. Een basisklasse wordt volledig geïnitialiseerd vóór de initialisatie van de overgeërfde klasse.