JavaSE回顧(三):方法的基本用法以及重載和遞歸

一、方法的基本用法

方法就是指將一段代碼封裝在一個結構體之中,實現某種功能,可以被重複調用的代碼塊。

方法的意義

  • 模塊化的組織代碼,使程序變得更簡短而清晰。
  • 有利於程序維護。
  • 可以提高程序開發的效率。
  • 提高了代碼的重用性。

基本語法

// 方法定義
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;
} 

此時程序的執行效率大大提高了 。

遞歸小結:

  • 優點:代碼簡潔、清晰,並且容易驗證正確性。
  • 缺點:它的運行需要較多次數的函數調用,如果調用層數比較深,需要增加額外的堆棧處理,比如參數傳遞需要壓棧等操作,會對執行效率有很大影響。但是,對於某些問題天然就是使用遞歸方式定義的(例如斐波那契數列, 二叉樹等), 此時使用遞歸來解就很容易,使用非遞歸代碼會很難看。有些問題使用遞歸和使用非遞歸(循環)都可以解決時. 那麼此時更推薦使用循環, 因爲相比於遞歸, 非遞歸程序更加高效。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章