1. Initialisering av variabler

Som du allerede vet, kan du deklarere flere variabler i klassen din, og ikke bare deklarere dem, men også umiddelbart initialisere dem med startverdiene.

Og de samme variablene kan også initialiseres i en konstruktør. Dette betyr at disse variablene i teorien kan tildeles verdier to ganger. Eksempel

Kode Merk
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";
   }
}



Variabelen agetildeles en startverdi




Startverdien overskrives


Aldersvariabelen lagrer startverdien.
 Cat cat = new Cat("Whiskers", 2);
Dette er tillatt: den første konstruktøren vil bli kalt
 Cat cat = new Cat();
Dette er tillatt: den andre konstruktøren vil bli kalt

Dette er hva som skjer når Cat cat = new Cat("Whiskers", 2);den utføres:

  • Et Catobjekt er opprettet
  • Alle forekomstvariabler initialiseres med deres startverdier
  • Konstruktøren kalles og dens kode kjøres.

Med andre ord, variablene får først sine startverdier, og først deretter blir koden til konstruktøren utført.


2. Rekkefølge for initialisering av variabler i en klasse

Variabler initialiseres ikke bare før konstruktøren kjører - de initialiseres i en veldefinert rekkefølge: rekkefølgen de er deklarert i klassen.

La oss se på en interessant kode:

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

Denne koden vil ikke kompilere, siden på tidspunktet variabelen aopprettes, er det ingen b og c variabler ennå. Men du kan skrive koden din som følger - denne koden vil kompilere og vil fungere helt fint.

Kode Merk
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 koden din må være gjennomsiktig for andre utviklere. Det er bedre å ikke bruke teknikker som dette, siden det svekker lesbarheten til koden.

Her må vi huske at før variabler tildeles en verdi, har de en standardverdi . For inttypen er dette null.

Når JVM initialiserer avariabelen, vil den ganske enkelt tildele standardverdien for int-typen: 0.

Når den når b, vil a-variabelen allerede være kjent og ha en verdi, så JVM vil tildele den verdien 2.

Og når den når cvariabelen, vil variablene aog ballerede være initialisert, så JVM vil enkelt beregne startverdien for c: 0+2+3.

Hvis du oppretter en variabel inne i en metode, kan du ikke bruke den med mindre du tidligere har tildelt en verdi til den. Men dette er ikke sant for variablene i en klasse! Hvis en startverdi ikke er tilordnet en variabel i en klasse, blir den tildelt en standardverdi.


3. Konstanter

Mens vi analyserer hvordan objekter skapes, er det verdt å berøre initialiseringen av konstanter, dvs. variabler med modifikatoren final.

Hvis en variabel har finalmodifikatoren, må den tildeles en startverdi. Du vet dette allerede, og det er ikke noe overraskende med det.

Men det du ikke vet er at du ikke trenger å tildele startverdien med en gang hvis du tilordner den i konstruktøren. Dette vil fungere helt fint for en endelig variabel. Det eneste kravet er at hvis du har flere konstruktører, må en endelig variabel tildeles en verdi 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 noen flere viktige merknader om konstruktører. Senere, mens du fortsetter å lære Java, vil du komme over ting som arv, serialisering, unntak osv. De påvirker alle konstruktørenes arbeid i ulik grad. Det gir ingen mening å dykke dypt inn i disse temaene nå, men vi er forpliktet til i det minste å berøre dem.

For eksempel, her er en viktig bemerkning om konstruktører. I teorien kan du skrive kode av hvilken som helst kompleksitet i en konstruktør. Men ikke gjør 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);
   }
}






Åpne en fil lesestrøm
Les filen inn i en byte array
Lagre byte array som en streng




Vis filens innhold på skjermen

I FilePrinter-klassekonstruktøren åpnet vi umiddelbart en bytestrøm på en fil og leste innholdet. Dette er kompleks oppførsel og kan føre til feil.

Hva om det ikke fantes en slik fil? Hva om det var problemer med å lese filen? Hva om den var for stor?

Kompleks logikk innebærer høy sannsynlighet for feil og det betyr at koden må håndtere unntak riktig.

Eksempel 1 — Serialisering

I et standard Java-program er det mange situasjoner der du ikke er den som lager objekter i klassen din. Anta for eksempel at du bestemmer deg for å sende et objekt over nettverket: i dette tilfellet vil Java-maskinen selv konvertere objektet ditt til et sett med byte, sende det og gjenskape objektet fra settet med byte.

Men anta at filen din ikke eksisterer på den andre datamaskinen. Det vil være en feil i konstruktøren, og ingen vil håndtere den. Og det er ganske i stand til å få programmet til å avslutte.

Eksempel 2 — Initialisering av felt i en klasse

Hvis klassekonstruktøren din kan kaste sjekkede unntak, dvs. er merket med nøkkelordet throws, må du fange opp de angitte unntakene i metoden som lager objektet ditt.

Men hva om det ikke finnes en slik metode? Eksempel:

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

Klassekonstruktøren FilePrinterkan kaste et sjekket unntak , som betyr at du ikke kan lage et FilePrinterobjekt uten å pakke det inn i en try-catch-blokk. Og en try-catch-blokk kan bare skrives i en metode



5. Basisklassekonstruktør

I tidligere leksjoner diskuterte vi arv litt. Dessverre er vår fulle diskusjon om arv og OOP reservert for nivået dedikert til OOP, og arv av konstruktører er allerede relevant for oss.

Hvis klassen din arver en annen klasse, vil et objekt fra den overordnede klassen bli innebygd i et objekt i klassen din. Dessuten har den overordnede klassen sine egne variabler og sine egne konstruktører.

Det betyr at det er veldig viktig for deg å vite og forstå hvordan variabler initialiseres og konstruktører kalles når klassen din har en overordnet klasse og du arver dens variabler og metoder.

Klasser

Hvordan vet vi i hvilken rekkefølge variabler initialiseres og konstruktører kalles? La oss starte med å skrive koden for to klasser. Den ene vil arve den andre:

Kode Merk
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 må bestemme rekkefølgen som variabler initialiseres og konstruktører kalles i. Logging vil hjelpe oss med dette.

Hogst

Logging er prosessen med å registrere handlinger utført av et program mens det kjører, ved å skrive dem til konsollen eller en fil.

Det er ganske enkelt å finne ut at konstruktøren har blitt kalt: i brødteksten til konstruktøren, skriv en melding til konsollen. Men hvordan kan du se om en variabel har blitt initialisert?

Faktisk er dette heller ikke veldig vanskelig: skriv en spesiell metode som vil returnere verdien som ble brukt til å initialisere variabelen, og logg initialiseringen. Slik kan koden se ut:

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




Opprett et ChildClassobjekt


Denne metoden skriver den beståtte teksten til konsollen og returnerer den også.





Erklær ParentClassklassen

Vis tekst og initialiser også variablene med den.




Skriv en melding om at konstruktøren har blitt kalt. Ignorer returverdien.


Erklær ChildClassklassen

Vis tekst og initialiser også variablene med den.




Skriv en melding om at konstruktøren har blitt kalt. Ignorer returverdien.

Hvis du kjører denne koden, vil teksten vises på skjermen som følger:

Konsollutgang av metodenMain.print()
ParentClass.a
ParentClass.b
ParentClass.constructor
ChildClass.c
ChildClass.d
ChildClass.constructor

Så du kan alltid personlig sørge for at variablene til en klasse initialiseres før konstruktøren kalles. En basisklasse initialiseres fullstendig før initialiseringen av den arvede klassen.