CodeGym /Java blogg /Slumpmässig /Java-konstruktörer
John Squirrels
Nivå
San Francisco

Java-konstruktörer

Publicerad i gruppen
Hej! Idag ska vi överväga ett mycket viktigt ämne som berör våra föremål. Utan att överdriva kan vi säga att du kommer att använda det här ämnet i verkligheten varje dag! Vi pratar om Java Constructors. Det kan vara första gången du hör den här termen, men du har faktiskt redan använt konstruktörer. Du insåg det bara inte :) Vi övertygar oss själva om detta senare.

Vad i hela friden är konstruktörer och varför behövs de?

Låt oss överväga två exempel.

public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {

       Car bugatti = new Car();
       bugatti.model = "Bugatti Veyron";
       bugatti.maxSpeed = 378;

   }
}
Vi skapade vår bil och ställde in dess modell och högsta hastighet. Men bilobjektet skulle uppenbarligen inte ha 2 fält i ett riktigt projekt. Till exempel kan den ha 16 fält!

public class Car {

   String model;// model
   int maxSpeed;// maximum speed
   int wheels;// wheel width
   double engineVolume;// engine volume
   String color;// color
   int productionYear;// production year
   String ownerFirstName;// first name of owner
   String ownerLastName;// last name of owner
   long price;// price
   boolean isNew;// flag indicating whether car is new
   int seatsInTheCar;// number of seats in the car
   String cabinMaterial;// interior material
   boolean insurance;// flag indicating whether car is insured
   String manufacturerCountry;// manufacturer country
   int trunkVolume;// size of the trunk
   int accelerationTo100km;// how long it takes to accelerate to 100 km/h (in seconds)


   public static void main(String[] args) {
       Car bugatti = new Car();

       bugatti.color = "blue";
       bugatti.accelerationTo100km = 3;
       bugatti.engineVolume = 6.3;
       bugatti.manufacturerCountry = "Italy";
       bugatti.ownerFirstName = "Amigo";
       bugatti.productionYear = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.seatsInTheCar = 2;
       bugatti.maxSpeed = 378;
       bugatti.model = "Bugatti Veyron";

   }

}
Vi har skapat ett nytt bilobjekt . Det finns ett problem: vi har 16 fält, men vi initierade bara 12 ! Titta på koden nu och försök hitta fälten vi glömde! Inte så lätt va? I den här situationen kan en programmerare lätt göra ett misstag och misslyckas med att initiera något fält. Som ett resultat kommer programmet att bete sig felaktigt:

public class Car {

   String model;// model
   int maxSpeed;// maximum speed
   int wheels;// wheel width
   double engineVolume;// engine volume
   String color;// color
   int productionYear;// production year
   String ownerFirstName;// first name of owner
   String ownerLastName;// last name of owner
   long price;// price
   boolean isNew;// flag indicating whether car is new
   int seatsInTheCar;// number of seats in the car
   String cabinMaterial;// interior material
   boolean insurance;// flag indicating whether car is insured
   String manufacturerCountry;// manufacturer country
   int trunkVolume;// size of the trunk
   int accelerationTo100km;// how long it takes to accelerate to 100 km/h (in seconds)


   public static void main(String[] args) {
       Car bugatti = new Car();

       bugatti.color = "blue";
       bugatti.accelerationTo100km = 3;
       bugatti.engineVolume = 6.3;
       bugatti.manufacturerCountry = "Italy";
       bugatti.ownerFirstName = "Amigo";
       bugatti.productionYear = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.seatsInTheCar = 2;
       bugatti.maxSpeed = 378;
       bugatti.model = "Bugatti Veyron";

       System.out.println("Model: Bugatti Veyron. Engine volume: " + bugatti.engineVolume + ". Trunk volume: " + bugatti.trunkVolume + ". Cabin material: " + bugatti.cabinMaterial +
       ". Wheel width: " + bugatti.wheels + ". Purchased in 2018 by Mr. " + bugatti.ownerLastName);

   }

}
Konsolutgång: Modell: Bugatti Veyron. Motorvolym: 6,3. Bagagevolym: 0. Hyttmaterial: null. Hjulbredd: 0. Köpt 2018 av Mr null Din köpare, som gav upp 2 miljoner dollar för bilen, kommer uppenbarligen inte att gilla att bli kallad " Mr. null "! Men allvarligt talat, slutsatsen är att vårt program skapade ett objekt felaktigt: en bil med en hjulbredd på 0 (dvs inga hjul alls), en saknad bagageutrymme, en hytt gjord av ett okänt material och framför allt en odefinierad ägare . Du kan bara föreställa dig hur ett sådant misstag kan "gå av" när programmet körs! Vi måste undvika sådana situationer på något sätt. Vi måste begränsa vårt program: när vi skapar en ny bilobjekt vill vi att fälten, som modell och maxhastighet, alltid ska anges. Annars vill vi förhindra att objektet skapas. Konstruktörer hanterar denna uppgift med lätthet. De fick sitt namn av en anledning. Konstruktören skapar ett slags klass "skelett" som varje nytt objekt måste matcha. För enkelhetens skull, låt oss gå tillbaka till den enklare versionen av bilklassen med två fält. Med tanke på våra krav kommer bilklassens konstruktör att se ut så här:

public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}

// And creating an object now looks like this:

public static void main(String[] args) {
   Car bugatti = new Car("Bugatti Veyron", 378);
}
Notera hur en konstruktor deklareras. Det liknar en vanlig metod, men den har ingen returtyp. Dessutom specificerar konstruktorn klassnamnet ( Car ) som börjar med en stor bokstav. Dessutom används konstruktorn med ett nyckelord som är nytt för dig: detta . Nyckelordet detta är för att indikera ett visst objekt. Koden i konstruktorn

public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
kan tolkas nästan ordagrant: " Modellen för den här bilen (den vi skapar nu) är modellargumentet som skickas till konstruktören. MaxSpeed ​​för den här bilen (den vi skapar) är maxSpeed -argumentet som skickas till konstruktör." Och det är bara vad som händer:

public class Car {

   String model;
   int maxSpeed;

   public Car(String model, int maxSpeed) {
       this.model = model;
       this.maxSpeed = maxSpeed;
   }

   public static void main(String[] args) {
       Car bugatti = new Car("Bugatti Veyron", 378);
       System.out.println(bugatti.model);
       System.out.println(bugatti.maxSpeed);
   }

}
Konsolutgång: Bugatti Veyron 378 Konstruktören tilldelade de erforderliga värdena korrekt. Du kanske har märkt att en konstruktör är väldigt lik en vanlig metod! Så är det. En konstruktör är egentligen en metod, men med specifika egenskaper :) Precis som med metoder skickade vi argument till vår konstruktör. Och precis som att anropa en metod fungerar det inte att anropa en konstruktor om du inte specificerar dem:

public class Car {

   String model;
   int maxSpeed;

   public Car(String model, int maxSpeed) {
       this.model = model;
       this.maxSpeed = maxSpeed;
   }

   public static void main(String[] args) {
       Car bugatti = new Car(); // Error!
   }
  
}
Du kan se att konstruktören åstadkommer det vi försökte uppnå. Nu kan du inte skapa en bil utan hastighet eller modell! Likheten mellan konstruktörer och metoder slutar inte här. Precis som metoder kan konstruktörer överbelastas. Föreställ dig att du har 2 husdjurskatter hemma. Du fick en av dem som kattunge. Men den andra tog du in från gatan när den redan var växt, och du vet inte exakt hur gammal den är. I det här fallet vill vi att vårt program ska kunna skapa två sorters katter: de med namn och ålder (för den första katten), och de med bara ett namn (för den andra katten). För detta kommer vi att överbelasta konstruktören:

public class Cat {

   String name;
   int age;

   // For the first cat
   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   // For the second cat
   public Cat(String name) {
       this.name = name;
   }

   public static void main(String[] args) {

       Cat smudge = new Cat("Smudge", 5);
       Cat streetCatNamedBob = new Cat("Bob");
   }

}
Förutom den ursprungliga konstruktorn med parametrarna "namn" och "ålder", lade vi till en till med bara en namnparameter. På exakt samma sätt som vi överbelastade metoder i tidigare lektioner. Nu kan vi skapa båda sorters katter :)
Varför behöver vi konstruktörer?  - 2
Kom ihåg att vi i början av lektionen sa att du redan har använt konstruktörer utan att inse det? Vi menade det vi sa. Faktum är att varje klass i Java har vad som kallas en standardkonstruktor. Det kräver inga argument, men det anropas varje gång du skapar något objekt av någon klass.

public class Cat {

   public static void main(String[] args) {

       Cat smudge = new Cat(); // The default constructor is invoked here
   }
}
Vid första anblicken är det osynligt. Vi skapade ett objekt, så vad? Var gör konstruktören något här? För att se det, låt oss uttryckligen skriva en tom konstruktor för Cat -klassen. Vi kommer att visa några fraser i den. Om frasen visas anropades konstruktorn.

public class Cat {

   public Cat() {
       System.out.println("A cat has been created!");
   }

   public static void main(String[] args) {

       Cat smudge = new Cat(); // The default constructor is invoked here
   }
}
Konsolutgång: En katt har skapats! Där är bekräftelsen! Standardkonstruktorn är alltid osynligt närvarande i dina klasser. Men du måste veta en sak till om det. Standardkonstruktorn tas bort från en klass när du skapar en konstruktor med argument. Faktum är att vi redan har sett bevis på detta ovan. Det stod i denna kod:

public class Cat {

   String name;
   int age;

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

   public static void main(String[] args) {

       Cat smudge = new Cat(); //Error!
   }
}
Vi kunde inte skapa en Cat utan namn och ålder, eftersom vi deklarerade en Cat- konstruktor med sträng- och int -parametrar. Detta gjorde att standardkonstruktorn omedelbart försvann från klassen. Så kom ihåg att om du behöver flera konstruktörer i din klass, inklusive en konstruktör utan argument, måste du deklarera det separat. Anta till exempel att vi skapar ett program för en veterinärklinik. Vår klinik vill göra goda gärningar och hjälpa hemlösa kattungar vars namn och ålder är okända. Då bör vår kod se ut så här:

public class Cat {

   String name;
   int age;

   // For cats with owners
   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   // For street cats
   public Cat() {
   }

   public static void main(String[] args) {

       Cat smudge = new Cat("Smudge", 5);
       Cat streetCat = new Cat();
   }
}
Nu när vi har skrivit en explicit standardkonstruktor kan vi skapa båda typerna av katter :) Som med alla metoder är ordningen på argument som skickas till en konstruktor väldigt viktig. Låt oss byta namn- och åldersargument i vår konstruktor.

public class Cat {

   String name;
   int age;

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

   public static void main(String[] args) {

       Cat smudge = new Cat("Smudge", 10); // Error!
   }
}
Ett fel! Konstruktören anger tydligt att när ett Cat- objekt skapas måste det skickas ett nummer och en sträng, i denna ordning. Så vår kod fungerar inte. Se till att komma ihåg detta och ha det i åtanke när du deklarerar dina egna klasser:

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

public Cat(int age, String name) {
   this.age = age;
   this.name = name;
}
Det här är två helt olika konstruktörer! Om vi ​​i en enda mening skulle uttrycka svaret på frågan "Varför behöver jag en konstruktör?", kan vi säga, "För att säkerställa att objekt alltid har ett giltigt tillstånd". När du använder konstruktorer kommer alla dina variabler att initieras korrekt. Dina program kommer inte att ha några bilar med en hastighet på 0 eller några andra "ogiltiga" objekt. Deras främsta fördel är för programmeraren. Om du initierar fält manuellt (efter att ha skapat ett objekt) är risken stor att du missar något och introducerar en bugg. Men detta kommer inte att hända med en konstruktor: om du misslyckas med att skicka alla nödvändiga argument eller om du skickar fel typer av argument, kommer kompilatorn omedelbart att registrera ett fel. Vi måste också separat säga att du inte ska lägga ditt program" s logik inuti en konstruktör. Det är vad metoderna är till för. Metoder är där du ska definiera all nödvändig funktionalitet. Låt oss se varför det är en dålig idé att lägga till logik i en konstruktör:

public class CarFactory {

   String name;
   int age;
   int carsCount;

   public CarFactory(String name, int age, int carsCount) {
   this.name = name;
   this.age = age;
   this.carsCount = carsCount;

   System.out.println("Our car factory is called " + this.name);
   System.out.println("It was founded " + this.age + " years ago" );
   System.out.println("Since that time, it has produced " + this.carsCount +  " cars");
   System.out.println("On average, it produces " + (this.carsCount/this.age) + " cars per year");
}

   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Ford", 115 , 50000000);
   }
}
Vi har en CarFactory -klass som beskriver bilfabriken. Inuti konstruktorn initierar vi alla fält och inkluderar lite logik: vi visar lite information om fabriken. Det verkar som att det inte är något dåligt med det här. Programmet fungerar bra. Konsolproduktion: Vår bilfabrik heter Ford. Den grundades för 115 år sedan. Sedan dess har den producerat 50000000 bilar I genomsnitt producerar den 434782 bilar per år Men vi har faktiskt lagt en fördröjd gruva. Och den här typen av kod kan mycket lätt leda till fel. Anta att vi nu inte pratar om Ford, utan om en ny fabrik som heter "Amigo Motors", som har funnits i mindre än ett år och har producerat 1000 bilar:

public class CarFactory {

   String name;
   int age;
   int carsCount;

   public CarFactory(String name, int age, int carsCount) {
   this.name = name;
   this.age = age;
   this.carsCount = carsCount;

   System.out.println("Our car factor is called " + this.name);
   System.out.println("It was founded " + this.age + " years ago" );
   System.out.println("Since that time, it has produced " + this.carsCount +  " cars");
   System.out.println("On average, it produces " + (this.carsCount/this.age) + " cars per year");
}


   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Amigo Motors", 0 , 1000);
   }
}
Konsolutgång: Vår bilfabrik heter Amigo Motors Exception i tråden "huvud" java.lang.ArithmeticException: / noll Den grundades för 0 år sedan Sedan dess har den producerat 1000 bilar på CarFactory. (CarFactory.java:15) på CarFactory.main(CarFactory.java:23) Processen avslutad med utgångskod 1 Bom! Programmet slutar med något slags obegripligt fel. Kan du försöka gissa orsaken? Problemet ligger i logiken vi lägger in i konstruktorn. Mer specifikt denna rad:

System.out.println("On average, it produces " + (this.carsCount/this.age) + " cars per year");
Här gör du en beräkning och dividerar antalet tillverkade bilar med fabrikens ålder. Och eftersom vår fabrik är ny (dvs. den är 0 år gammal) dividerar vi med 0, vilket vi inte kan göra i matematik. Följaktligen avslutas programmet med ett fel.

Vad borde vi ha gjort?

Lägg all logik i en separat metod. Låt oss kalla det printFactoryInfo() . Du kan skicka ett CarFactory -objekt till det som ett argument. Du kan lägga all logik där, och samtidigt hantera potentiella fel (som vårt som involverar noll år). Till var och en sitt. Konstruktörer behövs för att ställa in giltigt objekttillstånd. Vi har metoder för affärslogik. Blanda inte det ena med det andra. För att förstärka det du lärde dig föreslår vi att du tittar på en videolektion från vår Java-kurs
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION