【本節目標】
1. 掌握方法的定義以及使用
2. 掌握方法傳參
3. 掌握方法重載
4. 掌握遞歸
目錄
1.方法概念及使用
1.1什么是方法(method)
1.2 方法定義
1.3 方法調用的執行過程
1.4 實參和形參的關系
2. 方法重載
2.1 為什么需要方法重載
2.2 方法重載概念
3. 遞歸
3.1 生活中的故事
3.2 遞歸的概念
3.3 遞歸執行過程分析
3.4 遞歸練習
1.方法概念及使用
1.1什么是方法(method)
方法就是一個代碼片段. 類似于 C 語言中的 "函數"。方法存在的意義(不要背, 重在體會):
1. 是能夠模塊化的組織代碼(當代碼規模比較復雜的時候).
2. 做到代碼被重復使用, 一份代碼可以在多個位置使用.
3. 讓代碼更好理解更簡單.
4. 直接調用現有方法開發, 不必重復造輪子.
1.2 方法定義
方法語法格式
?比如:實現一個兩個整數相加的方法
public static int add(int x,int y){return x+y;
}
寫一個方法,檢測一個年份是否為閏年?
public static boolean isLeapYear(int year) {if ((year % 3 == 0 && year % 100 != 0) || (year % 400 == 0)) {return true;}return false;
}
?【注意事項】
1. 修飾符:現階段直接使用public static 固定搭配
2. 返回值類型:如果方法有返回值,返回值類型必須要與返回的實體類型一致,如果沒有返回值,必須寫成 void
3. 方法名字:采用小駝峰命名
4. 參數列表:如果方法沒有參數,()中什么都不寫,如果有參數,需指定參數類型,多個參數之間使用逗號隔開
5. 方法體:方法內部要執行的語句
6. 在java當中,方法必須寫在類當中
7. 在java當中,方法不能嵌套定義
8. 在java當中,沒有方法聲明一說
1.3 方法調用的執行過程
【方法調用過程】
調用方法--->傳遞參數--->找到方法地址--->執行被調方法的方法體--->被調方法結束返回--->回到主調方法繼續往下執行
?【注意事項】
- 定義方法的時候, 不會執行方法的代碼. 只有調用的時候才會執行.
- 一個方法可以被多次調用
代碼示例: 計算 1! + 2! + 3! + 4! + 5!
public class Test {//求n的階乘public static int fac(int n) {int ret = 1;for (int i = 1; i <= n; i++) {ret *= i;}return ret;}//求階乘和public static int facSum(int k){int sum = 0;for (int i = 1; i <= k; i++) {sum+=fac(i);}return sum;}public static void main(String[] args) {int ret = facSum(5);System.out.println(ret);}
}
1.4 實參和形參的關系
方法的形參相當于數學函數中的自變量,比如:1 + 2 + 3 + … + n的公式為sum(n) =
Java中方法的形參就相當于sum函數中的自變量n,用來接收sum函數在調用時傳遞的值的。形參的名字可以隨意取,對方法都沒有任何影響,形參只是方法在定義時需要借助的一個變量,用來保存方法在調用時傳遞過來的值。
?再比如:
注意:在Java中,實參的值永遠都是拷貝到形參中,形參和實參本質是兩個實體
代碼示例: 交換兩個整型變量
public class TestMethod {public static void main(String[] args) {int a = 10;int b = 20;swap(a, b);System.out.println("main: a = " + a + " b = " + b);}public static void swap(int x, int y) {int tmp = x;x = y;y = tmp;System.out.println("swap: x = " + x + " y = " + y);}
}
// 運行結果
swap: x = 20 y = 10
main: a = 10 b = 20
可以看到,在swap函數交換之后,形參x和y的值發生了改變,但是main方法中a和b還是交換之前的值,即沒有交 換成功。
【原因分析】
實參a和b是main方法中的兩個變量,其空間在main方法的棧(一塊特殊的內存空間)中,而形參x和y是swap方法中 的兩個變量,x和y的空間在swap方法運行時的棧中,因此:實參a和b 與 形參x和y是兩個沒有任何關聯性的變量, 在swap方法調用時,只是將實參a和b中的值拷貝了一份傳遞給了形參x和y,因此對形參x和y操作不會對實參a和b 產生任何影響。
注意:對于基礎類型來說, 形參相當于實參的拷貝. 即 傳值調用
【解決辦法】: 傳引用類型參數 (例如數組來解決這個問題)
public class TestMethod {public static void main(String[] args) {int[] arr = {10, 20};swap(arr);System.out.println("arr[0] = " + arr[0] + " arr[1] = " + arr[1]);}public static void swap(int[] arr) {int tmp = arr[0];arr[0] = arr[1];arr[1] = tmp;}
}
// 運行結果
arr[0] = 20 arr[1] = 10
2. 方法重載
2.1 為什么需要方法重載
仍然以之前的加法函數為例子:
public class Test{public static int addInt(int a, int b){return a+b;}public static void main(String[] args){int x = 10;int y = 20;int ret = addInt(x,y);System.out.println(ret);}
}
這串代碼是可以正常運行的,但是有一個局限,那就是當我們是實參不是int類型的數據時,程序就會出錯:
?由于參數類型不匹配, 所以不能直接使用現有的 add 方法.
2.2 方法重載概念
在Java中,方法重載(Method Overloading)是指在同一個類中可以存在多個同名的方法,但它們的參數列表不同。通過方法重載,可以根據傳入的不同參數來調用不同的方法實現,提供了更靈活和方便的方式來處理不同的情況。
方法重載的規則如下:
- 方法名稱必須相同。
- 方法參數列表必須不同,可以通過參數的個數、類型或順序進行區分。
方法重載與返回值類型是否相同無關
兩個方法如果僅僅只是因為返回值類型不同,是不能構成重載的
下面是一個示例來說明方法重載的概念:
public class MyClass {public static void main(String[] args) {int sum1 = addNumbers(2, 3);double sum2 = addNumbers(2.5, 3.8);int sum3 = addNumbers(1, 2, 3);System.out.println("Sum1: " + sum1);System.out.println("Sum2: " + sum2);System.out.println("Sum3: " + sum3);}public static int addNumbers(int a, int b) {return a + b;}public static double addNumbers(double a, double b) {return a + b;}public static int addNumbers(int a, int b, int c) {return a + b + c;}
}
3. 遞歸
3.1 生活中的故事
從前有坐山,山上有座廟,廟里有個老和尚給小和尚將故事,講的就是:
"從前有座山,山上有座廟,廟里有個老和尚給小和尚講故事,講的就是:
"從前有座山,山上有座廟..."
"從前有座山……" "
?上面的兩個故事有個共同的特征:自身中又包含了自己,該種思想在數學和編程中非常有用,因為有些時候,我們 遇到的問題直接并不好解決,但是發現將原問題拆分成其子問題之后,子問題與原問題有相同的解法,等子問題解 決之后,原問題就迎刃而解了。
3.2 遞歸的概念
一個方法在執行過程中調用自身, 就稱為 "遞歸".
遞歸相當于數學上的 "數學歸納法", 有一個起始條件, 然后有一個遞推公式.
例如, 我們求 N!
起始條件: N = 1 的時候, N! 為 1. 這個起始條件相當于遞歸的結束條件.
遞歸公式: 求 N! , 直接不好求, 可以把問題轉換成 N! => N * (N-1)!
遞歸的必要條件:
1. 將原問題劃分成其子問題,注意:子問題必須要與原問題的解法相同
2. 遞歸出口
代碼示例: 遞歸求 N 的階乘
3.3 遞歸執行過程分析
遞歸的程序的執行過程不太容易理解, 要想理解清楚遞歸, 必須先理解清楚 "方法的執行過程", 尤其是 "方法執行結束 之后, 回到調用位置繼續往下執行".
public class Test{public static void main(String[] args) {int n = 5;int ret = factor(n);System.out.println("ret = " + ret);}public static int factor(int n) {System.out.println("函數開始, n = " + n);if (n == 1) {System.out.println("函數結束, n = 1 ret = 1");return 1;}int ret = n * factor(n - 1);System.out.println("函數結束, n = " + n + " ret = " + ret);return ret;}
}
?執行過程圖
?程序按照序號中標識的 (1) -> (8) 的順序執行
關于 "調用棧"
方法調用的時候, 會有一個 "棧" 這樣的內存空間描述當前的調用關系. 稱為調用棧.
每一次的方法調用就稱為一個 "棧幀", 每個棧幀中包含了這次調用的參數是哪些, 返回到哪里繼續執行等信息.
后面我們借助 IDEA 很容易看到調用棧的內容.
3.4 遞歸練習
代碼示例1 按順序打印一個數字的每一位(例如 1234 打印出 1 2 3 4)
public class Test {public static void print(int n) {if (n >= 10) {print(n / 10);}System.out.print(n % 10 + " ");}public static void main(String[] args) {print(1234567);}
}
代碼示例2 遞歸求 1 + 2 + 3 + ... + 10
public class Test {public static int sum(int n) {if(n==1){return 1;}return n + sum(n-1);}public static void main(String[] args) {int ret = sum(10);System.out.println(ret);}
}
?代碼示例3 寫一個遞歸方法,輸入一個非負整數,返回組成它的數字之和. 例如,輸入 1729, 則應該返回 1+7+2+9,它的和是19
public class Test {public static int add(int n) {if (n < 10) {return n;}return n % 10 + add(n / 10);}public static void main(String[] args) {int ret = add(12345);System.out.println(ret);}
}