當年本科剛剛接觸編程的時候,對遞歸的理解始終不夠到位。原因主要是當時老師把遞歸放在計算機組成原理這門課上講解的,與編程實踐聯繫的不是非常緊密,而且當時自己沒有對知識理解形成一個體系,所以一直以來對遞歸只停留在簡單的使用層面。前幾天,在網上聽了一位老師對遞歸知識的講解,感覺豁然開朗。
-
什麼是遞歸?
遞歸從實際代碼層面來看就是方法對自身的調用,方法本身可以傳入實參。 -
使用遞歸的意義是什麼?
遞歸有助於解決複雜問題,同時使代碼變得簡潔。這裏可能大家還不能感知使用遞歸的好處,後面我們會通過實例來對遞歸進行一個比較深入的瞭解。 -
這裏我們通過一個計算一個整數的階乘的例子來講解遞歸調用的機制和原理:
-
實例代碼:
public class RecursionDemo {
public static void main(String[] args) {
System.out.println(factorial(3));
}
/**
* compute the factorial of a integer.
* @param num
* @return
*/
public static int factorial(int num){
if(num != 1){
return factorial(num - 1) * num;
}
return 1;
}
}
- 我們一步一步來分析該代碼的執行過程,並通JVM內存圖來進行輔助理解:
- 開始方法被調用,並傳入整數4。num不等於1,就執行 factorial(num - 1) * num,此時方法再次被調用。這時我們可能就會有疑問了,其底層的執行機制是怎樣的呢?
- 此時棧中被方法用的方法有着自己的一片內存來存儲局部變量,所以num在main方法中的值爲3。
- 當factorial(num - 1) 第一次被執行,num在factorial方法棧內存中的值爲3。
- . 當factorial(num - 1)第二次被執行,num在factorial方法棧內存中的值爲2。
- 當factorial(num - 1) 第三次被執行,num在factorial方法棧內存中的值爲1。num == 1,return 1被執行,此時棧中的方法開始回調。我們可以把前面的執行步驟理解爲每調用一次factorial方法就把當時方法中的局部數據壓入棧中。當return被執行,棧中方法依次出棧,並且有代碼可以繼續被執行。
- 因爲這裏的factorial方法是個有返回值的方法,return 1被執行後,1 被返回給調用者(caller)。
- return factorial()* num 被執行, num在當前棧方法的值爲2,factorial()的返回值是1。
- return factorial()* num 被執行, num在當前棧方法的值爲3,factorial()的返回值是2。
- 最後factorial()返回6,並被打印出來。
- 從上面的遞歸調用流程我們可以來總結幾點:
- 調用一個方法時,一個新的受保護的獨立空間(棧空間) 將被創建。
- 方法的局部變量是獨立的,不會相互影響,比如n變量
- 當一個方法執行完畢,或者遇到return,就會返回,遵守誰調用,就將結果返回給誰,同時當方法執行完畢或者返回時,該方法也就執行完畢。
- 除上面幾點,我們補充兩點額外知識點:
- 如果方法中使用的是引用類型變量(比如數組),就會共享該引用類型的數據.
- 遞歸必須向退出遞歸的條件逼近,否則就是無限遞歸,出現StackOverflowError (棧內存被用盡)。