Java之路:方法
一、方法的基本定義
方法(method)用來實現類的行為。 一個方法通常是用來完成一項具體的功能(function),所以方法在C++中也稱為成員函式(member function)。
在Java中,方法定義在類中,它和類的成員屬性(資料成員)一起構建一個完整的類。
方法是對邏輯程式碼的封裝,使程式結構完整條理清晰,便於後期的維護和擴充套件。 面向物件的程式語言將這一特點進一步放大,我們可以通過對方法加以許可權修飾(如private、public、protected等)來控制方法能夠在何處被呼叫。靈活的運用方法和許可權修飾符對編碼的邏輯控制非常有幫助。
構成方法有四大要素:返回值型別、方法名稱、引數、方法體。
所有方法均在類中定義和宣告。
一般情況下,定義一個方法的語法如下所示:
修飾符 返回值型別 方法名 (引數列表)
{
//方法體
return 返回值;
}
方法包含一個方法頭(method header)和一個方法體。
方法頭包括修飾符、返回值型別、方法名和引數列表等:
修飾符(modifier):定義了該方法的訪問型別。這是可選的,它告訴編譯器以什麼呼叫該方法。
返回值型別(return type):指定了方法返回的資料型別。它可以是任意有效的型別,包括構造型別(類就是一種構造型別)。如果方法沒有返回值,則其返回型別必須是void。方法體中的返回值型別要與方法頭中定義的返回值型別一致。
方法名(method name):方法名稱的命名規則遵循Java識別符號命名規範,這裡不再贅述,有興趣的可以去識別符號命名規則與規範看看。
引數列表(parameter list):引數列表是由型別、識別符號組成的序列,每對之間用逗號分開。引數實際上是方法被呼叫時接收傳遞過來的引數值的變數。如果方法沒有引數,那麼引數表為空的,但是圓括號不能省略。引數列表可將該方法需要的一些必要的資料傳給該方法。方法名和引數列表共同構成方法簽名,一起來標識方法的身份資訊。
方法體(body):方法體中存放的是封裝在{}內部的邏輯語句,用以完成一定的功能。
二、方法中的形參與實參
形參和是實參的關係如下:
⑴ 形參變數隸屬於方法體,也就是說它們是方法的區域性變數,只當在被呼叫時才被建立,才被臨時性的分配記憶體,在呼叫結束後,立即釋放所分配的記憶體單元。也就是說,當方法呼叫返回後,就不能再使用這些形式引數。
⑵ 在呼叫方法時,實參和形參在數量上、型別上、順序上應嚴格保證一一對應的關係,否則就會出現引數型別不匹配的錯誤,從而導致呼叫方法失敗。
三、方法的過載
方法名相同但引數列表不同的方法簽名機制,稱之為方法的過載(method overload)。
在呼叫的時候,編譯器會根據引數的型別或個數不同來執行不同的方法體程式碼。
注
(1)方法名稱相同。
(2)方法的引數列表不同(引數個數、引數型別、引數順序,至少有一項不同)。
(3)方法的返回值型別和修飾符不做要求,可以相同,也可以不同。
(4)方法的簽名僅包括方法名稱和引數,因此方法過載不能根據方法的不同返回值來區分不同方法,因為返回值不屬於方法簽名的一部分。
例如,int add(int, int)和void add(int, int)的方法簽名是相同的,編譯器會“認為”這兩個方法完全相同而無法區分,故此它們無法達到過載的目的。
三、構造方法
“構造”一詞來自於英文 “Constructor”,中文常譯為“構造器”,又稱為建構函式(C++中)或構造方法(Java中)。
構造方法與普通方法的差別在於,它是專用於在構造物件時初始物件成員的,其名稱和其所屬類名相同。
類名稱 物件名稱 = new 類名稱();
(1)類名稱:表示要定義變數的型別,只是有了類之後,變數的型別是由使用者自己定義的;
(2)物件名稱:表示變數的名稱,變數的命名規範與方法相同,例如:studentName;
(3)new:是作為開闢堆記憶體的唯一方法,表示例項化物件;
(4)類名稱():這就是一個構造方法。
所謂構造方法,就是在每一個類中定義的,並且是在使用關鍵字new例項化一個新物件的時候預設呼叫的方法。
在Java程式裡,構造方法所完成的主要工作是對新建立物件的資料成員賦初值。
可將構造方法視為一種特殊的方法,其定義方式如下:
class 類名稱 {
訪問許可權 類名稱(型別1 引數1,型別2 引數2,…) {//構造方法
程式語句;
// 構造方法沒有返回值
}
}
在使用構造方法的時候需注意以下幾點:
(1)構造方法名稱和其所屬的類名必須保持一致。
(2)構造方法沒有返回值,也不可以使用void。
(3)構造方法也可以向普通方法一樣被過載。
(4)構造方法不能被static和final修飾。
(5)構造方法不能被繼承,子類使用父類的構造方法需要使用super關鍵字。
(6)構造方法的呼叫時機與普通方法有所不同。普通方法是在需要時才呼叫,而構造方法則是在建立物件時就自動“隱式”執行。因此,構造方法無需在程式中直接呼叫,而是在物件產生時自動執行一次。通常用它來對物件的資料成員進行初始化。
(7)對於一個構造方法public Book() {}和一個普通方法public void Book() {},二者的區別在於,如果構造方法上寫上void,那麼其定義的形式就與普通方法一樣了。 構造方法是在一個物件例項化的時候只調用一次的方法,而普通方法則可通過一個例項化物件呼叫多次。正是因為構造方法的特殊性,它才有特殊的語法規範。
構造方法的私有化
一個方法可根據實際需要,將其設定為不同的訪問許可權——public(公有訪問)、private(私有訪問)或預設訪問(即方法前沒有修飾符)。
同樣,構造方法也有public與private之分。如果構造方法被設為private,那麼其他類中就無法呼叫該構造方法。 換句話說,在本類之外,就不能通過new關鍵字呼叫該構造方法建立該類的例項化物件。
public class Demo {
private Demo() {} // 私有構造
public static void main(String[] args) {
Demo dd = new Demo(); // 可以在本類中例項化
}
}
class Test {
Demo d = new Demo(); // 錯誤,在本類之外,不能通過new關鍵字呼叫私有構造方法建立該類的例項化物件。
}
單態設計模式
例項化物件需要呼叫構造方法,但如果將構造方法使用private藏起來,則外部肯定無法直接呼叫,那麼例項化該類物件就只能有一種途徑——在該類內部用new關鍵字建立該類的例項。通過這個方式,我們就可以確保一個類只能建立一個例項化物件。在軟體工程中,這種設計模式被稱之為單態設計模式(Singleton Design Pattern)。
Java中的構造方法私有化就是為這種軟體設計模式而服務的,
public class TestSingleDemo {
public static void main(String[]args) {
/*宣告一個Person類的物件
*雖私有化Person類的構造方法,
*但可通過Person類公有介面獲得Person例項化物件
*/
Person p; /* 宣告一個Person類的物件p,但並未例項化,
* 僅是在棧記憶體中為物件引用p分配了空間儲存,
* p所指向的物件並不存在。
*/
p=Person.getPerson();
System.out.println("姓名:"+p.name);
}
}
class Person
{
String name;
/*
* 在類聲明瞭一個Person類的例項化物件,
* 此物件是在Person類的內部例項化,所以可以呼叫私有構造方法。
* 此物件被標識為static型別,表示為一靜態屬性。
* 另外,在宣告Person物件的時候還加上了一個final關鍵字,
* 此關鍵字表示物件PERSON不能被重新例項化,表示該物件不可更改。
*/
private static final Person PERSON=new Person();
private Person()
{
name="kehr";
}
public static Person getPerson()
{
return PERSON;
}
}
【結果】
【結果分析】
由於Person類構造方法是private,所以如 Person p = new Person () ;已經不再可行了。只能通過“p = Person.getPerson();”來獲得例項。
而由於這個例項PERSON是static的,全域性共享一個,所以無論在Person類的外部宣告多少個物件,使用多少個“p = Person.getPerson();”,最終得到的例項都是同一個。
也就是說,此類只能產生一個例項物件。 這種做法就是上面提到的單態設計模式。
所謂設計模式也就是在大量的實踐中總結和理論化之後優選的程式碼結構、程式設計風格以及解決問題的思考方式。
四、遞迴方法
在程式設計領域,遞迴是指函式(或方法)直接或間接呼叫自身的一種操作。
遞迴呼叫能夠大大減少程式碼量,將原本複雜的問題簡化成一個簡單的基礎操作來完成。在編碼過程中“遞迴呼叫”是一個非常實用的技巧。
在編寫遞迴程式碼時,讀者要特別注意,遞迴程式一定要有結束條件,這又被稱作遞迴出口。
如果一個遞迴函式缺少遞迴出口,執行時就會陷入死迴圈,其後果非常嚴重。
遞迴出口可用if語句來控制,在滿足某種條件時繼續遞迴呼叫自身,否則就不再繼續。
缺點:使用遞迴實現需要函式壓棧和彈棧的操作,所以程式的執行速度比不用遞迴實現要慢的多。如果操作不慎還極易出現死迴圈,讀者編寫程式碼過程中需要多加註意,一定要設定遞迴操作的終止條件。