摘要:Java 也採用了構造器,並且還提供了一個垃圾收集器(garbage collector),當不再使用記憶體資源的時候,垃圾收集器會自動將其釋放。 本文分享自華為雲社區《一文帶你瞭解 Java 中的構造器》,作者: 宇宙之一粟 。 C ++ 引入了構造器(constructor,也叫構造函數)的 ...
摘要: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 就會為這些屬性分配預設值。
原始類型的預設值:
- 整數類型是 0
- 浮點類型是 0.0
- 布爾類型是 false
對於其他 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 編譯器會自動創建預設構造函數。上述表格得以印證。
原生方法和構造器的區別
- 構造函數必須與在 Java 中定義的類具有相同的名稱
- 當方法沒有返回任何值時,構造函數不會返回任何類型,而方法則具有返回類型或 void
- 在對象創建時,僅調用構造函數一次,而方法可以被調用任何次數
如果我們不用構造器來給屬性賦值的話,可以先使用 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); } }
總結
- 實例化對象時會隱式調用構造函數。
- 創建構造函數的兩條規則是:構造函數的名稱應與類相同。Java 構造函數不能有返回類型。
- 如果一個類沒有構造函數,Java 編譯器會在運行時自動創建一個預設構造函數。預設構造函數使用預設值初始化實例變數。例如 int 變數將被初始化為 0
- 構造函數類型:
- 無參構造器 - 不接受任何參數的構造函數參數化構造函數
- 接受參數的構造器 - 接受參數的構造函數
- 預設構造器 - 如果沒有明確定義,Java 編譯器會自動創建一個構造函數。
- 構造函數不能被 abstract、static 或 final 修飾
編譯器會報如下錯誤:
Illegal modifier for the constructor in type ReLearnConstructor; only public, protected & private are permitted
- 構造函數可以重載但不能被覆蓋