深入剖析 Java 構(gòu)造器調(diào)用及類的初始化順序
Java 中的構(gòu)造函數(shù)或稱為構(gòu)造器,其實(shí)就是一段代碼,是在創(chuàng)建類對(duì)象的實(shí)例并為該對(duì)象分配內(nèi)存時(shí)調(diào)用該代碼塊。它是一種用于初始化對(duì)象的特殊方法。在聲明構(gòu)造函數(shù)時(shí)使用訪問(wèn)修飾符也是允許的。
掌握構(gòu)造函數(shù)是有效學(xué)習(xí) Java 的重要組成部分。因此,本篇文章就來(lái)談?wù)剟?chuàng)建Java構(gòu)造器的有關(guān)規(guī)則、應(yīng)用以及初始化情況,以全面的理解和掌握 Java 構(gòu)造函數(shù)和相關(guān)情況。
1、構(gòu)造器規(guī)則
編寫(xiě)Java構(gòu)造器必須遵循的規(guī)則有:
- Java 構(gòu)造函數(shù)不得具有顯式返回類型;
- 它不能是抽象的(abstract)、最終的(final)、靜態(tài)的(static)或同步的(synchronized);
- 構(gòu)造函數(shù)名稱必須與屬于其類的名稱相同;
- 構(gòu)造器調(diào)用構(gòu)造器的第一行原則。
2、構(gòu)造器類型
Java中有兩種構(gòu)造函數(shù):
1)默認(rèn)構(gòu)造函數(shù)或無(wú)參數(shù)構(gòu)造函數(shù)
Java 默認(rèn)構(gòu)造函數(shù)沒(méi)有參數(shù)。這就是為什么它也被稱為無(wú)參數(shù)構(gòu)造函數(shù)的原因。 Java 默認(rèn)構(gòu)造函數(shù)的一般語(yǔ)法是:
- <class_name>(){
- //必要的初始化化代碼
- }
需要知道的是,如果 Java 類中沒(méi)有顯式定義構(gòu)造函數(shù),那么 Java 編譯器會(huì)自動(dòng)為該類創(chuàng)建一個(gè)默認(rèn)構(gòu)造函數(shù)。根據(jù)對(duì)象的類型,默認(rèn)構(gòu)造函數(shù)為對(duì)象提供默認(rèn)值或說(shuō)默認(rèn)初始化。
使用由 javac(java編譯器) 自動(dòng)創(chuàng)建的默認(rèn)構(gòu)造函數(shù)的缺點(diǎn)就是之后程序員無(wú)法設(shè)置或改變對(duì)象屬性的初始值。例如:
- package com.learning;
- /**
- *
- * @author Solo Cui
- */
- public class ConstructorDemo {
- ConstructorDemo(){//無(wú)參構(gòu)造器
- System.out.println("成功執(zhí)行構(gòu)造器,完成對(duì)象的創(chuàng)建。");
- }
- public static void main(String[] args) {
- ConstructorDemo cd = new ConstructorDemo() ;
- }
- }
運(yùn)行輸出結(jié)果如下:
成功執(zhí)行構(gòu)造器,完成對(duì)象的創(chuàng)建。
2)參數(shù)化構(gòu)造函數(shù)
任何帶有參數(shù)(一個(gè)或以上)的 Java 構(gòu)造函數(shù)都稱為參數(shù)化構(gòu)造函數(shù)。盡管參數(shù)化構(gòu)造函數(shù)通常用于為不同的 Java 對(duì)象提供不同的值,但它也可以為不同的 Java 對(duì)象提供相同的值,比如0或null。示例如下:
- package com.learning;
- /**
- * 參數(shù)化構(gòu)造器示例
- *
- * @author Solo Cui
- */
- public class ParametersConstructor {
- int id;
- String name;
- ParametersConstructor(String n,int i ) {
- name = n;
- id = i;
- }
- void display(){
- System.out.println(""+id+" "+name);
- }
- public static void main(String args[]) {
- ParametersConstructor s1 = new ParametersConstructor("Solo",666);
- ParametersConstructor s2 = new ParametersConstructor("Cui", 999);
- s1.display();
- s2.display();
- }
- }
運(yùn)行輸出結(jié)果如下:
- 666 Solo
- 999 Cui
3、構(gòu)造器調(diào)用
構(gòu)造器除了創(chuàng)建對(duì)象時(shí)的常規(guī)調(diào)用,在同一類內(nèi),構(gòu)造器也可調(diào)用其他構(gòu)造器。這里分兩種情況,即調(diào)用父類構(gòu)造器和調(diào)用當(dāng)前子類構(gòu)造器。示例如下:
1)this形式調(diào)用
- package com.learning;
- /**
- * 多個(gè)構(gòu)造器的類,this形式調(diào)用其他構(gòu)造器
- * @author Administrator
- */
- public class MyParent {
- MyParent(){
- System.out.println("無(wú)參構(gòu)造器進(jìn)行初始化處理……");
- }
- MyParent(String hello){
- this();
- System.out.println("打個(gè)招呼:"+hello);
- }
- public static void main(String[] args) {
- new MyParent("You're Great!");
- }
- }
這里一定要注意,調(diào)用同一類內(nèi)的其他構(gòu)造器的this()必須是是構(gòu)造器內(nèi)的第一行,是否有參數(shù),則根據(jù)調(diào)用的構(gòu)造器實(shí)際情況決定。
2)super形式調(diào)用
此種調(diào)用是針對(duì)類繼承關(guān)系的子類調(diào)用父情況。代碼示例如下:
- package com.learning;
- /**
- * 繼承MyParent類,調(diào)用父類
- * @author Administrator
- */
- public class MyChild extends MyParent{
- String name ="default";
- MyChild(){
- //super():編譯器會(huì)隱式調(diào)用父類的無(wú)參構(gòu)造器
- System.out.println("My Name is "+name);
- }
- MyChild(String n){
- super("大秦帝國(guó)");//顯式調(diào)用父類構(gòu)造器
- name = n ;
- }
- void showName(){
- System.out.println("Name is "+name);
- }
- public static void main(String[] args) {
- MyChild child = new MyChild();
- child.showName();
- MyChild child2 = new MyChild("Solo");
- child2.showName();
- }
- }
請(qǐng)注意:在子類未調(diào)用父類構(gòu)造器時(shí),則編譯器會(huì)隱式的調(diào)用父類無(wú)參構(gòu)造器的,即super();若子類顯式調(diào)用父類的構(gòu)造器,則必須是在構(gòu)造器的第一行,super內(nèi)的參數(shù)根據(jù)需要傳入即可。還有一點(diǎn)需要注意,即this同類構(gòu)造器調(diào)用和super父子類調(diào)用不能同時(shí)出現(xiàn)在同一個(gè)構(gòu)造器內(nèi)。
4、構(gòu)造器重載
與 方法一樣, Java類中的構(gòu)造函數(shù)可以重載。構(gòu)造函數(shù)重載,是用相同的構(gòu)造函名但具有不同的參數(shù)列表。所有這些構(gòu)造器各自執(zhí)行不同的任務(wù)。
Java 編譯器通過(guò)列表中參數(shù)的總數(shù)及其類型來(lái)區(qū)分重載的構(gòu)造函數(shù)。以下代碼片段演示了 Java 中的構(gòu)造函數(shù)重載:
- package com.learning;
- /**
- * 構(gòu)造器重載示例
- *
- * @author Solo Cui
- */
- public class OverloadConstructor {
- int id;
- String name;
- int age;
- OverloadConstructor(int i, String n) {
- id = i;
- name = n;
- }
- OverloadConstructor(int i, String n, int a) {
- id = i;
- name = n;
- age = a;
- }
- void display(){
- System.out.println("id="+id+";name="+name+";age="+age);
- }
- public static void main(String args[]) {
- OverloadConstructor s1 = new OverloadConstructor(333, "Solo");
- OverloadConstructor s2 = new OverloadConstructor(666, "Cui", 25);
- s1.display();
- s2.display();
- }
- }
運(yùn)行輸出結(jié)果如下:
- id=333;name=Solo;age=0
- id=666;name=Cui;age=25
5、構(gòu)造器與方法
簡(jiǎn)單來(lái)講,Java 方法是一段具有特定名稱的代碼。在可訪問(wèn)的范圍內(nèi),只需使用方法名稱即可在程序中的任何時(shí)間點(diǎn)調(diào)用它。你也可以理解為對(duì)數(shù)據(jù)進(jìn)行操作,然后返回值(某特定在或void)的子程序。
Java 構(gòu)造函數(shù)是一種特殊類型的方法。兩者在許多方面相似,但并不完全相同。以下是 Java 構(gòu)造函數(shù)和 Java 方法之間一些最重要的區(qū)別:
- 調(diào)用——子類通過(guò)super()隱式調(diào)用父類構(gòu)造函數(shù),而方法必須顯式調(diào)用;同類內(nèi)有多個(gè)構(gòu)造器時(shí),某個(gè)構(gòu)造器調(diào)用另一個(gè)構(gòu)造器時(shí),使用this()形式,this后括號(hào)有無(wú)參數(shù)根據(jù)調(diào)用的構(gòu)造器參數(shù)決定;而方法調(diào)用則是通過(guò)this+點(diǎn)(.)+方法名;
- 編譯器——Java編譯器從不提供 Java 默認(rèn)方法。但是,如果 Java 類中未定義構(gòu)造函數(shù),則 Java 編譯器會(huì)提供默認(rèn)構(gòu)造函數(shù);
- 命名約定——Java構(gòu)造函數(shù)的名稱必須與類的名稱相同。但是方法的名稱可能與包含它的類的名稱相同,也可能不同
- 調(diào)用次數(shù)——Java 構(gòu)造函數(shù)只在對(duì)象創(chuàng)建期間被調(diào)用一次。而Java 方法可以根據(jù)需要多次調(diào)用;
- 返回類型——Java 方法必須具有返回類型,但對(duì)于構(gòu)造函數(shù)則不需要返回類型;
- 用法——方法用于公開(kāi) Java 對(duì)象的行為,而構(gòu)造函數(shù)用于初始化相同對(duì)象的狀態(tài)。
6、復(fù)制構(gòu)造函數(shù)
盡管 Java 中沒(méi)有提供復(fù)制構(gòu)造函數(shù),但可以將值從一個(gè) Java 對(duì)象復(fù)制到另一個(gè)對(duì)象,就像在 C++ 中使用復(fù)制構(gòu)造函數(shù)一樣。
除了使用構(gòu)造函數(shù)將值從一個(gè)對(duì)象復(fù)制到另一個(gè)對(duì)象外,還可以通過(guò)以下方式完成:
將一個(gè)對(duì)象的值分配給另一個(gè)對(duì)象;
或者
通過(guò)使用 Object 類的 clone() 方法。
以下程序演示了使用 Java 構(gòu)造函數(shù)將值從一個(gè) Java 對(duì)象復(fù)制到另一個(gè)對(duì)象:
- package com.learning;
- /**
- * 復(fù)制當(dāng)前類的對(duì)象
- *
- * @author Solo Cui
- */
- public class SimpleCopy {
- int id;
- String name;
- SimpleCopy(int i, String n) {
- id = i;
- name = n;
- }
- SimpleCopy(SimpleCopy s) {
- id = s.id;
- name = s.name;
- }
- void show() {
- System.out.println("id=" + id + ";name=" + name);
- }
- public static void main(String[] args) {
- SimpleCopy s1 = new SimpleCopy(138, "Solo");
- SimpleCopy s2 = new SimpleCopy(s1);
- s1.show();
- s2.show();
- }
- }
7、類的初始化
在Java中類初始化可以分為兩類,即靜態(tài)初始化和非靜態(tài)初始化。當(dāng)創(chuàng)建java對(duì)象時(shí),程序總是依次調(diào)用每個(gè)父類的非靜態(tài)初始化塊、父類構(gòu)造器(總是從Object開(kāi)始——Java中的始祖類)執(zhí)行初始化,最后再調(diào)用當(dāng)前(子)類的非靜態(tài)初始化塊、構(gòu)造器執(zhí)行初始化。通過(guò)示例演示如下,下面我們來(lái)開(kāi)看其初始化的順序:
1)父類XParent:
- package com.learning;
- /**
- * 類初始化:父類
- *
- * @author Administrator
- */
- public class XParent {
- static {
- System.out.println("XParent:父類【靜態(tài)】初始化塊");
- }
- {
- System.out.println("XParent:父類【非靜態(tài)】初始化塊");
- }
- public XParent() {
- System.out.println("XParent:父類無(wú)參構(gòu)造器");
- }
- public XParent(String name) {
- System.out.println("XParent:父類含參構(gòu)造器:name=" + name);
- }
- }
2)子類XChild:
- package com.learning;
- /**
- * 子類XChild初始化
- *
- * @author Solo cui
- */
- public class XChild extends XParent {
- static {
- System.out.println("XChild:子類靜態(tài)初始化塊");
- }
- {
- System.out.println("XChild:子類非靜態(tài)初始化塊");
- }
- public XChild() {
- this("Solo");
- System.out.println("XChild:子類無(wú)參構(gòu)造器");
- }
- public XChild(String name) {
- super(name);
- System.out.println("XChild:子類含參構(gòu)造器: name=" + name);
- }
- public static void main(String[] args) {
- XChild c1 = new XChild("Tomy");
- XChild c2 = new XChild();
- }
- }
運(yùn)行子類輸出結(jié)果為:
- XParent:父類【靜態(tài)】初始化塊
- XChild:子類靜態(tài)初始化塊
- XParent:父類【非靜態(tài)】初始化塊
- XParent:父類含參構(gòu)造器:name=Tomy
- XChild:子類非靜態(tài)初始化塊
- XChild:子類含參構(gòu)造器: name=Tomy
- XParent:父類【非靜態(tài)】初始化塊
- XParent:父類含參構(gòu)造器:name=Solo
- XChild:子類非靜態(tài)初始化塊
- XChild:子類含參構(gòu)造器: name=Solo
- XChild:子類無(wú)參構(gòu)造器
所以,無(wú)論新實(shí)例化多少個(gè)對(duì)象,該類的所有父類以及自身的靜態(tài)初始化塊只執(zhí)行一次,而且是最先執(zhí)行的初始化,稱作類的初始化。之后的初始化會(huì)依次執(zhí)行父類的非靜態(tài)初始化塊、父類的構(gòu)造器和子類的非靜態(tài)初始化塊、子類的構(gòu)造器來(lái)完成初始化稱為對(duì)象初始化;在子類的構(gòu)造器中可以通過(guò)super來(lái)顯式調(diào)用父類的構(gòu)造器,可以通過(guò)this來(lái)調(diào)用該類重載的其他構(gòu)造器,而具體調(diào)用哪個(gè)構(gòu)造器決定于調(diào)用時(shí)的參數(shù)類型。
8、關(guān)于構(gòu)造器的FAQs
以下是一些關(guān)于 Java 構(gòu)造函數(shù)的最常見(jiàn)問(wèn)題。
問(wèn):構(gòu)造函數(shù)是否有返回值?
答:雖然不能在 Java 構(gòu)造函數(shù)中使用返回類型,但它確實(shí)返回一個(gè)值。 Java 構(gòu)造函數(shù)返回當(dāng)前類實(shí)例的引用。
問(wèn):Java 中的構(gòu)造函數(shù)鏈?zhǔn)鞘裁?
答:構(gòu)造函數(shù)鏈接是在 Java 編程語(yǔ)言中從其他構(gòu)造函數(shù)調(diào)用構(gòu)造函數(shù)的技術(shù)。 this() 方法用于調(diào)用同一個(gè)類構(gòu)造函數(shù),而 super() 方法用于調(diào)用超類構(gòu)造函數(shù)。
問(wèn):說(shuō)說(shuō)在構(gòu)造器調(diào)用構(gòu)造器時(shí),this和super的區(qū)別
答:super和this的調(diào)用只能在構(gòu)造器中,而且都必須作為構(gòu)造器中的第一行,因此super和this不會(huì)同時(shí)出現(xiàn)在同一個(gè)構(gòu)造器中。
問(wèn):在Java中可以從超類構(gòu)造函數(shù)中調(diào)用子類構(gòu)造函數(shù)嗎?
答:不行。
問(wèn):Java 有析構(gòu)函數(shù)嗎?
答:Java 沒(méi)有析構(gòu)函數(shù),因?yàn)樗抢曰厥照Z(yǔ)言。在 Java 中無(wú)法預(yù)測(cè)對(duì)象何時(shí)會(huì)被銷毀。
問(wèn):Java 構(gòu)造函數(shù)可以執(zhí)行除初始化之外的哪些任務(wù)?
答:Java 構(gòu)造函數(shù)可以執(zhí)行任何可以使用方法執(zhí)行的操作。使用 Java 構(gòu)造函數(shù)執(zhí)行的一些最流行的任務(wù)是:
調(diào)用方法;
對(duì)象創(chuàng)建;
開(kāi)始一個(gè)線程等。
問(wèn):Java 中何時(shí)需要構(gòu)造函數(shù)重載?
A:構(gòu)造函數(shù)重載在 Java 中通常用于需要以多種不同方式初始化 Java 對(duì)象的情況。
問(wèn):如果為 Java 構(gòu)造函數(shù)添加返回類型會(huì)發(fā)生什么?
答:具有返回類型的 Java 構(gòu)造函數(shù)將被視為典型的 Java 方法。
最后
這就是 Java 構(gòu)造函數(shù)的全部?jī)?nèi)容。掌握好如何有效地使用構(gòu)造函數(shù)是Java編程的必備核心技能之一。根據(jù)具體學(xué)習(xí)情況多加練習(xí)和酌情使用吧。






















