CodeGym /Java Blog /Toto sisi /Java構造函數
John Squirrels
等級 41
San Francisco

Java構造函數

在 Toto sisi 群組發布
你好!今天我們考慮一個非常重要的話題,它與我們的對像有關。毫不誇張地說,我們可以說您每天都會在現實生活中使用這個話題!我們正在談論 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";

   }

}
我們創建了一個新的Car對象。有一個問題:我們有 16 個字段,但我們只初始化了 12 個!現在查看代碼並嘗試找到我們忘記的字段!沒那麼容易吧?在這種情況下,程序員很容易犯錯誤,無法初始化某些字段。結果,該程序將表現不正確:

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

   }

}
控制台輸出: 型號:布加迪威龍。發動機容積:6.3。行李箱容積:0。客艙材料:無。輪寬:0。空先生於 2018 年購買 您的買家,為這輛車放棄了 200 萬美元,顯然不喜歡被稱為“空先生”!但嚴重的是,最重要的是我們的程序錯誤地創建了一個對象:車輪寬度為 0 的汽車(即根本沒有輪子)、丟失的行李箱、未知材料製成的小屋,最重要的是,未定義的所有者. 你只能想像程序運行時怎麼會出現這樣的錯誤!我們需要以某種方式避免這種情況。我們需要限制我們的程序:在創建新Car時對象,我們希望始終指定字段,例如模型和最大速度。否則,我們要阻止對象的創建。構造函數可以輕鬆處理此任務。他們得名是有原因的。構造函數創建了一種每個新對像都必須匹配的類“骨架”。為了方便起見,讓我們回到具有兩個字段的Car類的更簡單版本。考慮到我們的要求,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);
}
注意構造函數是如何聲明的。它類似於常規方法,但沒有返回類型。此外,構造函數指定以大寫字母開頭的類名 ( Car )。此外,構造函數與對您來說是新的關鍵字一起使用:this。關鍵字this用於指示特定對象。構造函數中的代碼

public Car(String model, int maxSpeed) {
   this.model = model;
   this.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 構造函數正確分配了所需的值。您可能已經註意到構造函數與普通方法非常相似!就是這樣。構造函數實際上是一種方法,但具有特定的功能 :) 就像方法一樣,我們將參數傳遞給構造函數。就像調用方法一樣,調用構造函數將不起作用,除非您指定它們:

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!
   }
  
}
您可以看到構造函數完成了我們試圖實現的目標。現在你不能創造沒有速度或模型的汽車!構造函數和方法之間的相似性並沒有就此結束。就像方法一樣,構造函數可以被重載。假設您家裡有 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 中的每個類都有所謂的默認構造函數。它不接受任何參數,但每次您創建任何類的任何對象時都會調用它。

public class Cat {

   public static void main(String[] args) {

       Cat smudge = new Cat(); // The default constructor is invoked here
   }
}
乍一看,它是看不見的。我們創建了一個對象,那又如何呢?構造函數在這裡做什麼?為了看到它,讓我們顯式地為Cat類編寫一個空的構造函數。我們將在其中顯示一些短語。如果顯示該短語,則調用了構造函數。

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
   }
}
控制台輸出: 已創建一隻貓! 有確認!默認構造函數總是不可見地存在於您的類中。但是你還需要知道一件事。一旦創建帶參數的構造函數,默認構造函數就會從類中刪除。事實上,我們已經在上面看到了這方面的證據。它在這段代碼中:

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構造函數。這導致默認構造函數立即從類中消失。所以一定要記住,如果你的類中需要多個構造函數,包括一個無參構造函數,你必須單獨聲明它。例如,假設我們正在為獸醫診所創建一個程序。我們診所希望做好事,幫助那些名字和年齡不詳的無家可歸的小貓。那麼我們的代碼應該是這樣的:

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();
   }
}
現在我們已經編寫了顯式默認構造函數,我們可以創建兩種類型的貓 :) 與任何方法一樣,傳遞給構造函數的參數順序非常重要。讓我們在構造函數中 交換nameage參數。

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對象時,必須依次傳遞一個數字和一個字符串。所以,我們的代碼不起作用。一定要記住這一點,並在聲明自己的類時記住這一點:

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("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類。在構造函數中,我們初始化所有字段並包含一些邏輯:我們顯示有關工廠的一些信息。好像這沒什麼不好的。該程序運行良好。控制台輸出: 我們的汽車廠叫福特,它成立於115年前,從那時起,它生產了5000萬輛汽車平均每年生產434782輛汽車 但我們實際上埋下了一個延時地雷。而且這種代碼很容易導致錯誤。假設現在我們談論的不是福特,而是一家名為“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);
   }
}
控制台輸出: 我們的汽車工廠名為 Amigo Motors Exception in thread "main" java.lang.ArithmeticException: / by zero 它成立於 0 年前,從那時起,它 在 CarFactory 生產了 1000 輛汽車。 (CarFactory.java:15) 在 CarFactory.main(CarFactory.java:23) 進程完成,退出代碼為 1 繁榮!該程序以某種無法理解的錯誤結束。你能試著猜出原因嗎?問題出在我們放入構造函數的邏輯中。更具體地說,這一行:

System.out.println("On average, it produces " + (this.carsCount/this.age) + " cars per year");
在這裡,您正在執行計算並將生產的汽車數量除以工廠的年齡。由於我們的工廠是新工廠(即 0 歲),我們除以 0,這在數學上是做不到的。因此,程序因錯誤而終止。

我們應該做什麼?

將所有邏輯放在一個單獨的方法中。我們稱它為printFactoryInfo()。您可以將CarFactory對像作為參數傳遞給它。您可以將所有邏輯放在那裡,同時處理潛在的錯誤(比如我們涉及零年的錯誤)。每個人都有自己的。需要構造函數來設置有效的對象狀態。我們有業務邏輯的方法。不要將一個與另一個混在一起。 為了鞏固您所學的知識,我們建議您觀看我們的 Java 課程中的視頻課程
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION