關(guān)于Java類的構(gòu)造方法詳解
?Java語言中,類的構(gòu)造方法是一種很特殊的方法。關(guān)于構(gòu)造方法要記憶和理解的知識點其實挺多的,下面我們就來詳細的講講構(gòu)造方法,相信看過這篇文章之后,你會對構(gòu)造方法有一個比較深刻的認識和理解。(預(yù)警:此文較長,請耐心看完!)
首先來說說構(gòu)造方法的五個特點:?
一、構(gòu)造方法的名稱必須與類的名稱相同。比如類的名稱叫A,那么它的構(gòu)造方法必須也叫A。?
二、構(gòu)造方法的前面不能聲明返回值類型,即便是void也不行。只有滿足了這兩個條件,編譯器才會認定這個方法是構(gòu)造方法。?
三、如果程序員沒有在類中定義構(gòu)造方法,那么在編譯階段,編譯器會“免費贈送”給這個類一個構(gòu)造方法,也就是說,編譯器會在編譯階段在字節(jié)碼文件中補充添加一個構(gòu)造方法。但如果程序員已經(jīng)在類中已經(jīng)定義了自己的構(gòu)造方法,則編譯器不會再為類添加構(gòu)造方法。?
四、編譯器為類“免費贈送”的這個構(gòu)造方法是一個沒有參數(shù)的構(gòu)造方法。至于說這個構(gòu)造方法里面有什么內(nèi)容,咱們一會兒再說。?
五、編譯器“免費贈送”的構(gòu)造方法與類的修飾符相同,也就是說,如果類本身的訪問修飾符是public,那么這個編譯器“免費贈送”的構(gòu)造方法的前面也會自動加上public關(guān)鍵字,同理,如果類的前面沒有訪問修飾符,那么這個構(gòu)造方法前面也不會有任何任何訪問修飾符。?
說完了構(gòu)造方法的特點,我們再來說說構(gòu)造方法的作用。很多教科書上都把構(gòu)造方法的作用說成是為了創(chuàng)建一個對象,其實這種理解是有問題的。必須承認,我們創(chuàng)建一個類的對象必須要調(diào)用構(gòu)造方法,但構(gòu)造方法的作用其實并不是為了創(chuàng)建對象,而是為了“初始化對象的內(nèi)部狀態(tài)”。“初始化對象的內(nèi)部狀態(tài)”這句話聽起來不太好理解,其實就是為了給對象的各個屬性賦初始值。我們來看一個例子:
我們定義了一個Person類,并且給Person類定義了兩個屬性,分別是String類型的name和int類型的age,并且還定義了一個printInfo()方法,用來打印這兩個屬性的值。接下來我們在main方法中創(chuàng)建兩個Person類對象
執(zhí)行程序的結(jié)果如下圖:
我們發(fā)現(xiàn),這兩個對象的name屬性和age屬性的值都是默認的那個初始值,這種初始值其實沒有太大意義。如果我們希望在創(chuàng)建對象的時候給就對象的兩個屬性賦上有意義的值,此時我們就可以把給屬性賦值的語句寫到構(gòu)造方法當中。
有了構(gòu)造方法之后,我們再次執(zhí)行main方法,得到的結(jié)果是這樣的:
從運行結(jié)果上來看,兩個對象的屬性都被賦予了有意義的值。但是問題來了:按照這樣的寫法,我們所創(chuàng)建的每個Person對象,name屬性都被賦值為“張三”,而age屬性都被賦值為20。這說明我們創(chuàng)建的對象是“千篇一律”的,并且從情理上也說不通,畢竟每個人都有屬于自己的名字和年齡,不可能每個人都叫張三年齡20歲。?
那么,如何在創(chuàng)建對象的時候,為每個對象都初始化屬于自己的真實數(shù)據(jù)呢?這時候,我們就要用有參數(shù)的構(gòu)造方法來搞定這個問題了。我們可以給構(gòu)造方法添加兩個參數(shù),通過參數(shù)把數(shù)據(jù)傳遞給對象的屬性。
當我們給構(gòu)造方法添加了兩個參數(shù)之后,卻發(fā)現(xiàn)main方法中原來本來正確的代碼出現(xiàn)了語法錯誤。
這是為什么呢?就是因為我們給Person類的構(gòu)造方法添加了參數(shù),現(xiàn)在Person類當中已經(jīng)沒有無參數(shù)的構(gòu)造方法了,既然Person類當中已經(jīng)沒有了無參數(shù)的構(gòu)造方法,那么我們在main方法中調(diào)用Person類無參數(shù)的構(gòu)造方法,肯定會報錯。?
有人會問:編譯器不是會“免費贈送”給每個類一個無參數(shù)的構(gòu)造方法嗎?那個送來的構(gòu)造方法哪去了?這里需要特別說明一下:編譯器“贈送”給類無參數(shù)構(gòu)造方法是有條件的,這個條件就是:程序員沒有為類定義構(gòu)造方法。也就是說:只有程序員沒有為類添加構(gòu)造方法的情況下,編譯器才會在編譯的時候給這個類去自動添加一個無參數(shù)的構(gòu)造方法,現(xiàn)在,程序員已經(jīng)給Person類定義了構(gòu)造方法,那么編譯器就不會再給這個類添加構(gòu)造方法了。?
好,回歸正題,現(xiàn)在我們想修改這個語法錯誤很簡單,只要在main方法中給Person類的構(gòu)造方法傳遞適當?shù)膮?shù)就可以了。
給構(gòu)造方法傳遞了參數(shù)之后,語法錯誤自然消失。再次運行程序,會得到這樣的結(jié)果。
大家可以看到,這一次,我們就能夠按照我們的意愿,創(chuàng)建出有自己個性化的對象了。通過這個例子我們可以看出:構(gòu)造方法的作用是給對象的各個屬性賦上合理的初始值,從而使得我們所創(chuàng)建的對象不再是“千篇一律”,而是“千姿百態(tài)”。?
那么,現(xiàn)在我們可以再來思考兩個問題:第一個問題:構(gòu)造方法可以像普通方法那樣實現(xiàn)重載嗎?這是完全沒有問題的,我們可以在一個類中定義多個構(gòu)造方法,只要這些構(gòu)造方法參數(shù)不同,就構(gòu)成了重載。第二個問題:在一個構(gòu)造方法中,可以調(diào)用另外一個構(gòu)造方法嗎?也沒有問題,但調(diào)用的時候,需要注意:不能像調(diào)用普通方法那樣,通過類名去調(diào)用,而是需要用一個關(guān)鍵字this。但是這種調(diào)用也有兩個條件:?
- 一、調(diào)用構(gòu)造構(gòu)造方法的語句必須放在第一行。?
- 二、兩個構(gòu)造方法不能形成相互調(diào)用關(guān)系。
為了方便表述,我們把一個類中的兩個構(gòu)造方法代稱為X和Y。如果我們在X中調(diào)用了Y,那么就不能在Y中去調(diào)用X了。否則就會形成循環(huán)依賴關(guān)系,我們來看下面的例子。
我們在一個構(gòu)造方法中調(diào)用了另一個構(gòu)造方法,調(diào)用的時候,需要用到this關(guān)鍵字,并且把調(diào)用語句寫到第一行,這樣才能順利通過編譯。?
以上我們講解的都是關(guān)于構(gòu)造方法的基本常識,在這個講解的過程中,并沒有設(shè)計到類的繼承關(guān)系。如果涉及繼承關(guān)系,構(gòu)造方法在定義和調(diào)用的過程中也有一些必須了解的知識點。?
首先必須清楚,如果我們創(chuàng)建的是一個子類的對象,那么在創(chuàng)建這個子類對象的時候,虛擬機會先調(diào)用父類的構(gòu)造方法,之后才去調(diào)用子類的構(gòu)造方法。這個順序不能錯,否則會出現(xiàn)語法錯誤。為了說明問題:我們先來給Person類添加一個無參數(shù)的構(gòu)造方法,并在構(gòu)造方法中輸出一句”父類的構(gòu)造方法”
之后我們再定義出一個Person類的子類Student,并且在子類中也定義一個無參數(shù)的構(gòu)造方法,在構(gòu)造方法中輸出一句”子類的構(gòu)造方法“
之后,我們在main方法中創(chuàng)建一個子類對象。
運行main方法,得到結(jié)果如下圖:
很多人不明白,在子類的構(gòu)造方法中,只是輸出了“子類的構(gòu)造方法”這樣一句話,控制臺上為什么同時還輸出了“父類的構(gòu)造方法”?原因就是我們剛才所說的:調(diào)用子類構(gòu)造方法的時候,會首先調(diào)用父類的構(gòu)造方法。即使程序員沒有寫代碼去調(diào)用父類的構(gòu)造方法,編譯器也會把調(diào)用父類構(gòu)造方法的語句補充添加到代碼中。?
那么,補充添加調(diào)用父類構(gòu)造方法的代碼,是如何實現(xiàn)的?這里必須先講一下子類調(diào)用父類構(gòu)造方法的語法細節(jié):?
- 一、子類調(diào)用父類構(gòu)造方法的時候,不能通過構(gòu)造方法本身的名稱來調(diào)用,必須使用super關(guān)鍵字。?
- 二、子類在它的普通方法中不能調(diào)用父類的構(gòu)造方法,只能在它自身的構(gòu)造方法中才能調(diào)用。?
- 三、子類調(diào)用父類構(gòu)造方法的語句,必須寫在自身構(gòu)造方法的第一行。?
這三條語法規(guī)則至關(guān)重要,請牢記。按照這個語法規(guī)則,編譯器看到程序員沒有在子類構(gòu)造方法中調(diào)用父類構(gòu)造方法,會按下圖所示的方式把調(diào)用父類構(gòu)造方法的語句添加到代碼中
通過上圖可以看到,如果程序員沒有在子類構(gòu)造方法中添加調(diào)用父類構(gòu)造方法的語句,編譯器會自動把那條調(diào)用語句補充進來,并且放到子類構(gòu)造方法的第一行。在文章一開始提出了一個問題:編譯器會在贈送給我們的構(gòu)造方法中添加什么內(nèi)容,此時你應(yīng)該明白了吧?就是因為在子類的構(gòu)造方法中調(diào)用了父類的構(gòu)造方法,所以我們才會在控制臺上會看到兩條輸出語句。?
現(xiàn)在又冒出一個問題:父類如果有好幾個構(gòu)造方法,編譯器會自動調(diào)用哪一個呢?這里必須明確:編譯器只會調(diào)用那個無參數(shù)的構(gòu)造方法,不會調(diào)用有參數(shù)的構(gòu)造方法。這個規(guī)則又會引發(fā)一個新的問題,那就是:如果父類中壓根就沒有無參數(shù)的構(gòu)造方法,那怎么辦?在這種情況下,編譯器就會強制子類定義一個構(gòu)造方法,并且在它的構(gòu)造方法中,通過手動調(diào)用的形式去調(diào)用父類的構(gòu)造方法,如果你不那么干,編譯器就會使出它的殺手锏:劃紅線!來看下圖:
既然無論程序員是否愿意,子類在它的構(gòu)造方法當中必須要調(diào)用父類的構(gòu)造方法,那么,通常情況下我們應(yīng)該怎樣調(diào)用父類構(gòu)造方法才算合理呢?一般來講,子類都會比父類擁有更多的屬性。就本文而言,父類(Person)有2個屬性,分別是name和age,而子類(Student)有3個屬性,分別是name、age和num。當然,子類的3個屬性當中,有2個是從父類那里繼承過來的。在創(chuàng)建一個Student對象的時候,必須對這3個屬性進行初始化。所以通常子類的構(gòu)造方法會定義3個參數(shù),這3個參數(shù)分別用來初始化子類的3個屬性。既然子類的3個屬性當中,有2個是繼承于父類的,那么就可以用父類的構(gòu)造方法去初始化那2個繼承來的屬性,而剩下的那個由子類自身所定義的屬性num,可以在子類自身構(gòu)造方法中實現(xiàn)初始化,看下圖
通常來講,子類就是這樣調(diào)用父類構(gòu)造方法的。?
最后再回答一個問題:普通方法的名稱可以與類名相同嗎?答案是:可以!在這種情況下,編譯器區(qū)分該方法是普通方法還是構(gòu)造方法,就看方法的前面有沒有聲明返回值類型。這就是為什么Java語言規(guī)定構(gòu)造方法不能有聲明返回值類型的原因。?
希望通過閱讀本文,能讓你理解構(gòu)造方法的意義和用法!
到此這篇關(guān)于關(guān)于Java類的構(gòu)造方法詳解的文章就介紹到這了,更多相關(guān)Java類的構(gòu)造方法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java 實戰(zhàn)圖書管理系統(tǒng)的實現(xiàn)流程
讀萬卷書不如行萬里路,只學(xué)書上的理論是遠遠不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SSM+jsp+mysql+maven實現(xiàn)一個圖書管理系統(tǒng),大家可以在過程中查缺補漏,提升水平2021-11-11解決idea默認帶的equals和hashcode引起的bug
這篇文章主要介紹了解決idea默認帶的equals和hashcode引起的bug,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07使用@Validated和@Valid 解決list校驗的問題
這篇文章主要介紹了使用@Validated和@Valid 解決list校驗的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10如何用idea編寫并運行第一個spark scala處理程序
詳細介紹了如何使用IntelliJ IDEA創(chuàng)建Scala項目,包括配置JDK和Scala SDK,添加Maven支持,編輯pom.xml,并創(chuàng)建及運行Scala程序,這為Scala初學(xué)者提供了一個基礎(chǔ)的項目搭建和運行指南2024-09-09