CodeGym /Java блог /Случаен /Java конструктори
John Squirrels
Ниво
San Francisco

Java конструктори

Публикувано в групата
здрасти Днес ще разгледаме една много важна тема, която засяга нашите обекти. Без преувеличение можем да кажем, че ще използвате тази тема в реалния живот всеки ден! Говорим за конструктори на Java. Може би за първи път чувате този термин, но всъщност вече сте използвали конструктори. Просто не си разбрал :) В това се убеждаваме по-късно.

Какво, за бога, са конструкторите и защо са необходими?

Нека разгледаме два примера.

public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {

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

   }
}
Създадохме нашата кола и зададохме нейния модел и максимална скорост. Но обектът Car очевидно няма да има 2 полета в реален проект. Например, може да има 16 полета!

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

   }

}
Създадохме нов обект Автомобил . Има един проблем: имаме 16 полета, но инициализирахме само 12 ! Вижте codeа сега и се опитайте да намерите полетата, които сме забравor! Не е толкова лесно, а? В тази ситуация програмистът лесно може да направи грешка и да не успее да инициализира някое поле. В резултат на това програмата ще се държи неправилно:

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

   }

}
Изход на конзолата: Модел: Bugatti Veyron. Обем на двигателя: 6.3. Обем на багажника: 0. Материал на кабината: нула. Широчина на колелото: 0. Закупен през 2018 г. от Mr. null Вашият купувач, който се отказа от 2 мorона долара за колата, очевидно няма да хареса да го наричат ​​" г-н null "! Но сериозно, основното е, че нашата програма създаде обект неправилно: кола с ширина на колелата 0 (т.е. без колела), липсващ багажник, кабина, напequalsа от неизвестен материал, и преди всичко недефиниран собственик . Можете само да си представите How подобна грешка може да "избухне", когато програмата работи! Трябва по няHowъв начин да избягваме подобни ситуации. Трябва да ограничим нашата програма: когато създаваме нова колаобект, искаме полетата, като модел и максимална скорост, винаги да бъдат посочени. В противен случай искаме да предотвратим създаването на обекта. Конструкторите се справят с лекота с тази задача. Те са получor името си с причина. Конструкторът създава един вид "скелет" на класа, на който трябва да съответства всеки нов обект. За улеснение да се върнем към по-простата version на класа Автомобил с две полета. Отчитайки нашите изисквания, конструкторът на класа Car ще изглежда така:

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);
}
Обърнете внимание How се декларира конструктор. Подобен е на обикновен метод, но няма тип връщане. Освен това конструкторът указва името на класа ( Car ), започващо с главна буква. Освен това конструкторът се използва с ключова дума, която е нова за вас: this . Ключовата дума this е за указване на определен обект. Кодът в конструктора

public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
може да се тълкува почти дословно: "Моделът за тази кола (тази, която създаваме сега) е аргументът на модела , предаден на конструктора. MaxSpeed ​​за тази кола (тази, която създаваме) е аргументът maxSpeed , предаден на конструктор." И точно това се случва:

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

}
Конзолен изход: Bugatti Veyron 378 Конструкторът е задал правилно необходимите стойности. Може би сте забелязали, че конструкторът е много подобен на обикновен метод! Така е. Конструкторът наистина е метод, но със специфични функции :) Точно Howто при методите, ние предаваме аргументи на нашия конструктор. И точно като извикването на метод, извикването на конструктор няма да работи, освен ако не ги посочите:

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!
   }
  
}
Можете да видите, че конструкторът постига това, което се опитвахме да постигнем. Сега не можете да създадете кола без скорост or модел! Прorката между конструктори и методи не свършва до тук. Точно като методите, конструкторите могат да бъдат претоварени. Представете си, че имате 2 домашни котки у дома. Имате едно от тях като коте. Но второто, което взехте от улицата, когато беше вече пораснало, и не знаете точно на колко години е. В този случай искаме нашата програма да може да създава два вида котки: такива с име и възраст (за първата котка) и такива само с име (за втората котка). За целта ще претоварим конструктора:

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

}
В допълнение към оригиналния конструктор с параметри "name" и "age", добавихме още един само с параметър name. Точно по същия начин, по който претоварихме методите в предишните уроци. Сега можем да създадем и двата вида котки :)
Защо имаме нужда от конструктори?  - 2
Помните ли, че в началото на урока казахме, че вече сте използвали конструктори, без да го осъзнавате? Имахме предвид това, което казахме. Факт е, че всеки клас в Java има това, което се нарича конструктор по подразбиране. Не приема ниHowви аргументи, но се извиква всеки път, когато създавате обект от произволен клас.

public class Cat {

   public static void main(String[] args) {

       Cat smudge = new Cat(); // The default constructor is invoked here
   }
}
На пръв поглед е невидимо. Създадохме обект, Howво от това? Къде конструкторът прави нещо тук? За да го видите, нека изрично напишем празен конструктор за класа Cat . Ще покажем няHowва фраза вътре в него. Ако фразата е показана, тогава конструкторът е бил извикан.

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
   }
}
Изход от конзолата: Създадена е котка! Ето потвърждението! Конструкторът по подразбиране винаги присъства невидимо във вашите класове. Но трябва да знаете още нещо за това. Конструкторът по подразбиране се елиминира от клас, след като създадете конструктор с аргументи. Всъщност вече видяхме доказателство за това по-горе. Беше в този code:

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!
   }
}
Не можахме да създадем Cat без име и възраст, защото декларирахме конструктор Cat с параметри string и int . Това накара конструктора по подразбиране незабавно да изчезне от класа. Така че не забравяйте да запомните, че ако имате нужда от няколко конструктора във вашия клас, включително конструктор без аргументи, ще трябва да го декларирате отделно. Да предположим например, че създаваме програма за ветеринарна клиника. Нашата клиника иска да направи добри дела и да помогне на бездомни котенца, чиито имена и възраст са неизвестни. Тогава нашият code трябва да изглежда така:

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();
   }
}
Сега, след като сме написали изричен конструктор по подразбиране, можем да създадем и двата типа котки :) Както при всеки метод, редът на аргументите, предавани на конструктора, е много важен. Нека разменим аргументите име и възраст в нашия конструктор.

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!
   }
}
Грешка! Конструкторът ясно определя, че когато се създава Cat обект, трябва да му бъдат предадени число и низ в този ред. И така, нашият code не работи. Не забравяйте да запомните това и да го имате предвид, когато декларирате вашите собствени класове:

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

public Cat(int age, String name) {
   this.age = age;
   this.name = name;
}
Това са два напълно различни конструктора! Ако трябваше да изразим в едно изречение отговора на въпроса „Защо имам нужда от конструктор?“, бихме могли да кажем: „За да гарантирам, че обектите винаги имат валидно състояние“. Когато използвате конструктори, всички ваши променливи ще бъдат правилно инициализирани. Вашите програми няма да имат коли със скорост 0 or други "невалидни" обекти. Основната им полза е за програмиста. Ако инициализирате полета ръчно (след създаване на обект), има голям риск да пропуснете нещо и да въведете грешка. Но това няма да се случи с конструктор: ако не успеете да подадете всички необходими аргументи or подадете грешни типове аргументи, компилаторът веднага ще регистрира грешка. Отделно трябва да кажем, че не трябва да поставяте вашата програма s логика вътре в конструктор. За това са методите. Методите са мястото, където трябва да дефинирате цялата необходима функционалност. Нека да видим защо добавянето на логика към конструктор е лоша идея:

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);
   }
}
Имаме клас CarFactory , който описва фабриката за автомобor. Вътре в конструктора инициализираме всички полета и включваме няHowва логика: показваме малко информация за фабриката. Изглежда, че няма нищо лошо в това. Програмата работи добре. Конзолна продукция: Нашата фабрика за автомобor се нарича Ford. Основана е преди 115 години. Оттогава е произвела 50000000 коли. Средно произвежда 434782 коли на година. Но ние всъщност заложихме мина със закъснение. И този вид code може много лесно да доведе до грешки. Да предположим, че сега говорим не за Ford, а за нов завод, наречен "Amigo Motors", който съществува от по-малко от година и е произвел 1000 автомобила:

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);
   }
}
Конзолен изход: Нашата фабрика за автомобor се нарича Amigo Motors Exception в нишката "main" java.lang.ArithmeticException: / by zero Основана е преди 0 години Оттогава е произвела 1000 коли в CarFactory. (CarFactory.java:15) в CarFactory.main(CarFactory.java:23) Процесът завърши с изходен code 1 Бум! Програмата завършва с няHowва неразбираема грешка. Можете ли да се опитате да отгатнете причината? Проблемът е в логиката, която влагаме в конструктора. По-конкретно, този ред:

System.out.println("On average, it produces " + (this.carsCount/this.age) + " cars per year");
Тук извършвате изчисление и разделяте броя на произведените автомобor на възрастта на фабриката. И тъй като нашата фабрика е нова (т.е. на 0 години), делим на 0, което не можем да направим по математика. Следователно програмата се прекратява с грешка.

Какво трябваше да направим?

Поставете цялата логика в отделен метод. Нека го наречем printFactoryInfo() . Можете да му предадете обект CarFactory като аргумент. Можете да поставите цялата логика там и едновременно с това да обработвате потенциални грешки (като нашата, включваща нула години). Всеки с вкуса си. Необходими са конструктори за задаване на валидно състояние на обекта. Имаме методи за бизнес логика. Не смесвайте едното с другото. За да затвърдите наученото, ви предлагаме да гледате видео урок от нашия курс по Java
Коментари
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION