JavaRush /Blog Java /Random-PL /Konstruktory w Javie

Konstruktory w Javie

Opublikowano w grupie Random-PL
Cześć! Dziś przyjrzymy się bardzo ważnemu tematowi, który dotyczy naszych obiektów. Tutaj bez przesady możemy powiedzieć, że tę wiedzę będziesz wykorzystywać na co dzień w prawdziwej pracy! Porozmawiamy o konstruktorach. Być może słyszysz to określenie po raz pierwszy, ale tak naprawdę prawdopodobnie korzystałeś z konstruktorów, ale sam tego nie zauważyłeś :) Zobaczymy to później.

Co to jest konstruktor w Javie i dlaczego jest potrzebny?

Spójrzmy na dwa przykłady.
public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {

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

   }
}
Stworzyliśmy nasz samochód i ustaliliśmy jego model oraz prędkość maksymalną. Jednak w prawdziwym projekcie obiekt Car będzie miał wyraźnie więcej niż 2 pola. I na przykład 16 pól!
public class Car {

   String model;//Model
   int maxSpeed;//maksymalna prędkość
   int wheels;// szerokość dysku
   double engineVolume;//pojemność silnika
   String color;//kolor
   int yearOfIssue;//rok wystawienia
   String ownerFirstName;//Imię właściciela
   String ownerLastName;//nazwisko właściciela
   long price;//cena
   boolean isNew;//nowy czy nie
   int placesInTheSalon;//liczba miejsc w kabinie
   String salonMaterial;// materiał wnętrza
   boolean insurance;//czy jest ubezpieczony
   String manufacturerCountry;//kraj producenta
   int trunkVolume;// objętość bagażnika
   int accelerationTo100km;//przyspieszenie do 100 km/h w sekundach


   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.yearOfIssue = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.placesInTheSalon = 2;
       bugatti.maxSpeed = 407;
       bugatti.model = "Bugatti Veyron";

   }

}
Stworzyliśmy nowy obiekt Car . Jeden problem: mamy 16 pól, ale zainicjowaliśmy tylko 12 ! Spróbuj teraz użyć kodu, aby znaleźć te, o których zapomnieliśmy! Nie tak łatwo, prawda? W takiej sytuacji programista może łatwo popełnić błąd i pominąć inicjalizację jakiegoś pola. W rezultacie zachowanie programu stanie się błędne:
public class Car {

   String model;//Model
   int maxSpeed;//maksymalna prędkość
   int wheels;// szerokość dysku
   double engineVolume;//pojemność silnika
   String color;//kolor
   int yearOfIssue;//rok wystawienia
   String ownerFirstName;//Imię właściciela
   String ownerLastName;//nazwisko właściciela
   long price;//cena
   boolean isNew;//nowy czy nie
   int placesInTheSalon;//liczba miejsc w kabinie
   String salonMaterial;// materiał wnętrza
   boolean insurance;//czy jest ubezpieczony
   String manufacturerCountry;//kraj producenta
   int trunkVolume;// objętość bagażnika
   int accelerationTo100km;//przyspieszenie do 100 km/h w sekundach


   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.yearOfIssue = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.placesInTheSalon = 2;
       bugatti.maxSpeed = 407;
       bugatti.model = "Bugatti Veyron";

       System.out.println(„Model Bugatti Veyron. Rozmiar silnika -” + bugatti.engineVolume + ", pień - " + bugatti.trunkVolume + "salon składa się z" + bugatti.salonMaterial +
       ", szerokość dysku - " + bugatti.wheels + ". Został przejęty w 2018 roku przez pana " + bugatti.ownerLastName);

   }

}
Wyjście konsoli:
Model Bugatti Veyron. Poj. silnika - 6,3, bagażnik - 0, wnętrze null, szerokość felgi - 0. Został zakupiony w 2018 roku przez pana nulla
Twojemu nabywcy, który zapłacił za samochód 2 miliony dolarów, najwyraźniej nie będzie się podobać, gdy nazywa się go „panem Nullem”! Ale tak na poważnie, w efekcie nasz program skończył z niepoprawnie utworzonym obiektem - samochodem z felgą o szerokości 0 (czyli w ogóle bez felg), brakującym bagażnikiem, wnętrzem wykonanym z nieznanego materiału, a nawet należącym do kogoś nieznanego . Można sobie tylko wyobrazić, jak taki błąd mógł wystąpić podczas działania programu! Musimy jakoś unikać takich sytuacji. Potrzebujemy, aby nasz program miał ograniczenie: np. podczas tworzenia nowego obiektu pojazdu należy zawsze określić dla niego model i maksymalną prędkość. W przeciwnym razie nie zezwalaj na tworzenie obiektów. Funkcje konstruktora z łatwością radzą sobie z tym zadaniem. Nie bez powodu otrzymali swoją nazwę. Konstruktor tworzy swego rodzaju „szkielet” klasy, któremu musi odpowiadać każdy nowy obiekt klasy. Dla wygody wróćmy do prostszej wersji klasy Car z dwoma polami. Biorąc pod uwagę nasze wymagania, konstruktor klasy Car będzie wyglądał następująco:
public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
Tworzenie obiektu wygląda teraz tak:
public static void main(String[] args) {
   Car bugatti = new Car("Bugatti Veyron", 407);
}
Zwróć uwagęjak tworzony jest konstruktor. Jest podobna do zwykłej metody, ale nie ma typu zwracanego. W tym przypadku nazwa klasy jest podana w konstruktorze, również wielką literą. W naszym przypadku - Samochód . Ponadto konstruktor używa słowa kluczowego new-to-you this . „to” w języku angielskim oznacza „to, to”. To słowo odnosi się do konkretnego przedmiotu. Kod w konstruktorze:
public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
można przetłumaczyć niemal dosłownie: „ model dla tej maszyny (którą właśnie tworzymy) = argument modelu , który jest podany w konstruktorze. maxSpeed ​​dla tej maszyny (którą tworzymy) = argument maxSpeed , który jest określony w konstruktorze.” To jest to, co się stało:
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", 407);
       System.out.println(bugatti.model);
       System.out.println(bugatti.maxSpeed);
   }

}
Wyjście konsoli:
Bugatti Veyron 407
Konstruktor pomyślnie przypisał wymagane wartości. Być może zauważyłeś, że konstruktor jest bardzo podobny do zwykłej metody! A tak to jest: konstruktor to metoda, tylko trochę specyficzna :) Podobnie jak w metodzie, przekazaliśmy parametry naszemu konstruktorowi. I podobnie jak wywołanie metody, wywołanie konstruktora nie zadziała, jeśli go nie określisz:
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(); //błąd!
   }

}
Widzisz, projektant zrobił to, co chcieliśmy osiągnąć. Teraz nie możesz stworzyć samochodu bez prędkości i bez modelu! Na tym nie kończą się podobieństwa między konstruktorami i metodami. Podobnie jak metody, konstruktory mogą być przeciążane. Wyobraź sobie, że masz w domu 2 koty. Jednego wziąłeś jako kociaka, drugiego przyniosłeś z ulicy jako dorosły i nie wiesz dokładnie, ile ma lat. Oznacza to, że nasz program powinien móc tworzyć koty dwóch typów - z imieniem i wiekiem dla pierwszego kota i tylko z imieniem dla drugiego kota. W tym celu przeciążamy konstruktor:
public class Cat {

   String name;
   int age;

   //dla pierwszego kota
   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   //dla drugiego kota
   public Cat(String name) {
       this.name = name;
   }

   public static void main(String[] args) {

       Cat barsik = new Cat("Barsik", 5);
       Cat streetCatNamedBob = new Cat("Bob");
   }

}
Do pierwotnego konstruktora z parametrami „nazwa” i „wiek” dodaliśmy kolejny, już tylko z nazwą. W ten sam sposób przeciążaliśmy metody na poprzednich lekcjach. Teraz z powodzeniem możemy stworzyć obie wersje kotów :) Dlaczego konstruktorzy są potrzebni?  - 2Czy pamiętasz, jak na początku wykładu mówiliśmy, że korzystałeś już z konstruktorów, ale po prostu tego nie zauważyłeś? To prawda. Faktem jest, że każda klasa w Javie posiada tzw. konstruktora domyślnego. Nie ma żadnych argumentów, ale uruchamia się za każdym razem, gdy tworzony jest dowolny obiekt dowolnej klasy.
public class Cat {

   public static void main(String[] args) {

       Cat barsik = new Cat(); //tutaj działał domyślny konstruktor
   }
}
Na pierwszy rzut oka nie jest to zauważalne. No cóż, stworzyliśmy obiekt i stworzyliśmy, gdzie jest praca projektanta? Aby to zobaczyć napiszmy własnymi rękami pusty konstruktor dla klasy Cat , a wewnątrz niego wypiszemy na konsolę jakąś frazę. Jeśli jest wyświetlony, oznacza to, że konstruktor zadziałał.
public class Cat {

   public Cat() {
       System.out.println(„Stworzył kota!”);
   }

   public static void main(String[] args) {

       Cat barsik = new Cat(); //tutaj działał domyślny konstruktor
   }
}
Wyjście konsoli:
Stworzyli kota!
Oto potwierdzenie! Domyślny konstruktor jest zawsze niewidoczny w twoich klasach. Ale musisz poznać jeszcze jedną jego cechę. Domyślny konstruktor znika z klasy, gdy utworzysz konstruktor z argumentami. Dowód na to widzieliśmy już powyżej. Tutaj w tym kodzie:
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 barsik = new Cat(); //błąd!
   }
}
Nie mogliśmy stworzyć kota bez imienia i wieku, ponieważ zdefiniowaliśmy konstruktor dla Cat : string + number. Domyślny konstruktor zniknął z klasy natychmiast po tym. Dlatego pamiętaj: jeśli potrzebujesz w swojej klasie kilku konstruktorów, w tym pustego, musisz go utworzyć osobno. Przykładowo tworzymy program dla kliniki weterynaryjnej. Nasza klinika pragnie czynić dobre uczynki i pomagać bezdomnym kotom, o których nie znamy ani imienia, ani wieku. Wtedy nasz kod powinien wyglądać tak:
public class Cat {

   String name;
   int age;

   //dla kotów domowych
   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   //dla kotów ulicznych
   public Cat() {
   }

   public static void main(String[] args) {

       Cat barsik = new Cat("Barsik", 5);
       Cat streetCat = new Cat();
   }
}
Teraz, gdy już wyraźnie napisaliśmy domyślny konstruktor, możemy stworzyć koty obu typów :) Dla konstruktora (jak dla każdej metody) kolejność argumentów jest bardzo ważna. Zamieńmy argumenty name i age w naszym konstruktorze.
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 barsik = new Cat("Barsik", 10); //błąd!
   }
}
Błąd! Konstruktor wyraźnie stwierdza, że ​​podczas tworzenia obiektu Cat należy przekazać mu liczbę i ciąg znaków w tej kolejności. Dlatego nasz kod nie działa. Pamiętaj o tym i miej to na uwadze tworząc własne zajęcia:
public Cat(String name, int age) {
   this.name = name;
   this.age = age;
}

public Cat(int age, String name) {
   this.age = age;
   this.name = name;
}
To dwaj zupełnie różni projektanci! Jeśli wyrazimy odpowiedź na pytanie „Po co nam konstruktor?” jednym zdaniem, możemy powiedzieć: tak, aby obiekty były zawsze w odpowiednim stanie. Kiedy użyjesz konstruktorów, wszystkie zmienne zostaną poprawnie zainicjalizowane, a w programie nie będzie żadnych samochodów o prędkości 0 ani innych „nieprawidłowych” obiektów. Ich zastosowanie jest bardzo korzystne przede wszystkim dla samego programisty. Jeśli samodzielnie inicjujesz pola, istnieje duże ryzyko, że coś przeoczysz i popełnisz błąd. Ale tak się nie stanie w przypadku konstruktora: jeśli nie przekazałeś do niego wszystkich wymaganych argumentów lub pomieszałeś ich typy, kompilator natychmiast zgłosi błąd. Warto osobno wspomnieć, że nie należy umieszczać logiki swojego programu w konstruktorze. Aby to zrobić, masz do dyspozycji metody, w których możesz opisać całą potrzebną funkcjonalność. Przyjrzyjmy się, dlaczego logika konstruktora to zły pomysł:
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(„Nazywa się nasza fabryka samochodów” + this.name);
   System.out.println(„Została założona” + this.age + " Lata temu" );
   System.out.println(„W tym czasie został wyprodukowany” + this.carsCount +  "samochody");
   System.out.println(„Średnio produkuje” + (this.carsCount/this.age) + „samochodów rocznie”);
}

   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Ford", 115 , 50000000);
   }
}
Mamy klasę CarFactory opisującą fabrykę produkującą samochody. Wewnątrz konstruktora inicjujemy wszystkie pola i tutaj umieszczamy logikę: wyświetlamy na konsoli informacje o fabryce. Wydawałoby się, że nie ma w tym nic złego, program działał idealnie. Wyjście konsoli:
Nasza fabryka samochodów nazywa się Ford. Powstała 115 lat temu. W tym czasie wyprodukowała 50 000 000 samochodów. Średnio produkuje 434 782 samochody rocznie.
Ale tak naprawdę podłożyliśmy bombę zegarową. A taki kod może bardzo łatwo doprowadzić do błędów. Wyobraźmy sobie, że teraz nie mówimy o Fordzie, ale o nowej fabryce „Amigo Motors”, która istnieje niecały rok i wyprodukowała 1000 samochodów:
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(„Nazywa się nasza fabryka samochodów” + this.name);
   System.out.println(„Została założona” + this.age + " Lata temu" );
   System.out.println(„W tym czasie został wyprodukowany” + this.carsCount +  "samochody");
   System.out.println(„Średnio produkuje” + (this.carsCount/this.age) + „samochodów rocznie”);
}


   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Amigo Motors", 0 , 1000);
   }
}
Wyjście konsoli:
Nasza fabryka samochodów nazywa się Amigo Motors Wyjątek w wątku „main” java.lang.ArithmeticException: / by zero Została założona 0 lat temu. W tym czasie wyprodukowała 1000 samochodów w CarFactory.<init>(CarFactory.java:15) o godz. CarFactory.main(CarFactory.java:23) Proces zakończony kodem zakończenia 1</init>
Przybyliśmy! Program zakończył się jakimś dziwnym błędem. Spróbujesz zgadnąć, jaki jest tego powód? Powodem jest logika, którą umieściliśmy w konstruktorze. Konkretnie w tej linijce:
System.out.println(„Średnio produkuje” + (this.carsCount/this.age) + „samochodów rocznie”);
Tutaj wykonujemy obliczenia i dzielimy liczbę wyprodukowanych samochodów przez wiek fabryki. A ponieważ nasza fabryka jest nowa (czyli ma 0 lat), wynikiem jest dzielenie przez 0, co jest zabronione w matematyce. W rezultacie program kończy się z błędem. Co powinniśmy byli zrobić? Przenieś całą logikę do osobnej metody i wywołaj ją, na przykład printFactoryInfo() . Możesz przekazać mu obiekt CarFactory jako parametr . Można tam też umieścić całą logikę, a jednocześnie przetworzyć ewentualne błędy, jak nasz z zerowymi latami. Do każdej jego własności. Konstruktory są potrzebne do prawidłowego ustawienia stanu obiektu. Dla logiki biznesowej mamy metody. Nie należy mieszać jednego z drugim.
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION