摘要:Java 也採用了構造器,並且還提供了一個垃圾收集器(garbage collector),當不再使用記憶體資源的時候,垃圾收集器會自動將其釋放。
本文分享自華為雲社群《一文帶你瞭解 Java 中的構造器》,作者: 宇宙之一粟 。
C ++ 引入了構造器(constructor,也叫建構函式)的概念,它是在建立物件時被自動呼叫的特殊方法。
Java 也採用了構造器,並且還提供了一個垃圾收集器(garbage collector),當不再使用記憶體資源的時候,垃圾收集器會自動將其釋放。
在 Java 中,可以通過編寫構造器來確保每個物件的初始化。但是這裡有兩個問題:
C++ 語言採用的方案就是將構造器和類的名字定義相同,Java 也採用了這個方案。
構造器的作用是用來建立一個新的類的範例,當一個物件被建立時,JVM 使用一個建構函式,併為其分配記憶體空間。
class ClassName { ClassName() { } }
例如,在下面的範例中,我們建立了一個名為 ReLearnConstructor 的建構函式。在建構函式內部,我們正在初始化 hello 變數的值。:
public class ReLearnConstructor { String hello; // 屬性 // 構造器 public ReLearnConstructor() { hello = "Hello, Constructor!"; } public static void main(String[] args) { ReLearnConstructor rc = new ReLearnConstructor(); System.out.println(rc.hello); } }
注意建立 ReLearnConstructor 類的物件的語句:ReLearnConstructor rc = new ReLearnConstructor();
在這裡,當建立物件時,呼叫 ReLearnConstructor 建構函式。並且,hello 變數的值被初始化。
因此列印的 hello 的值為:
建構函式的目的是初始化物件的狀態,為所有宣告的屬性賦值。如果我們沒有自定義建構函式,JVM 就會為這些屬性分配預設值。
原始型別的預設值:
對於其他 Java 參照型別,預設值是null,這意味著參照型別的屬性沒有被分配任何值。
後面可以用程式碼檢視這些預設值。
在 Java 中,有三種型別的構造器:
與方法類似,Java 建構函式可能有引數,也可能沒有任何引數。如果建構函式不接受任何引數,則稱為無引數構造器。例如上述程式碼中 ReLearnConstructor 構造器就是:
// 無參構造器 public ReLearnConstructor() { hello = "Hello, Constructor!"; }
字面理解,具有引數的建構函式稱為有引數構造器。那為什麼需要使用有參構造器?
有參構造器可用於為不同物件提供不同初始化的值。 例如:
public class ReLearnConstructor { String languages; // 接受單個引數的構造器 public ReLearnConstructor(String lang) { languages = lang; System.out.println("我在學習 " + languages + " 語言!"); } public static void main(String[] args) { // 向構造器中傳入不同的值 ReLearnConstructor rc1 = new ReLearnConstructor("Java"); ReLearnConstructor rc2 = new ReLearnConstructor("Go"); ReLearnConstructor rc3 = new ReLearnConstructor("Python"); } }
執行結果:
如果我們不建立任何建構函式,Java 編譯器會在程式執行期間自動建立一個無引數建構函式。這個建構函式稱為預設建構函式。來看一個例子;
public class ReLearnConstructor { String languages; int a; boolean b; float c; public static void main(String[] args) { ReLearnConstructor rc = new ReLearnConstructor(); System.out.println("預設值:"); System.out.println("languages:" + rc.languages); System.out.println("a:" + rc.a); System.out.println("b:" + rc.b); System.out.println("c:" + rc.c); } }
執行結果:
預設值: languages:null a:0 b:false c:0.0
可以看到,我們還沒有建立任何建構函式。因此,Java 編譯器會自動建立預設建構函式。上述表格得以印證。
如果我們不用構造器來給屬性賦值的話,可以先使用 new 運運算元獲取類的範例,並使用類的 setter 方法設定值,如下:
import java.util.Arrays; class Person { private String name; private int age; @Override public String toString() { return Arrays.asList(name, String.valueOf(age)).toString(); } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } // getters } // Initialize an object in Java class Main { public static void main(String[] args) { Person person = new Person(); person.setName("Yuzhou1su"); person.setAge(22); System.out.println(person); } }
通過構造器進行初始化就可以省去我們的 setter 方法。
如下的例子:
import java.util.Arrays; class Person { private String name; private int age; // 構造器 public Person(String name, int age) { this.name = name; this.age = age; } public String toString() { return Arrays.asList(name, String.valueOf(age)).toString(); } } class SimpleConstructor { public static void main(String[] args) { Person person = new Person("Yuzhou1su", 22); System.out.println(person); } }
執行結果:
[Yuzhou1su, 22]
與 Java 方法過載類似,我們也可以建立兩個或多個具有不同引數的建構函式。這稱為建構函式過載。
public class ReLearnConstructor { String language; public ReLearnConstructor() { this.language = "Java"; } // 構造器 public ReLearnConstructor(String language) { this.language = language; } public void getName() { System.out.println("程式語言:" + this.language); } public static void main(String[] args) { ReLearnConstructor rc1 = new ReLearnConstructor(); ReLearnConstructor rc2 = new ReLearnConstructor("Python"); rc1.getName(); rc2.getName(); } }
在上面的例子中,我們有兩個建構函式:ReLearnConstructor() 和 ReLearnConstructor(String language)。在這裡,兩個建構函式都用不同的值初始化變數語言的值。根據建立物件時傳遞的引數,呼叫不同的建構函式,分配不同的值。
執行結果:
程式語言:Java
程式語言:Python
Java 中的拷貝構造方法是一種使用該類的一個物件構造另外一個物件的構造方法。
複製建構函式是一種特殊建構函式,用於將新物件建立為現有物件的副本。它只需要一個引數,它將是同一類的另一個範例。我們可以使用 this() 語句從複製建構函式中顯式呼叫另一個建構函式:
public class ReLearnConstructor { private String language; // 構造器 public ReLearnConstructor(String language) { this.language = language; } // 拷貝構造器 public ReLearnConstructor(ReLearnConstructor rc) { this.language = rc.language; } public void getName() { System.out.println("程式語言:" + this.language); } public static void main(String[] args) { ReLearnConstructor rc = new ReLearnConstructor("Python"); ReLearnConstructor copyOfrc = new ReLearnConstructor(rc); rc.getName(); copyOfrc.getName(); } }
執行結果:
程式語言:Python
程式語言:Python
當需要拷貝一個帶有多個成員變數的複雜物件或者想構造已存在物件的深拷貝物件時非常有用。
除了上文介紹的使用構造器的方法,另一種初始化物件的方法是使用「雙大括號初始化」。這將建立一個匿名內部類,其中只有一個範例初始化程式。建議不要使用這種方法。
import java.util.Arrays; class Person { private String name; private int age; @Override public String toString() { return Arrays.asList(name, String.valueOf(age)).toString(); } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } // getters } // Initialize an object in Java class Main { public static void main(String[] args) { // Anonymous class Person person = new Person() {{ // Initializer block setName("Yuzhou1su"); setAge(22); }}; System.out.println(person); } }
編譯器會報如下錯誤:
Illegal modifier for the constructor in type ReLearnConstructor; only public, protected & private are permitted