Java構造函數

構造函數在創建對象時用來初始化對象。 它與類具有相同的名稱,並且在語法上與方法類似。但是,構造函數沒有返回值類型。

通常,使用構造函數為類定義的實例變數提供初始值,或執行創建完全形成的對象所需的其他啟動過程。

所有類都有構造函數,無論是否定義了構造函數,因為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.zaixian.constructor;

public class Car {

    public static void main(String[] args) {
        Car c = new Car();
    }
}
  • 默認構造函數的唯一作用是初始化對象並將其返回給調用代碼。
  • 默認構造函數始終沒有參數,只有在沒有定義現有構造函數的情況下由java編譯器提供。
  • 大多數情況下,可以使用默認構造函數本身,因為可以通過getter/setter方法訪問和初始化其他屬性。

1.2. 無參構造函數

沒有任何參數的構造函數稱為無參構造函數。 這就像覆蓋默認構造函數並用於執行一些預初始化的東西,例如:檢查資源,網路連接,日誌記錄等。通過以下代碼流覽一下java中的無參構造函數。

package com.zaixian.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.zaixian.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.zaixian.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.zaixian.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("zaixian", 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 = zaixian

從另一個構造函數調用一個構造函數,這個構造函數被稱為構造函數鏈接過程。

5. Java超類構造函數

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

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

Person類源代碼 -


package com.zaixian.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.zaixian.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.zaixian.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.zaixian.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]

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


上一篇: java中方法重載和方法重寫的區別 下一篇:無