一、方法的基本用法
方法就是指將一段代碼封裝在一個結構體之中,實現某種功能,可以被重複調用的代碼塊。
方法的意義
- 模塊化的組織代碼,使程序變得更簡短而清晰。
- 有利於程序維護。
- 可以提高程序開發的效率。
- 提高了代碼的重用性。
基本語法
// 方法定義
public static 方法返回值 方法名稱([參數類型 形參 ...]){
方法體代碼;
[return 返回值];
}
// 方法調用
返回值變量 = 方法名稱 (實參...);
方法調用的執行過程
1.定義方法的時候, 不會執行方法的代碼. 只有調用的時候纔會執行;
2.當方法被調用的時候, 會將實參賦值給形參;
3.參數傳遞完畢後, 就會執行到方法體代碼;
4.當方法執行完畢之後(遇到 return 語句), 就執行完畢, 回到方法調用位置繼續往下執行;
5.一個方法可以被多次調用。
方法中實參和形參的關係
形參:就是形式參數,用於定義方法的時候使用的參數,是用來接收調用者傳遞的參數的。形參只有在方法被調用的時候,虛擬機纔會分配內存單元,在方法調用結束之後便會釋放所分配的內存單元。
實參:就是實際參數,用於調用時傳遞給方法的參數。實參在傳遞給別的方法之前是要被預先賦值的。
代碼示例1: 交換兩個整型變量
class Test {
public static void main(String[] args) {
int a = 10;
int b = 20;
swap(a, b);
System.out.println("a = " + a + " b = " + b);
}
public static void swap(int x, int y) {
int tmp = x;
x = y;
y = tmp;
}
}
// 運行結果
a = 10 b = 20
上面的swap方法是交換兩個整數的值,但是在主函數中調用後a和b的值並沒有發生改變,爲什麼呢?
因爲對於基礎類型來說, 形參相當於實參的拷貝,即 傳值調用。
代碼示例2:交換數組中兩個整數的值
class Test {
public static void main(String[] args) {
int[] arr = {10, 20};
swap(arr);
System.out.println("a = " + arr[0] + " b = " + arr[1]);
}
public static void swap(int[] arr) {
int tmp = arr[0];
arr[0] = arr[1];
arr[1] = tmp;
}
}
// 運行結果
a = 20 b = 10
在這段代碼中可以發現arr[0]和arr[1]的值發生了交換,爲什麼這段代碼可以發生交換呢?
因爲在Java中數組元素的變量名保存的數組中元素的地址,交換實際上是改變了指針的指向,所以看起來是完成了數值的交換。
結論:
- 當方法中參數是基本數據類型時實參和形參之間是按值傳遞。
- 方法中參數是引用類型 時實參和形參按引用傳遞。
二、方法的重載
Java中方法的重載,是指若同一個類中,兩個或兩個以上方法名相同,但是調用的方法參數的個數、順序或類型不同的方法,則稱爲方法的重載。
重載的規則
在同一個類中:
- 方法名相同。
- 方法的參數不同(參數個數或者參數類型,順序不同)。
- 方法的返回值類型不影響重載,相同或者不同都可以。
重載示例1
class Test {
public static void main(String[] args) {
int a = 10;
int b = 20;
int ret = add(a, b);
System.out.println("ret = " + ret);
double a2 = 10.5;
double b2 = 20.5;
double ret2 = add(a2, b2);
System.out.println("ret2 = " + ret2);
double a3 = 10.5;
double b3 = 10.5;
double c3 = 20.5;
double ret3 = add(a3, b3, c3);
System.out.println("ret3 = " + ret3);
}
//第一個add方法,兩個int類型相加
public static int add(int x, int y) {
return x + y;
}
//第二個add方法,兩個double類型相加
public static double add(double x, double y) {
return x + y;
}
//第三個add方法,三個add類型相加
public static double add(double x, double y, double z) {
return x + y + z;
}
}
方法的名字都叫 add. 但是有的 add 是計算 int 相加, 有的是 double 相加; 有的計算兩個數字相加, 有的是計算三個數字相加。調用時會根據傳的參數的不同,自動調用合適的方法。
同一個方法名字, 提供不同版本的實現, 稱爲方法重載。
重載示例2
class Test {
public static void main(String[] args) {
int a = 10;
int b = 20;
int ret = add(a, b);
System.out.println("ret = " + ret);
}
public static int add(int x, int y) {
return x + y;
}
public static double add(int x, int y) {
return x + y;
}
}
// 編譯出錯
Test.java:13: 錯誤: 已在類 Test中定義了方法 add(int,int)
public static double add(int x, int y) {
^
1 個錯誤
當兩個方法的名字相同, 參數也相同, 但是返回值不同的時候, 不構成重載。
關於方法重載和重寫的區別:
1.方法的重載和重寫都是實現多態的方式,區別在於前者實現的是編譯時的多態性,而後者實現的是運行時的多態性;
2.重載發生在一個類中,重寫發生在子類與父類之間;
3.同名的方法如果有不同的參數列表(參數類型不同、參數個數不同或者順序不同)則視爲重載;重寫要求子類被重寫方法與父類被重寫方法有相同的參數列表,有兼容的返回類型,比父類被重寫方法更好訪問,不能比父類被重寫方法聲明更多的異常(里氏代換原則)。
4.重載對返回類型沒有特殊的要求,不能根據返回類型進行區分。
三、方法的遞歸
如果一個方法在執行過程中調用了自己本身,就稱作“遞歸”。
使用遞歸時的注意事項:
1.一定要有一個終止的條件,保證遞歸能夠停止下來,否則就會發生內存溢出。
2. 在遞歸中雖然有限定條件,但是遞歸次數不能太多,否則也會發生棧內存溢出;
3. 構造方法禁止遞歸,因爲構造方法是創建對象使用的,不能讓對象一直創建下去把。
經典遞歸代碼:求斐波那契數列的第 N 項
斐波那契數列爲1、1、2、3、5、8、13、21、34……此數列從第3項開始,每一項都等於前兩項之和,遞推公式爲F(n)=F(n-1)+F(n-2),n≥3,F(1)=1,F(2)=1。
代碼:
public static int fib(int n) {
if (n == 1 || n == 2) {
return 1;
}
return fib(n - 1) + fib(n - 2);
}
代碼很簡潔,但是有一個問題,隨着N變大,程序執行速度極慢,原因是進行了大量的重複運算。
比如可以用一段代碼來測一下:
class Test {
public static int count = 0;
public static void main(String[] args) {
System.out.println(fib(40));
System.out.println(count);
}
public static int fib(int n) {
if (n == 1 || n == 2) {
return 1;
}
if (n == 3) {
count++;
}
return fib(n - 1) + fib(n - 2);
}
}
// 執行結果
102334155
39088169 // fib(3) 重複執行了 3 千萬 次
通過上面的代碼可以看到計算到第40個時,僅僅是fib(3)就被重複執行了三千萬次。可想而知運行速度有多慢。
對於上面的代碼,可以改爲使用循環的方式來求, 避免出現冗餘運算。
public static int fib(int n) {
int last2 = 1;
int last1 = 1;
int cur = 0;
for (int i = 3; i <= n; i++) {
cur = last1 + last2;
last2 = last1;
last1 = cur;
}
return cur;
}
此時程序的執行效率大大提高了 。
遞歸小結:
- 優點:代碼簡潔、清晰,並且容易驗證正確性。
- 缺點:它的運行需要較多次數的函數調用,如果調用層數比較深,需要增加額外的堆棧處理,比如參數傳遞需要壓棧等操作,會對執行效率有很大影響。但是,對於某些問題天然就是使用遞歸方式定義的(例如斐波那契數列, 二叉樹等), 此時使用遞歸來解就很容易,使用非遞歸代碼會很難看。有些問題使用遞歸和使用非遞歸(循環)都可以解決時. 那麼此時更推薦使用循環, 因爲相比於遞歸, 非遞歸程序更加高效。