Java建構函式

2019-10-16 22:23:19

建構函式在建立物件時用來初始化物件。 它與類具有相同的名稱,並且在語法上與方法類似。但是,建構函式沒有返回值型別。

通常,使用建構函式為類定義的範例變數提供初始值,或執行建立完全形成的物件所需的其他啟動過程。

所有類都有建構函式,無論是否定義了建構函式,因為Java會自動提供一個預設建構函式,將所有成員變數初始化為零。 但是,一旦定義了自己的建構函式,就不再使用預設建構函式。

語法

以下是建構函式的語法 -

class ClassName {
   ClassName() {
   }
}

每當使用new關鍵字建立類的範例時,都會呼叫建構函式並返回類的物件。 由於建構函式只能將物件返回給類,它是由java執行時隱式完成的,不應該向它新增返回型別。

如果將返回型別新增到建構函式,那麼它將成為類的方法。 這是java執行時區分普通方法和建構函式的方式。 假設在Employee類中有以下程式碼。

import java.io.*;

public class Employee {

    public Employee() {
        System.out.println("Employee建構函式");
    }


    public Employee Employee() {
        System.out.println("Employee一般方法");
        return new Employee();
    }
}

這裡第一個是建構函式,因為它沒有返回型別和返回語句。 第二個是一個常規方法,我們再次呼叫第一個建構函式來獲取Employee範例並返回它。建議不要將方法名稱與類名相同,因為會容易造成混淆。

1. Java中建構函式的型別

java中有三種型別的建構函式。

  • 預設建構函式
  • 無引數建構函式
  • 引數化建構函式

下面將通過範例程式來學習這些建構函式型別。

1.1. Java中的預設建構函式

不需要始終在類程式碼中提供建構函式實現。如果不提供建構函式,那麼java提供預設的建構函式實現供我們使用。 下面來看一個使用預設建構函式的簡單程式,它沒有顯式定義建構函式。

package com.yiibai.constructor;

public class Car {

    public static void main(String[] args) {
        Car c = new Car();
    }
}
  • 預設建構函式的唯一作用是初始化物件並將其返回給呼叫程式碼。
  • 預設建構函式始終沒有引數,只有在沒有定義現有建構函式的情況下由java編譯器提供。
  • 大多數情況下,可以使用預設建構函式本身,因為可以通過getter/setter方法存取和初始化其他屬性。

1.2. 無參建構函式

沒有任何引數的建構函式稱為無參建構函式。 這就像覆蓋預設建構函式並用於執行一些預初始化的東西,例如:檢查資源,網路連線,紀錄檔記錄等。通過以下程式碼瀏覽一下java中的無參建構函式。

package com.yiibai.constructor;

public class Car {
    // 無參建構函式
    public Car() {
        System.out.println("No-Args Constructor");
    }
    public static void main(String[] args) {
        Car d = new Car();
    }
}

當呼叫new new()時,將呼叫無參建構函式。執行上面程式碼,程式在控制台輸出結果如下:

No-Args Constructor

1.3. 引數化建構函式

帶引數的建構函式稱為引數化建構函式。下面來看看java中引數化建構函式的例子。

package com.yiibai.constructor;

public class Language {

    private String name;

    public Language(String n) {
        System.out.println("Parameterized Constructor");
        this.name = n;
    }

    public String getName() {
        return name;
    }

    public static void main(String[] args) {
        Language lang = new Language("Java");
        System.out.println(lang.getName());
    }

}

執行上面程式碼,程式在控制台輸出結果如下:

Parameterized Constructor
Java

2. 在Java中建構函式過載

當有多個建構函式時,它就是java中的建構函式過載。通過下面範例程式碼來了解java程式中建構函式過載。

package com.yiibai.constructor;

public class Data {

    private String name;
    private int id;

    //no-args constructor
    public Data() {
        this.name = "Default Name";
    }
    //one parameter constructor
    public Data(String n) {
        this.name = n;
    }
    //two parameter constructor
    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("Maxsu", 25);
        System.out.println(d);

    }

}

3. Java中的私有建構函式

不能將abstractfinalstaticsynchronized關鍵字與建構函式一起使用。 但是,可以使用存取修飾符來控制類物件的範例化。 使用public存取和default存取仍然沒有問題,但是將建構函式設為私有的用途是什麼? 在這種情況下,任何其他類都將無法建立該類的範例。

如果想要實現單例設計模式,建構函式是private。 由於java自動提供預設建構函式,因此必須顯式建立建構函式並將其保持為private。 用戶端類提供了實用程式靜態方法來獲取類的範例。 下面給出了Data類的私有建構函式的範例。

// private 建構函式
private Data() {
    // 單例模式實現的空建構函式
    // 可以在類的getInstance()方法中使用程式碼
}

4. Java建構函式連結

當建構函式呼叫同一個類的另一個建構函式時,它被稱為建構函式連結。需要使用this關鍵字來呼叫該類的另一個建構函式。有時它用於設定類變數的一些預設值。

請注意,另一個建構函式呼叫應該是程式碼塊中的第一個語句。 此外,不應該有一個會產生無限迴圈的遞迴呼叫。下面來看看java程式中建構函式連結的一個例子。


package com.yiibai.constructor;

public class Employee {

    private int id;
    private String name;

    public Employee() {
        this("Maxsu", 1999);
        System.out.println("Default Employee Created");
    }

    public Employee(int i) {
        this("Maxsu", 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("Yiibai", 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;
    }

}

上程式碼程式碼中,已經重寫了toString()方法來列印一些有關Employee物件的資訊。 以下是上面程式產生的輸出 -

Employee Created
Default Employee Created
ID = 1999, Name = Maxsu
Employee Created
Employee Created with Default Name
ID = 10, Name = Maxsu
Employee Created
ID = 20, Name = Yiibai

從另一個建構函式呼叫一個建構函式,這個建構函式被稱為建構函式連結過程。

5. Java超類建構函式

有時一個類是從超類繼承的,在這種情況下,如果必須呼叫超類建構函式,那麼可以使用super關鍵字。

請注意,super造函式呼叫應該是子類建構函式中的第一個語句。 此外,在範例化子類建構函式時,java首先初始化超類,然後初始化子類。 因此,如果未顯式呼叫超類建構函式,則java執行時將呼叫defaultno-args建構函式。下面將通過一些範例程式來理解這些概念。
假設有兩個類,如下所示。

Person類原始碼 -


package com.yiibai.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);
    }

}

Student類原始碼 -

package com.yiibai.constructor;

public class Student extends Person {

    private String name;

    public Student() {
        System.out.println("Student Created");
    }

    public Student(int i, String n) {
        super(i); // 超類建構函式呼叫
        this.name = n;
        System.out.println("Student Created with name = " + n);
    }

}

現在,如果建立一個Student物件,如下所示:

Student st = new Student();

那將輸出什麼結果? 上面程式碼的輸出將是:

Person Created
Student Created

所以呼叫轉到了Student類的no-args建構函式,因為在第一個語句中沒有super呼叫,所以呼叫了Person類的no-args或預設建構函式。如果使用Student類的引數化建構函式作為 -

Student st = new Student(1999,"Maxsu");

那麼輸出將是:

Person Created with Age = 1999
Student Created with name = Maxsu

這裡輸出很明顯,因為我們顯式呼叫了超類建構函式,所以java不需要從no-args建構函式這邊做任何額外的工作。

6. Java拷貝建構函式

Java拷貝建構函式將相同類的物件作為引數,並建立它的副本。有時需要另一個物件的副本來進行一些處理。 可以通過以下方式做到這一點:

  • 實現克隆。
  • 提供用於物件深拷貝的方法。
  • 實現一個複製建構函式。

現在來看看如何編寫複製建構函式,假設有一個類:Fruits,程式碼如下。


package com.yiibai.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.yiibai.constructor;

import java.util.ArrayList;
import java.util.List;

public class ConstructorTest {

    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 fr = fr;
        fr.getFruitsList().add("Apple");

        System.out.println(fr.getFruitsList());

        fr = new Fruits(fr);
        fr.getFruitsList().add("Banana");
        System.out.println(fr.getFruitsList());
        System.out.println(fr.getFruitsList());

    }

}

執行上面查詢語句,得到以下結果:


[Mango, Orange]
[Mango, Orange, Apple]
[Mango, Orange, Apple]
[Mango, Orange, Apple, Banana]

請注意,當使用複製建構函式時,原始物件和它的副本彼此無關,並且其中一個中的任何修改都不會反映到其他物件中。