JavaRush /Java Blog /Random-TW /Java 中的建構函數

Java 中的建構函數

在 Random-TW 群組發布
你好!今天我們將討論一個與我們的物件有關的非常重要的主題。在這裡,我們可以毫不誇張地說,你每天都會在實際工作中運用這些知識!我們將討論構造函數。您可能是第一次聽到這個術語,但實際上您可能使用過構造函數,只是您自己沒有註意到:) 我們稍後會看到這一點。

Java 中的建構函式是什麼以及為什麼需要它?

讓我們來看兩個例子。
public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {

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

   }
}
我們創建了我們的汽車並設定了它的模型和最大速度。然而,在實際項目中, Car物件顯然會有超過 2 個欄位。例如,16 個字段!
public class Car {

   String model;//model
   int maxSpeed;//max speed
   int wheels;// disk width
   double engineVolume;//engine capacity
   String color;//color
   int yearOfIssue;//year of issue
   String ownerFirstName;//Owner's name
   String ownerLastName;//owner's last name
   long price;//price
   boolean isNew;//new or not
   int placesInTheSalon;//number of seats in the cabin
   String salonMaterial;// interior material
   boolean insurance;//is it insured
   String manufacturerCountry;//manufacturer country
   int trunkVolume;// trunk volume
   int accelerationTo100km;//acceleration 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.yearOfIssue = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.placesInTheSalon = 2;
       bugatti.maxSpeed = 407;
       bugatti.model = "Bugatti Veyron";

   }

}
我們創建了一個新的Car物件。一個問題:我們有 16 個字段,但我們只初始化了 12 個!現在嘗試使用程式碼來尋找我們忘記的那些!沒那麼容易,對吧?在這種情況下,程式設計師很容易犯錯並跳過某些欄位的初始化。結果,程式行為將變得錯誤:
public class Car {

   String model;//model
   int maxSpeed;//max speed
   int wheels;// disk width
   double engineVolume;//engine capacity
   String color;//color
   int yearOfIssue;//year of issue
   String ownerFirstName;//Owner's name
   String ownerLastName;//owner's last name
   long price;//price
   boolean isNew;//new or not
   int placesInTheSalon;//number of seats in the cabin
   String salonMaterial;// interior material
   boolean insurance;//is it insured
   String manufacturerCountry;//manufacturer country
   int trunkVolume;// trunk volume
   int accelerationTo100km;//acceleration 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.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. Engine size - " + bugatti.engineVolume + ", trunk - " + bugatti.trunkVolume + ", salon is made of" + bugatti.salonMaterial +
       ", disc width - " + bugatti.wheels + ". Was acquired in 2018 by Mr. " + bugatti.ownerLastName);

   }

}
控制台輸出:
布加迪威龍模型。引擎排氣量 - 6.3,後行李箱 - 0,內裝由 null 製成,輪圈寬度 - 0。由 null 先生於 2018 年購買
你的買家花了 200 萬美元買了一輛車,顯然不喜歡被稱為「空先生」!但說真的,最後我們的程序最終得到了一個錯誤創建的對象- 一輛輪輞寬度為0(即根本沒有輪輞)的汽車,一個丟失的行李箱,由未知材料製成的內飾,甚至屬於未知的人。可以想像,在程式運行時怎麼會出現這樣的錯誤!我們需要以某種方式避免這種情況。我們需要我們的程序有一個限制:例如,當創建一個新的車輛物件時,必須始終為其指定模型和最大速度。否則,不允許創建物件。構造函數可以輕鬆應付這項任務。他們的名字是有原因的。建構函式創建類別的一種“骨架”,類別的每個新物件都必須與其相對應。為了方便起見,讓我們回到具有兩個欄位的Car類別的簡單版本。根據我們的要求, Car類別的建構函數將如下所示:
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);
}
注意構造函數是如何創建的。它與常規方法類似,但沒有傳回類型。在這種情況下,類別名稱在建構函數中指示,也使用大寫字母。在我們的例子中 -汽車。此外,建構函數使用 new-to-you 關鍵字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", 407);
       System.out.println(bugatti.model);
       System.out.println(bugatti.maxSpeed);
   }

}
控制台輸出:
布加迪威龍 407
構造函數成功分配了所需的值。您可能已經注意到建構函式與常規方法非常相似!就是這樣:建構函式是一個方法,只是有點具體:) 就像在方法中一樣,我們將參數傳遞給建構函式。就像呼叫方法一樣,如果不指定建構函數,呼叫建構函數將不起作用:
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!
   }

}
你看,設計師做到了我們想要實現的目標。現在,沒有速度或沒有模型就無法製造汽車!構造函數和方法之間的相似之處不止於此。就像方法一樣,建構函式可以重載。想像你家裡有兩隻貓。你把其中一隻當成小貓了,當你把第二隻成年後從街上帶回家時,你不知道他到底有多大。這意味著我們的程式應該能夠創建兩種類型的貓 - 第一隻貓有名字和年齡,第二隻貓只有名字。為此,我們將重載建構函數:
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 barsik = new Cat("Barsik", 5);
       Cat streetCatNamedBob = new Cat("Bob");
   }

}
對於帶有參數“name”和“age”的原始建構函數,我們新增了另一個僅帶有名稱的建構函數。我們在前面的課程中以相同的方式重載了方法。現在我們可以成功創建兩個版本的貓了:) 為什麼需要構造函數? - 2你還記得在講座開始時我們說過你已經使用過構造函數,但你只是沒有註意到它嗎?這是真實的。事實上,Java 中的每個類別都有一個所謂的預設建構子。它沒有任何參數,但每次創建任何類別的任何物件時都會觸發它。
public class Cat {

   public static void main(String[] args) {

       Cat barsik = new Cat(); //this is where the default constructor worked
   }
}
乍看之下這並不明顯。好了,我們創建了一個物件並創建了它,設計師的工作在哪裡?為了看到這一點,讓我們親手為Cat類別編寫一個空的建構函數,並在其中將一些短語印到控制台。如果顯示出來,那麼建構函式已經工作了。
public class Cat {

   public Cat() {
       System.out.println("Created a cat!");
   }

   public static void main(String[] args) {

       Cat barsik = new Cat(); //this is where the default constructor worked
   }
}
控制台輸出:
他們創造了一隻貓!
這是確認!預設構造函數始終不可見地存在於您的類別中。但您還需要了解它的另一個功能。當您建立帶有參數的建構函式時,預設建構函式將從類別中消失。這一點的證明,其實我們在上面已經看到了。在此程式碼中:
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(); //error!
   }
}
我們無法創建沒有名字和年齡的貓,因為我們為Cat定義了一個建構子:字串 + 數字。此後,預設構造函數立即從類別中消失。因此,請務必記住:如果您的類別中需要多個建構函式(包括一個空建構函式),則需要單獨建立它。例如,我們正在為獸醫診所創建一個程式。我們的診所希望做好事並幫助無家可歸的貓,我們不知道它們的名字或年齡。那我們的程式碼應該是這樣的:
public class Cat {

   String name;
   int age;

   //for domestic cats
   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   //for street cats
   public Cat() {
   }

   public static void main(String[] args) {

       Cat barsik = new Cat("Barsik", 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 barsik = new Cat("Barsik", 10); //error!
   }
}
錯誤!建構函數明確指出,當建立 Cat 物件時,必須按順序向其傳遞一個數字和一個字串。這就是為什麼我們的程式碼不起作用。請務必記住這一點,並在創建自己的類別時牢記這一點:
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 的汽車或其他「不正確」的物件。首先,它們的使用對於程式設計師本身來說是非常有益的。如果您自己初始化字段,則很可能會丟失某些內容並犯錯。但建構函式不會發生這種情況:如果您沒有將所有必需的參數傳遞給它或混合了它們的類型,編譯器將立即拋出錯誤。值得單獨提及的是,您不應該將程式的邏輯放在建構函式中。為此,您可以使用一些方法來描述您需要的所有功能。讓我們看看為什麼建構函數邏輯是一個壞主意:
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("She was founded" + this.age + " years ago" );
   System.out.println("During this time it was produced" + this.carsCount +  "cars");
   System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
}

   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Ford", 115 , 50000000);
   }
}
我們有一個CarFactory類,它描述了一個生產汽車的工廠。在建構函式內,我們初始化所有欄位並將邏輯放置在這裡:我們向控制台顯示有關工廠的一些資訊。看起來這並沒有什麼問題,程式運作得完美無瑕。控制台輸出:
我們的汽車廠叫福特,成立已經115年了,這段時間生產了50,000,000輛汽車,平均每年生產434,782輛汽車。
但事實上,我們已經埋下了定時炸彈。而且這樣的程式碼很容易導致錯誤。讓我們想像一下,現在我們談論的不是福特,而是新工廠“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 factory is called" + this.name);
   System.out.println("She was founded" + this.age + " years ago" );
   System.out.println("During this time it was produced" + this.carsCount +  "cars");
   System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
}


   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Amigo Motors", 0 , 1000);
   }
}
控制台輸出:
我們的汽車工廠名為Amigo Motors 異常,位於線程“main” java.lang.ArithmeticException: / by Zero 它成立於0 年前在此期間,它在CarFactory.<init>(CarFactory.java:15) 生產了1000 輛汽車CarFactory.main(CarFactory.java:23) 進程完成,退出程式碼 1</init>
我們到了!程式因一些奇怪的錯誤而結束。你會試著猜猜原因是什麼嗎?原因是我們放置在建構函數中的邏輯。具體來說,在這一行中:
System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
這裡我們進行計算,將生產的汽車數量除以工廠的年齡。而且由於我們的工廠是新的(即0年),所以結果除以0,這在數學中是禁止的。結果,程式因錯誤而終止。我們該做什麼?將所有邏輯移至單獨的方法中並呼叫它,例如printFactoryInfo()您可以將CarFactory物件作為參數傳遞給它。您也可以將所有邏輯放在那裡,同時處理可能的錯誤,就像我們零年的錯誤一樣。每個人都有自己的。需要建構函數來正確設定物件的狀態。對於業務邏輯,我們有方法。你不應該將其中一種與另一種混合。
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION