第 8 章 函式 1
本章提要 8.1 前言 8.2 如何定義函式 8.3 函式的呼叫和返回 8.4 傳遞陣列 8.5 方法多載 8.6 遞迴 8.7 綜合練習 8.8 後記 2
8.1 前言 每一種高階程式語言都有提供函式 (Function)( 或稱函數 ) 的功能, 以便將經常使用到的程式功能包裝成函式的形式, 如此一來便能反覆地呼叫該函式來完成某件特定工作在高階程式語言中, 副程式 (Subroutine) 程序(Procedure) 和函式都是同義詞, 它們都代表一個具有獨立功能且可以被反覆呼叫 執行的程式片斷 3
8.1 前言 函式具有下列優點 : 將程式模組化, 可以重複地呼叫 使用, 減少程式開發的成本 簡化程式碼, 讓程式看起來較為簡潔, 容易閱讀 容易除錯, 可以降低程式維護的成本 可以充分發揮資訊隱藏 多載 改寫 (Override) 等物件導向特性 4
8.1 前言 本章將介紹的主題有 : 如何定義函式函數的呼叫和返回傳遞引數傳遞陣列方法多載遞迴 5
8.2 如何定義函式 存取方式 public: 公開的 讓這個函式可以從 main() 裡去呼叫它 private protected 6
8.2 如何定義函式 static: 用來表示這個函式為 類別函式 ( 或稱類別方法 ) 類別函式的用法規則如下 : 一般 ( 非類別 ) 函式的呼叫方式是必須先建立物件, 然後以 物件名稱. 函式名稱 ( 引數列 ) 來呼叫 類別函式除了可以採用一般函式的呼叫方式外, 也可以直接用 函式名稱 ( 引數列 ) 來呼叫, 或以 類別名稱. 函式名稱 ( 引數列 ) 來呼叫 static 函式只能存取 static 成員, 亦即類別函式只能存取類別變數和類別函式, 不能存取實例變數和實例函式 ( 參考第 9 章 9.5 節 ) 7
傳回值型別 8.2 如何定義函式 又稱函式型別, 就是指函式傳回 (return) 值的資料型別 函式若沒有傳回值則 傳回值型別 須用 void 來表示 函式名稱 參數列 函式主體 8
8.2 如何定義函式 呼叫函式的語法 呼叫函式的語法 1. 函式名稱 ( 引數 1, 引數 2,...); // 呼叫函式 2. 變數 = 函式名稱 ( 引數 1, 引數 2,...); // 呼叫函式, 並將傳回值指定給變數 9
8.3 函式的呼叫和返回 8.3.1 沒有傳遞引數的函式 8.3.2 呼叫函式時的引數傳遞 10
8.3.1 沒有傳遞引數的函式 範例程式 1 呼叫函式, 不傳遞引數, 沒有傳回值 2 // 程式名稱 :ch08_print.java 8 public static void main(string args[]) 9 { 10 System.out.println(" 呼叫 print() 函式..."); 11 print(); 12 System.out.println(" 從 print() 函式返回 "); 13 } 14 15 public static void print() // 必須宣告為 static 16 { 17 System.out.println("I love java!"); 18 } 執行結果 呼叫 print() 函式... I love java! 從 print() 函式返回 11
8.3.1 沒有傳遞引數的函式 圖 8.1: 函式的呼叫和返回 12
8.3.2 呼叫函式時的引數傳遞 傳值 (Pass By Value) 傳遞以基本資料型別宣告的引數時 將引數的值拷貝一份, 然後將這份拷貝指定給對應之參數 不會對引數有任何影響 傳參照 (Pass By Reference) 傳遞陣列或物件時 將引數陣列的起始位置傳遞給參數 若在函式的執行過程中改變了陣列的值, 那麼引數陣列的值也會被改變 13
8.3.2 呼叫函式時的引數傳遞 範例程式 2 找出兩數之大者 ( 傳值 ) 2 // 程式名稱 :ch08_max.java 10 int i = 1, j = 2; 11 12 System.out.println("max(" + i + "," + j + ")=" + max(i, j)); 13 } 14 15 public static int max(int x, int y) 16 { 執行結果 17 return (x >= y)? x : y; max(1,2)=2 18 } 14
8.3.2 呼叫函式時的引數傳遞 圖 8.2: 傳值 ( 將引數 i j 的值傳遞給參數 x y) 15
8.3.2 呼叫函式時的引數傳遞 範例程式 3 兩數相加 ( 傳值 ) 2 // 程式名稱 :ch08_add.java 10 int i = 1, j = 2, k = 0 ; 11 12 System.out.println("< 在主程式 main() 內 > i=" + i + ", j=" + j + 13 ", k=" + k); 14 k = add(i, j); // 引數 i j 15 System.out.println("< 在主程式 main() 內 > i=" + i + ", j=" + j + 16 ", k=" + k); 17 } 16
8.3.2 呼叫函式時的引數傳遞 19 public static int add(int i, int j) // 參數 i j 20 { 21 i = i + j; 22 System.out.println(" < 在函式 add() 內 > i=" + i); 23 return i; 24 } 執行結果 < 在主程式 main() 內 > i=1, j=2, k=0 < 在函式 add() 內 > i=3 < 在主程式 main() 內 > i=1, j=2, k=3 17
8.4 傳遞陣列 傳遞陣列時, 是以 傳參照 (Pass By Reference) 的方式來進行 若在函式裡變更了參數陣列的元素值, 那麼引數陣列的元素值也會一起改變 因為兩者都是參照到相同記憶體位置的資料 18
8.4.1 傳遞 main() 的引數 c>java 程式名稱 引數 0 引數 1... 引數 n public static void main(string args[]) { }... 19
8.4.1 傳遞 main() 的引數 範例程式 4 main() 的引數 2 // 程式名稱 :ch08_args.java 8 public static void main(string args[]) 9 { 執行結果 10 String s0, s1, s2, s3; 12 s0 = args[0]; 13 s1 = args[1]; 14 s2 = args[2]; 15 s3 = s0 + s1+ s2; 17 System.out.println("s0=" + s0); 18 System.out.println("s1=" + s1); 19 System.out.println("s2=" + s2); 20 System.out.println("s3=" + s3); c:\java\ch08>java ch08_args 我愛喝 s0= 我愛喝 s1=java s2= 咖啡 s3= 我愛喝 java 咖啡 java 咖啡 20
8.4.2 傳遞一維陣列 範例程式 5 取平方根 2 // 程式名稱 :ch08_sqrt.java 8 public static void main(string args[]) 9 { 10 int a[] = {2, 4, 9, 16, 25, 36, 49, 64, 81}; 11 12 print(a); 13 sqrt(a); 14 print(a); 15 } 21
8.4.2 傳遞一維陣列 17 public static void print(int x[]) 18 { 19 System.out.print(" 陣列內容為 :"); 20 for(int i = 0; i < x.length; i++) 21 System.out.print(x[i] + " "); 22 System.out.println(); 23 } 25 public static void sqrt(int x[]) 26 { 27 for(int i = 0; i < x.length; i++) 28 x[i] = (int)math.sqrt((double)x[i]); 29 } 執行結果 15 } 陣列內容為 :2 4 9 16 25 36 49 64 81 陣列內容為 :1 2 3 4 5 6 7 8 9 22
8.4.2 傳遞一維陣列 圖 8.3: 傳參照 ( 將引數陣列 a 的位址傳遞給參數陣列 x) 23
8.4.3 傳遞二維陣列 範例程式 6 將 a b 兩陣列相加 2 // 程式名稱 :ch08_matrixadd.java 10 int a[][] = {{1, 2, 3, 4, 5}, 11 {6, 7, 8, 9, 10}}; 12 int b[][] = {{0, 1, 2, 3, 0}, 13 {2, 1, 3, 3, 5}}; 14 int c[][] = new int[a.length][a[1].length]; 15 16 print('a', a); 17 print('b', b); 18 c = add(a, b); 19 print('c', c); 24
8.4.3 傳遞二維陣列 22 public static void print(char c, int x[][]) 23 { 24 System.out.println(c + " 陣列內容為 :"); 25 for(int i = 0; i < x.length; i++){ 26 for(int j = 0; j < x[i].length; j++) 27 System.out.printf("%2d ", x[i][j]); 28 System.out.println(); 29 } 30 System.out.println(); 31 } 25
8.4.3 傳遞二維陣列 33 public static int[][] add(int x[][], int y[][]) 34 { 35 int z[][] = new int[x.length][x[1].length]; 36 37 for(int i = 0; i < x.length; i++){ 執行結果 38 for(int j = 0; j< x[i].length; j++) a 陣列內容為 : 39 z[i][j] = x[i][j] + y[i][j]; 1 2 3 4 5 40 } 6 7 8 9 10 41 return z; b 陣列內容為 : 0 1 2 3 0 42 } 2 1 3 3 5 c 陣列內容為 : 1 3 5 7 5 8 8 11 12 15 26
8.5 方法多載 方法多載 (Method Overloading) 使用同一個方法 ( 函式 ) 名稱, 但它具備多種相似的功能例如, 我們可以設計加法函式, 讓它可以處理整數的相加, 浮點數的相加, 字串的相加, 並且都是使用 add 這個函式名稱 public static int add(int i, int j) public static String add(string i, String j) 27
8.5 方法多載 範例程式 7 方法多載 2 // 程式名稱 :ch08_add_overloading.java 10 int i = 1, j = 2, k = 0 ; 11 String s1 = "I love "; 12 String s2 = "Java!"; 13 String s3 = new String(); 14 15 k = add(i, j); 16 s3 = add(s1, s2); 17 18 System.out.println("i=" + i + ", j=" + j + ", k=i+j=" + k); 19 System.out.println("s1=\"" + s1 + "\", s2=\"" + s2 + 20 "\", s3=s1+s2=\"" + s3 + "\""); 28
8.5 方法多載 23 public static int add(int i, int j) 24 { 25 return (i+j); 26 } 27 28 public static String add(string i, String j) 29 { 30 return (i+j); 31 } 執行結果 i=1, j=2, k=i+j=3 s1="i love ", s2="java!", s3=s1+s2="i love Java!" 29
8.6 遞迴 計算出 2n 之值的公式如下 : 30
8.6 遞迴 圖 8.4: 以遞迴方式計算 2 的 n 次方 31
8.6 遞迴 撰寫遞迴程式可以從下列幾點著手 : 1. 函數名稱? 2. 函數引數及其資料型別? 3. 何時要遞迴呼叫? 4. 遞迴呼叫時要傳遞的參數值? 5. 何時該終止遞迴? 6. 遞迴函數的傳回值為何? 32
8.6 遞迴 15 public static int power2n_r(int n) // 函數名稱, 函數引數及其資料型別 16 { 17 int i, ans = 1; 18 19 if(n == 0) 20 return 1; // 終止遞迴, 傳回值 21 if(n >= 1) 22 return (2 * power2n_r(n-1)); // 遞迴呼叫 23 else 24 return -1; // n 必須為正整數 25 } 33
8.6 遞迴 範例程式 8 以非遞迴方式計算 2 的 n 次方之值,n 為正整數 2 // 程式名稱 :ch08_power2n.java 10 int i = 3; 11 12 System.out.println("2 的 " + i + " 次方 =" + power2n(i)); 13 } 34
8.6 遞迴 15 public static int power2n(int n) 16 { 17 int i, ans = 1; 18 19 if(n == 0) 20 return 1; 21 if(n >= 1){ 22 for(i = n; i > 0; i--){ 23 ans = 2 * ans; 24 } 25 return ans; 26 } 27 else 28 return -1; // n 必須為正整數 29 } 執行結果 2 的 3 次方 =8 35
8.6 遞迴 範例程式 9 以遞迴方式計算 2 的 n 次方之值,n 為正整數 2 // 程式名稱 :ch08_power2n_r.java 10 int i = 3; 11 12 System.out.println("2 的 " + i + " 次方 =" + power2n_r(i)); 13 } 36
8.6 遞迴 15 public static int power2n_r(int n) // 函式名稱, 函式引數及其資料型別 16 { 17 int i, ans = 1; 18 19 if(n == 0) 20 return 1; // 終止遞迴, 傳回值 21 if(n >= 1) 22 return (2 * power2n_r(n-1)); // 遞迴呼叫 23 else 24 return -1; // n 必須為正整數 25 } 執行結果 1 到 10 的奇數和 = 25 37
8.7.1 單字的擷取 8.7 綜合練習 8.7.2 以遞迴方式計算 n! 之值 38
8.8 後記 函式具有下列優點 : 將程式模組化, 可以重複地呼叫 使用, 減少程式開發的成本 簡化程式碼, 讓程式看起來較為簡潔, 容易閱讀 容易除錯, 可以降低程式維護的成本 可以充分發揮資訊隱藏 多載 改寫 ( 第 10 章 ) 等物件導向優點 39
8.8 後記 一般 ( 非類別 ) 函式的呼叫方式為 : 物件名稱. 函式名稱 ( 引數列 ) 類別函式的呼叫方式有以下兩種 : 1. 函式名稱 ( 引數列 ) 2. 類別名稱. 函式名稱 ( 引數列 ) 40
8.8 後記 呼叫函式時將引數值傳遞給參數是遵守以下規則 : 1. 傳遞以基本資料型別宣告的引數時, 是採用 傳值 (Pass By Value) 的方式傳遞 2. 傳遞陣列 ( 或物件 ) 時, 則採用 傳參照 (Pass By Reference) 的方式傳遞 41