首頁 > 軟體

關於Java類的構造方法詳解

2023-01-24 14:00:24

​Java語言中,類的構造方法是一種很特殊的方法。關於構造方法要記憶和理解的知識點其實挺多的,下面我們就來詳細的講講構造方法,相信看過這篇文章之後,你會對構造方法有一個比較深刻的認識和理解。(預警:此文較長,請耐心看完!)

首先來說說構造方法的五個特點:​

一、構造方法的名稱必須與類的名稱相同。比如類的名稱叫A,那麼它的構造方法必須也叫A。​

二、構造方法的前面不能宣告返回值型別,即便是void也不行。只有滿足了這兩個條件,編譯器才會認定這個方法是構造方法。​

三、如果程式設計師沒有在類中定義構造方法,那麼在編譯階段,編譯器會“免費贈送”給這個類一個構造方法,也就是說,編譯器會在編譯階段在位元組碼檔案中補充新增一個構造方法。但如果程式設計師已經在類中已經定義了自己的構造方法,則編譯器不會再為類新增構造方法。​

四、編譯器為類“免費贈送”的這個構造方法是一個沒有引數的構造方法。至於說這個構造方法裡面有什麼內容,咱們一會兒再說。​

五、編譯器“免費贈送”的構造方法與類的修飾符相同,也就是說,如果類本身的存取修飾符是public,那麼這個編譯器“免費贈送”的構造方法的前面也會自動加上public關鍵字,同理,如果類的前面沒有存取修飾符,那麼這個構造方法前面也不會有任何任何存取修飾符。​

說完了構造方法的特點,我們再來說說構造方法的作用。很多教科書上都把構造方法的作用說成是為了建立一個物件,其實這種理解是有問題的。必須承認,我們建立一個類的物件必須要呼叫構造方法,但構造方法的作用其實並不是為了建立物件,而是為了“初始化物件的內部狀態”。“初始化物件的內部狀態”這句話聽起來不太好理解,其實就是為了給物件的各個屬性賦初始值。我們來看一個例子:

我們定義了一個Person類,並且給Person類定義了兩個屬性,分別是String型別的name和int型別的age,並且還定義了一個printInfo()方法,用來列印這兩個屬性的值。接下來我們在main方法中建立兩個Person類物件

執行程式的結果如下圖:

我們發現,這兩個物件的name屬性和age屬性的值都是預設的那個初始值,這種初始值其實沒有太大意義。如果我們希望在建立物件的時候給就物件的兩個屬性賦上有意義的值,此時我們就可以把給屬性賦值的語句寫到構造方法當中。

有了構造方法之後,我們再次執行main方法,得到的結果是這樣的:

從執行結果上來看,兩個物件的屬性都被賦予了有意義的值。但是問題來了:按照這樣的寫法,我們所建立的每個Person物件,name屬性都被賦值為“張三”,而age屬性都被賦值為20。這說明我們建立的物件是“千篇一律”的,並且從情理上也說不通,畢竟每個人都有屬於自己的名字和年齡,不可能每個人都叫張三年齡20歲。​

那麼,如何在建立物件的時候,為每個物件都初始化屬於自己的真實資料呢?這時候,我們就要用有引數的構造方法來搞定這個問題了。我們可以給構造方法新增兩個引數,通過引數把資料傳遞給物件的屬性。

當我們給構造方法新增了兩個引數之後,卻發現main方法中原來本來正確的程式碼出現了語法錯誤。

這是為什麼呢?就是因為我們給Person類的構造方法新增了引數,現在Person類當中已經沒有無引數的構造方法了,既然Person類當中已經沒有了無引數的構造方法,那麼我們在main方法中呼叫Person類無引數的構造方法,肯定會報錯。​

有人會問:編譯器不是會“免費贈送”給每個類一個無引數的構造方法嗎?那個送來的構造方法哪去了?這裡需要特別說明一下:編譯器“贈送”給類無引數構造方法是有條件的,這個條件就是:程式設計師沒有為類定義構造方法。也就是說:只有程式設計師沒有為類新增構造方法的情況下,編譯器才會在編譯的時候給這個類去自動新增一個無引數的構造方法,現在,程式設計師已經給Person類定義了構造方法,那麼編譯器就不會再給這個類新增構造方法了。​

好,迴歸正題,現在我們想修改這個語法錯誤很簡單,只要在main方法中給Person類的構造方法傳遞適當的引數就可以了。

給構造方法傳遞了引數之後,語法錯誤自然消失。再次執行程式,會得到這樣的結果。

大家可以看到,這一次,我們就能夠按照我們的意願,建立出有自己個性化的物件了。通過這個例子我們可以看出:構造方法的作用是給物件的各個屬性賦上合理的初始值,從而使得我們所建立的物件不再是“千篇一律”,而是“千姿百態”。​

那麼,現在我們可以再來思考兩個問題:第一個問題:構造方法可以像普通方法那樣實現過載嗎?這是完全沒有問題的,我們可以在一個類中定義多個構造方法,只要這些構造方法引數不同,就構成了過載。第二個問題:在一個構造方法中,可以呼叫另外一個構造方法嗎?也沒有問題,但呼叫的時候,需要注意:不能像呼叫普通方法那樣,通過類名去呼叫,而是需要用一個關鍵字this。但是這種呼叫也有兩個條件:​

  • 一、呼叫構造構造方法的語句必須放在第一行。​
  • 二、兩個構造方法不能形成相互呼叫關係。

為了方便表述,我們把一個類中的兩個構造方法代稱為X和Y。如果我們在X中呼叫了Y,那麼就不能在Y中去呼叫X了。否則就會形成迴圈依賴關係,我們來看下面的例子。

我們在一個構造方法中呼叫了另一個構造方法,呼叫的時候,需要用到this關鍵字,並且把呼叫語句寫到第一行,這樣才能順利通過編譯。​

以上我們講解的都是關於構造方法的基本常識,在這個講解的過程中,並沒有設計到類的繼承關係。如果涉及繼承關係,構造方法在定義和呼叫的過程中也有一些必須瞭解的知識點。​

首先必須清楚,如果我們建立的是一個子類的物件,那麼在建立這個子類物件的時候,虛擬機器器會先呼叫父類別的構造方法,之後才去呼叫子類的構造方法。這個順序不能錯,否則會出現語法錯誤。為了說明問題:我們先來給Person類新增一個無引數的構造方法,並在構造方法中輸出一句”父類別的構造方法”

之後我們再定義出一個Person類的子類Student,並且在子類中也定義一個無引數的構造方法,在構造方法中輸出一句”子類的構造方法“

之後,我們在main方法中建立一個子類物件。

執行main方法,得到結果如下圖:

很多人不明白,在子類的構造方法中,只是輸出了“子類的構造方法”這樣一句話,控制檯上為什麼同時還輸出了“父類別的構造方法”?原因就是我們剛才所說的:呼叫子類構造方法的時候,會首先呼叫父類別的構造方法。即使程式設計師沒有寫程式碼去呼叫父類別的構造方法,編譯器也會把呼叫父類別構造方法的語句補充新增到程式碼中。​

那麼,補充新增呼叫父類別構造方法的程式碼,是如何實現的?這裡必須先講一下子類呼叫父類別構造方法的語法細節:​

  • 一、子類呼叫父類別構造方法的時候,不能通過構造方法本身的名稱來呼叫,必須使用super關鍵字。​
  • 二、子類在它的普通方法中不能呼叫父類別的構造方法,只能在它自身的構造方法中才能呼叫。​
  • 三、子類呼叫父類別構造方法的語句,必須寫在自身構造方法的第一行。​

這三條語法規則至關重要,請牢記。按照這個語法規則,編譯器看到程式設計師沒有在子類構造方法中呼叫父類別構造方法,會按下圖所示的方式把呼叫父類別構造方法的語句新增到程式碼中

通過上圖可以看到,如果程式設計師沒有在子類構造方法中新增呼叫父類別構造方法的語句,編譯器會自動把那條呼叫語句補充進來,並且放到子類構造方法的第一行。在文章一開始提出了一個問題:編譯器會在贈送給我們的構造方法中新增什麼內容,此時你應該明白了吧?就是因為在子類的構造方法中呼叫了父類別的構造方法,所以我們才會在控制檯上會看到兩條輸出語句。​

現在又冒出一個問題:父類別如果有好幾個構造方法,編譯器會自動呼叫哪一個呢?這裡必須明確:編譯器只會呼叫那個無引數的構造方法,不會呼叫有引數的構造方法。這個規則又會引發一個新的問題,那就是:如果父類別中壓根就沒有無引數的構造方法,那怎麼辦?在這種情況下,編譯器就會強制子類定義一個構造方法,並且在它的構造方法中,通過手動呼叫的形式去呼叫父類別的構造方法,如果你不那麼幹,編譯器就會使出它的殺手鐗:劃紅線!來看下圖:

既然無論程式設計師是否願意,子類在它的構造方法當中必須要呼叫父類別的構造方法,那麼,通常情況下我們應該怎樣呼叫父類別構造方法才算合理呢?一般來講,子類都會比父類別擁有更多的屬性。就本文而言,父類別(Person)有2個屬性,分別是name和age,而子類(Student)有3個屬性,分別是name、age和num。當然,子類的3個屬性當中,有2個是從父類別那裡繼承過來的。在建立一個Student物件的時候,必須對這3個屬性進行初始化。所以通常子類的構造方法會定義3個引數,這3個引數分別用來初始化子類的3個屬性。既然子類的3個屬性當中,有2個是繼承於父類別的,那麼就可以用父類別的構造方法去初始化那2個繼承來的屬性,而剩下的那個由子類自身所定義的屬性num,可以在子類自身構造方法中實現初始化,看下圖

通常來講,子類就是這樣呼叫父類別構造方法的。​

最後再回答一個問題:普通方法的名稱可以與類名相同嗎?答案是:可以!在這種情況下,編譯器區分該方法是普通方法還是構造方法,就看方法的前面有沒有宣告返回值型別。這就是為什麼Java語言規定構造方法不能有宣告返回值型別的原因。​

希望通過閱讀本文,能讓你理解構造方法的意義和用法!

到此這篇關於關於Java類的構造方法詳解的文章就介紹到這了,更多相關Java類的構造方法內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com