
💜寫在前邊💜
前言
咱們在C語言里肯定都學過函式吧,相信大家對函式的理解已經很深刻了,因為函式在C里用的會很多,特別是做專案的時候,會分模塊來寫,Java里同樣為大家提供了“函式”,只不過叫法不一樣,Java里叫【方法】,接下來請往下看
【?Java】C語言里叫【函式】,Java里叫【方法】
- 💜寫在前邊💜
- 🌟方法的基本用法
- 🌙什么是方法(method)
- 🌙方法定義語法
- 🌙方法呼叫的執行程序
- 🌙實參和形參的關系(敲重點)
- 🌙無回傳值的方法
- 🌟方法的多載
- 🌙多載要解決的問題·
- 🌙使用多載
- 🌙多載的規則
- 🌟方法遞回
- 🌙遞回的概念
- 🌙遞回執行程序分析
- 🌙遞回練習
- 🌙遞回總結
🌟方法的基本用法
🌙什么是方法(method)
方法就是一個代碼片段. 類似于 C 語言中的 “函式”.
方法存在的意義:
1. 是能夠模塊化的組織代碼(當代碼規模比較復雜的時候).
2. 做到代碼被重復使用, 一份代碼可以在多個位置使用.
3. 讓代碼更好理解更簡單.
4. 直接呼叫現有方法開發, 不必重復造輪子.
🌙方法定義語法
基本語法
// 方法定義 public static 方法回傳值 方法名稱([引數型別 形參 ...]){ 方法體代碼; [return 回傳值]; } // 方法呼叫 回傳值變數 = 方法名稱(實參...);
代碼示例: 實作一個方法實作兩個整數相加
class Test { public static void main(String[] args) { int a = 3; int b = 2; // 方法的呼叫 int ret = add(a, b); System.out.println("ret = " + ret); } ----------------------------------------------- // 方法的定義 public static int add(int x, int y) { return x + y; } } ----------------------------------------------- // 執行結果 ret = 5
注意事項
1. public 和 static 兩個關鍵字在此處具有特定含義, 暫時不討論, 后面會詳細介紹.
2. 方法定義時, 引數可以沒有. 每個引數要指定型別
3. 方法定義時, 回傳值也可以沒有, 如果沒有回傳值, 則回傳值型別應寫成 void(類似C語言)
4. 方法定義時的引數稱為 “形參”, 方法呼叫時的引數稱為 “實參”.
5. 方法的定義必須在類之中, 代碼書寫在呼叫位置的上方或者下方均可.
6. Java 中 沒有 “函式宣告” 這樣的概念(個人覺得是很爽的,在C里邊經常被函式宣告的問題惡心到).
🌙方法呼叫的執行程序
基本規則
- 定義方法的時候, 不會執行方法的代碼. 只有呼叫的時候才會執行.
- 當方法被呼叫的時候, 會將實參賦值給形參.
- 引數傳遞完畢后, 就會執行到方法體代碼.
- 當方法執行完畢之后(遇到 return 陳述句), 就執行完畢, 回到方法呼叫位置繼續往下執行.
- 一個方法可以被多次呼叫.
代碼示例1 計算兩個整數相加
class Test { public static void main(String[] args) { int a = 10; int b = 20; System.out.println("第一次呼叫方法之前"); int ret = add(a, b); System.out.println("第一次呼叫方法之后"); System.out.println("ret = " + ret); System.out.println("第二次呼叫方法之前"); ret = add(30, 50); System.out.println("第二次呼叫方法之后"); System.out.println("ret = " + ret); } public static int add(int x, int y) { System.out.println("呼叫方法中 x = " + x + " y = " + y); return x + y; } } // 執行結果 一次呼叫方法之前 呼叫方法中 x = 10 y = 20 第一次呼叫方法之后 ret = 30 第二次呼叫方法之前 呼叫方法中 x = 30 y = 50 第二次呼叫方法之后 ret = 80
代碼示例2: 計算 1! + 2! + 3! + 4! + 5!`
class Test { public static void main(String[] args) { int sum = 0; for (int i = 1; i <= 5; i++) { sum += factor(i); } System.out.println("sum = " + sum); } public static int factor(int n) { System.out.println("計算 n 的階乘中! n = " + n); int result = 1; for (int i = 1; i <= n; i++) { result *= i; } return result; } } // 執行結果 計算 n 的階乘中! n = 1 計算 n 的階乘中! n = 2 計算 n 的階乘中! n = 3 計算 n 的階乘中! n = 4 計算 n 的階乘中! n = 5 sum = 153
使用方法, 避免使用二重回圈, 讓代碼更簡單清晰
🌙實參和形參的關系(敲重點)
代碼示例: 交換兩個整型變數
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原因分析
剛才的代碼, 沒有完成資料的交換.
對于基礎型別來說, 形參相當于實參的拷貝. 即 傳值呼叫int a = 10; int b = 20; int x = a; int y = b; int tmp = x; x = y; y = tmp;可以看到, 對 x 和 y 的修改, 不影響 a 和 b.
解決辦法:由于在Java里,是拿不到堆疊上的地址的!
所以用傳參考型別引數 (例如陣列來解決這個問題)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
🌙無回傳值的方法
方法的回傳值是可選的. 有些時候可以沒有的.
代碼示例
class Test { public static void main(String[] args) { int a = 10; int b = 20; print(a, b); } public static void print(int x, int y) { System.out.println("x = " + x + " y = " + y); } }另外, 如剛才的交換兩個整數的方法, 就是沒有回傳值的.
🌟方法的多載
有些時候我們需要用一個函式同時兼容多種引數的情況, 我們就可以使用到方法多載.
🌙多載要解決的問題·
代碼示例
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); } public static int add(int x, int y) { return x + y; } } // 編譯出錯 Test.java:13: 錯誤: 不兼容的型別: 從double轉換到int可能會有損失 double ret2 = add(a2, b2);由于引數型別不匹配, 所以不能直接使用現有的 add 方法
修改后的代碼
class Test { public static void main(String[] args) { int a = 10; int b = 20; int ret = addInt(a, b); System.out.println("ret = " + ret); double a2 = 10.5; double b2 = 20.5; double ret2 = addDouble(a2, b2); System.out.println("ret2 = " + ret2); } public static int addInt(int x, int y) { return x + y; } public static double addDouble(double x, double y) { return x + y; } }這樣的寫法是對的(例如 Go 語言就是這么做的)
但是 Java 認為 addInt 這樣的名字不友好, 不如直接就叫 add
🌙使用多載
代碼示例
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); } public static int add(int x, int y) { return x + y; } public static double add(double x, double y) { return x + y; } public static double add(double x, double y, double z) { return x + y + z; } }
方法的名字都叫
add. 但是有的add是計算int相加, 有的是double相加; 有的計算兩個數字相加, 有的是計算三個數字相加.
同一個方法名字, 提供不同版本的實作, 稱為 方法多載
🌙多載的規則
針對同一個類或者繼承關系:
- 方法名相同
- 方法的引數不同(引數個數或者引數型別)
- 方法的回傳值型別不影響多載.
代碼示例
public static int add(int x, int y) { return x + y; } public static double add(double x, double y) { return x + y; } public static double add(double x, double y, double z) { return x + y + z; } } 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)設定出口,如果沒有出口那么程式會一直遞回下去,
例如, 我們求 N!
起始條件: N = 1 的時候, N! 為 1. 這個起始條件相當于遞回的結束條件.
遞回公式: 求 N! , 直接不好求, 可以把問題轉換成 N! => N * (N-1)!
代碼示例: 遞回求 N 的階乘
public static void main(String[] args) { int n = 5; int ret = factor(n); System.out.println("ret = " + ret); } public static int factor(int n) { if (n == 1) { return 1; } return n * factor(n - 1); 這句代碼是關鍵所在 factor 函式呼叫函式自身 } // 執行結果 ret = 120這個代碼是關鍵所在就是 【factor 函式呼叫函式自身】接下來請看詳細分析
🌙遞回執行程序分析
遞回的程式的執行程序不太容易理解, 要想理解清楚遞回, 必須先理解清楚 “方法的執行程序”, 尤其是 “方法執行結束之后, 回到呼叫位置繼續往下執行”.
代碼示例: 遞回求 N 的階乘, 步驟列印
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; } -------// 設定求5的階乘,執行結果如下------------------------ 函式開始, n = 5 //呼叫自己 函式開始, n = 4 //呼叫自己 函式開始, n = 3 //呼叫自己 函式開始, n = 2 //呼叫自己 函式開始, n = 1 //到達停止條件,回傳 1 -------//到達終止條件,開始一層一層回傳值--------------------- 函式結束, n = 1 ret = 1 函式結束, n = 2 ret = 2 函式結束, n = 3 ret = 6 函式結束, n = 4 ret = 24 函式結束, n = 5 ret = 120 ret = 120執行程序圖解
🌙遞回練習
代碼示例1
按順序列印一個數字的每一位(例如 1234 列印出 1 2 3 4)public static void print(int num) { if (num > 9) { print(num / 10); } System.out.println(num % 10); }
代碼示例2
遞回求 1 + 2 + 3 + … + 10public static int sum(int num) { if (num == 1) { return 1; } return num + sum(num - 1); }
代碼示例3
寫一個遞回方法,輸入一個非負整數,回傳組成它的數字之和. 例如,輸入 1729, 則應該回傳1+7+2+9,它的和是19public static int sum(int num) { if (num < 10) { return num; } return num % 10 + sum(num / 10); }
代碼示例4 (敲重點!!敲重點!!敲重點!!)
求斐波那契數列的第n項【斐波那契數列是一組第一位和第二位為1,從第三位開始,后一位是前兩位和的一組遞增數列,像這樣的:0、1、1、2、3、5、8、13、21、34、55…】
方法一:回圈
這種解法是比較高效的一種解法
時間復雜度O(n),空間復雜度O(1)import java.util.Scanner; public class 斐波那契數 {//時間復雜度O(n),空間復雜度O(1) public static void main(String[] args) { Scanner scn = new Scanner(System.in); int n = scn.nextInt(); int a = 0; int b = 1; int tmp = 0; if (n==1){ System.out.println(0); } else if (n==2){ System.out.println(1); } else if (n>2) { for ( int i = 3; i <= n; i++ ) { tmp = a+b; a = b; b = tmp; } System.out.println(b); } } }
方法二:遞回
此解法思維方式非常簡單
但是時間復雜度特別高,時間復雜度O(2^n),空間復雜度O(n)
不建議采用這種方法,import java.util.Scanner; public class 遞回求斐波那契數列 {//時間復雜度O(2^N),空間復雜度O(n) public static int count = 0; public static int Fib(int n) { if (n==1) return 0; else if (n==2||n==3) return 1; else if (n==4) count++; return Fib(n-1)+Fib(n-2); } public static void main(String[] args) { Scanner scn = new Scanner(System.in); while (scn.hasNextInt()) { int n = scn.nextInt(); System.out.println("第"+n+"個斐波那契數是"+Fib(n)); System.out.println("遞回了"+count+"次"); count = 0; } } }可以看到當求第40個斐波那契數時,重復次數高達24157817次
方法三:高效遞回
如果說前一種遞回解法是由后向前計算,那么這種解法就是由前向后計算了,
這種遞回方式屬于尾遞回,因此在進行遞回時函式只會使用第一次壓堆疊所開辟的堆疊空間,在一個堆疊空間內回圈,而不會開辟別的堆疊空間
所以這種方式時間復雜度為O(n),空間復雜度為O(1)是一種非常高效的遞回方式,import java.util.Scanner; public class 高效遞回求斐波那契數列 { //時間復雜度O(n),空間復雜度O(1) public static int Fib(int first,int sec,int n) { if (n==1) return first; else return Fib(sec,first+sec,n-1); } public static void main(String[] args) { Scanner scn = new Scanner(System.in); while (scn.hasNextInt()) { int n = scn.nextInt(); System.out.println("第"+n+"個斐波那契數是"+Fib(0,1,n)); } } }
🌙遞回總結
遞回是一種重要的編程解決問題的方式.
有些問題天然就是使用遞回方式定義的(例如斐波那契數列, 二叉樹等), 此時使用遞回來解就很容易.
有些問題使用遞回和使用非遞回(回圈)都可以解決. 那么此時更推薦使用回圈, 相比于遞回, 非遞回程式更加高效.
?原創不易,如有錯誤,歡迎評論區留言指出,感激不盡?
? 如果覺得內容不錯,給個三連不過分吧~ ?
? 看到會回訪~ ?
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/333786.html
標籤:java
上一篇:??JavaSE系列??Java程式的封裝——Java方法多載及遞回
下一篇:Python基礎 第一篇


