在Java中,构造函数用于创建类的实例。构造函数与方法几乎相似,只有两个不同之处——它的名称与类名相同,并且没有返回类型。有时构造函数也被称为特殊方法来初始化对象。
Java中的构造函数
每当我们使用new
关键字创建一个类的实例时,构造函数被调用并返回类的对象。由于构造函数只能返回类的对象,Java运行时会隐式地执行这一操作,我们不需要为其添加返回类型。如果我们给构造函数添加返回类型,它将变成类的一个方法。这是Java运行时区分普通方法和构造函数的方式。假设我们在Employee
类中有以下代码:
public Employee() {
System.out.println("Employee Constructor");
}
public Employee Employee() {
System.out.println("Employee Method");
return new Employee();
}
这里的第一个是构造函数,请注意它没有返回类型和返回语句。第二个是一个普通方法,我们在其中再次调用第一个构造函数以获得Employee实例并返回它。建议不要将方法名与类名相同,因为这会造成混淆。
Java中的构造函数类型
Java中有三种类型的构造函数。
- 建構子
- 預設建構子
- 帶參數的建構子
讓我們透過範例程式來深入研究這些建構子類型。
Java 中的預設建構子
在類別程式碼中並非總是需要提供建構子實作。如果我們沒有提供建構子,Java 就會為我們提供預設建構子的實作。讓我們看一個簡單的範例程式,其中使用了預設建構子,因為我們不會明確定義建構子。
package com.journaldev.constructor;
public class Data {
public static void main(String[] args) {
Data d = new Data();
}
}
- 預設建構子的唯一作用是初始化物件並將其返回給呼叫程式碼。
- 預設建構子總是不帶引數的,只有在沒有現有建構子的情況下,Java 編譯器才會提供它。
- 大多數情況下,我們滿足於使用預設建構子,因為其他屬性可以通過 getter 和 setter 方法進行訪問和初始化。
無參數建構子
package com.journaldev.constructor;
public class Data {
無參數的構造函數稱為無參數構造函數。就像覆蓋默認構造函數一樣,用於進行一些預初始化工作,例如檢查資源、網絡連接、日誌記錄等。讓我們快速看一下Java中的無參數構造函數。
public Data() {
System.out.println("No-Args Constructor");
}
public static void main(String[] args) {
Data d = new Data();
}
}
//無參數構造函數
帶參數的構造函數
帶參數的構造函數被稱為參數化構造函數。讓我們看一下Java中帶參數的構造函數的例子。
package com.journaldev.constructor;
public class Data {
private String name;
public Data(String n) {
System.out.println("Parameterized Constructor");
this.name = n;
}
public String getName() {
return name;
}
public static void main(String[] args) {
Data d = new Data("Java");
System.out.println(d.getName());
}
}
Java中的構造函數重載
當我們有多於一個構造函數時,在Java中就是構造函數重載。讓我們看一下Java程序中構造函數重載的例子。
package com.journaldev.constructor;
public class Data {
private String name;
private int id;
//無參數構造函數
public Data() {
this.name = "Default Name";
}
//帶一個參數的構造函數
public Data(String n) {
this.name = n;
}
//帶兩個參數的構造函數
public Data(String n, int i) {
this.name = n;
this.id = i;
}
public String getName() {
return name;
}
public int getId() {
return id;
}
@Override
public String toString() {
return "ID="+id+", Name="+name;
}
public static void main(String[] args) {
Data d = new Data();
System.out.println(d);
d = new Data("Java");
System.out.println(d);
d = new Data("Pankaj", 25);
System.out.println(d);
}
}
Java 中的私有構造函數
請注意,我們不能使用 abstract、final、static 和 synchronized 關鍵字與構造函數。但是,我們可以使用訪問修飾符來控制類對象的實例化。仍然可以使用 public
和 default
訪問,但是將構造函數設置為私有有什麼用呢?在這種情況下,任何其他類都無法創建該類的實例。好吧,當我們想要實現單例設計模式時,就會將構造函數設置為私有。由於 Java 自動提供默認構造函數,我們必須明確地創建一個構造函數並將其設置為私有。客戶類提供了一個實用的靜態方法來獲取該類的實例。下面是 Data
類的私有構造函數示例。
// 私有構造函數
private Data() {
// 用於單例模式實現的空構造函數
// 可以在類的 getInstance() 方法內使用的代碼
}
Java 中的建構子鏈
當一個建構子調用同一類別的另一個建構子時,這被稱為建構子鏈。我們必須使用this
關鍵字來調用該類別的另一個建構子。有時它被用於設置類別變量的一些默認值。請注意,另一個建構子調用應該是程式區塊中的第一條語句。此外,不應該有導致無限迴圈的遞迴調用。讓我們看一個 Java 程式中的建構子鏈的例子。
package com.journaldev.constructor;
public class Employee {
private int id;
private String name;
public Employee() {
this("John Doe", 999);
System.out.println("Default Employee Created");
}
public Employee(int i) {
this("John Doe", i);
System.out.println("Employee Created with Default Name");
}
public Employee(String s, int i) {
this.id = i;
this.name = s;
System.out.println("Employee Created");
}
public static void main(String[] args) {
Employee emp = new Employee();
System.out.println(emp);
Employee emp1 = new Employee(10);
System.out.println(emp1);
Employee emp2 = new Employee("Pankaj", 20);
System.out.println(emp2);
}
@Override
public String toString() {
return "ID = "+id+", Name = "+name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
I have overridden the toString() method to print some useful information about Employee object. Below is the output produced by above program.
Employee Created
Default Employee Created
ID = 999, Name = John Doe
Employee Created
Employee Created with Default Name
ID = 10, Name = John Doe
Employee Created
ID = 20, Name = Pankaj
注意一個建構子如何從另一個建構子中被調用,這就是建構子鏈的過程。
Java 超級建構子
有時候一個類別會繼承自一個超類,在這種情況下,如果我們需要調用超類的構造函數,我們可以使用super
關鍵字。讓我們看一個使用超類構造函數的例子。請注意,超類構造函數的調用應該是子類構造函數中的第一個語句。同樣,當實例化子類構造函數時,Java首先初始化超類,然後才初始化子類。因此,如果沒有顯式調用超類構造函數,則Java運行時會調用默認的無參數構造函數。通過一些例子程序來理解這些概念。假設我們有以下兩個類。
package com.journaldev.constructor;
public class Person {
private int age;
public Person() {
System.out.println("Person Created");
}
public Person(int i) {
this.age = i;
System.out.println("Person Created with Age = " + i);
}
}
package com.journaldev.constructor;
public class Student extends Person {
private String name;
public Student() {
System.out.println("Student Created");
}
public Student(int i, String n) {
super(i); // super class constructor called
this.name = n;
System.out.println("Student Created with name = " + n);
}
}
現在,如果我們像下面這樣創建一個Student對象;
Student st = new Student();
以上代碼的輸出將是:
Person Created
Student Created
所以調用了Student類的無參數構造函數,因為第一個語句中沒有超類調用,所以調用了Person類的無參數或默認構造函數。因此,輸出是如此。如果我們使用Student類的帶參構造函數,如Student st = new Student(34, "Pankaj");
,輸出將是:
Person Created with Age = 34
Student Created with name = Pankaj
在這裡,輸出很清楚,因為我們顯式地調用了超類構造函數,所以Java不需要從他們的方面進行任何額外的工作。
Java 複製構造函數
Java複製構造函數以同一類的對象作為參數並創建其副本。有時我們需要另一個對象的副本來進行一些處理。我們可以通過以下方式之一來實現這一點:
現在讓我們看看如何編寫一個複製構造函數。假設我們有一個類 Fruits
如下。
package com.journaldev.constructor;
import java.util.ArrayList;
import java.util.List;
public class Fruits {
private List<String> fruitsList;
public List<String> getFruitsList() {
return fruitsList;
}
public void setFruitsList(List<String> fruitsList) {
this.fruitsList = fruitsList;
}
public Fruits(List<String> fl) {
this.fruitsList = fl;
}
public Fruits(Fruits fr) {
List<String> fl = new ArrayList<>();
for (String f : fr.getFruitsList()) {
fl.add(f);
}
this.fruitsList = fl;
}
}
請注意,Fruits(Fruits fr)
執行深度拷貝以返回對象的副本。讓我們來看一個測試程序,以了解為什麼最好使用複製構造函數來複製對象。
package com.journaldev.constructor;
import java.util.ArrayList;
import java.util.List;
public class CopyConstructorTest {
public static void main(String[] args) {
List<String> fl = new ArrayList<>();
fl.add("Mango");
fl.add("Orange");
Fruits fr = new Fruits(fl);
System.out.println(fr.getFruitsList());
Fruits frCopy = fr;
frCopy.getFruitsList().add("Apple");
System.out.println(fr.getFruitsList());
frCopy = new Fruits(fr);
frCopy.getFruitsList().add("Banana");
System.out.println(fr.getFruitsList());
System.out.println(frCopy.getFruitsList());
}
}
上面程序的輸出是:
[Mango, Orange]
[Mango, Orange, Apple]
[Mango, Orange, Apple]
[Mango, Orange, Apple, Banana]
請注意,當使用複製構造函數時,原始對象和其副本彼此無關,對其中一個的任何修改都不會反映到另一個上。這就是Java中的構造函數的全部。
Source:
https://www.digitalocean.com/community/tutorials/constructor-in-java