一、 Java中的方法
需求:求整數10和整數3的乘積,然後在求整數2000和整數300的乘積。
public class MethodTest01 {
public static void main(String[] args){
int i = 10; int j = 3; int k = i * j; System.out.println("i * j = " + k);
int l = 2000; int m = 300; int n = l * m; System.out.println("l * m =" + n); } } |
功能已經實現,但是存在的問題是,兩段代碼完成的功能相同,數據的類型也相同。只是具體的數據不同。並且代碼重複使用了兩次,雖然實現了功能,但缺點是相同功能的代碼不能實現重用。所以在Java中可以使用方法來實現相同功能的代碼塊重複利用。
1.1 方法的定義
方法就是一段代碼片段,可以完成特定的功能,並且可以重複利用。定義方法的語法格式如下。
[方法修飾列表] 返回值類型 方法名(方法形式參數列表){ 方法體; } |
方法修飾符:可以是public、protected、private、abstract、static、final和synchronized。注意:其中public、protected和private不能同時存在,只能是其中之一。
方法的返回值類型:可以是Java語言中的任何一種數據類型(基本數據類型和引用數據類型)。如果該方法執行結束之後,沒有任何返回值,那麼定義方法的時返回值類型爲void。如果存在返回值,使用 return語句。Return語句後面不能再執行語句,因爲不可能會執行到,編譯不能通過。
方法名:合法的標識符。
方法參數列表:定義方法時可以有參數,也可以沒有參數,如果有多個參數,參數之間要使用逗號進行分隔。
定義一個方法實現上面的需求。
public class MethodTest02 {
public static void main(String[] args){ // 調用static修飾的靜態方法需要使用類名.的方式 // 直接調用方法並傳入需要乘積的參數 MethodTest02.method(10, 3); MethodTest02.method(2000, 300); } // static修飾的方法是靜態方法 public static void method(int i,int j){ int product = i * j; System.out.println("i * j = " + product); } } |
定義方法。
public class MethodTest03 {
public static void main(String[] args){ // 調用方法,靜態方法直接使用類名.的方式進行調用 MethodTest03.method01(); // 調用方法時同時傳遞參數給方法 MethodTest03.method02("張三"); // 調用有返回值的方法 int product = MethodTest03.method03(20, 30); System.out.println("product = " + product); } // 定義方法 // public 公共的,外面的類可以調用 // static 方法是靜態的 // void 沒有返回值 // method01 方法名 // 方法沒有參數 public static void method01(){ System.out.println("Hello Java"); } // 方法帶有一個String類型的參數 // 參數name是局部變量,作用域在方法體內 public static void method02(String name){ System.out.println(name); } // 方法有兩個int類型的參數 // 方法有返回值,返回值類型爲int類型 public static int method03(int i,int j){ int product = i * j; // 方法有返回值,必須有return語句,並且返回值的類型與方法定義的類型 // 相同,否則編譯不能通過 return product; // return 語句後面不能存在其他語句,否則編譯不能通過 // System.out.println("product = " + product); } } |
1.2 方法的調用
靜態方法的調用原則。在同一個類中,調用靜態方法可以使用類名.的方式進行調用,也可以直接使用方法名進行調用。
public class MethodTest04 {
public static void main(String[] args){ // 在同一個類中,可以直接使用方法名調用靜態方法 method02(); // 也可以使用類名的方式調用靜態方法 MethodTest04.method02(); // 不在同一個類中的靜態方法必須使用類名.的方式進價調用 A.method03(); }
public void method01(){ method02(); MethodTest04.method02(); }
public static void method02(){ System.out.println("Hello Java"); } }
class A{ // 在A類中定義的靜態方法 public static void method03(){ System.out.println("A method03()"); } } |
調用帶參數的靜態方法,需要傳遞相應類型的參數。通常情況下調用有返回值的方法就是要取得返回值,返回值變量的類型必須與方法返回值的類型相同。在方法中,return語句執行則方法結束,執行流程放回到方法調用處。
public class MethodTest05 {
public static void main(String[] args){ // 調用有返回值的靜態方法 int success = method01(true); // 返回值類型是方法的返回值類型 // 參數的類型必須是方法定義的參數類型,並且順序相同 int product = method02(10,3); System.out.println("product: " + product); // 不在同一個類中,必須採用類名.的方式調用靜態方法 A.method03("張三"); } // 靜態方法 public static int method01(boolean b){ /* if(b){ return 1; }else{ return 0; } */
if(b){ return 1; }
return 0; } // 靜態方法,返回值類型是int型,兩個參數。形式參數類型是int public static int method02(int i,int j){ // 返回值的類型必須是方法定義的返回值類型 return i / j; // return語句執行,方法結束,所以不能有語句在return後面,編譯不能通過 // System.out.println(i / j); } }
class A{ // 定義的靜態方法 public static void method03(String name){ System.out.println("name: " + name); } } |
如何終止方法的執行,採用的方式是如果符合某種條件,使用return語句終止方法的執行。
public class MethodTest06 {
public static void main(String[] args){
method01(11); method01(8); } // 靜態方法,沒有返回值,一個形式參數,類型是int public static void method01(int i){ // 如果參數值大於10終止方法執行 if(i > 10){ // return語句執行,方法結束 return; }
for(;i <= 10;i++){ if(i == 5){ // break只是結束終止循環 break; } System.out.println("i = " + i); }
System.out.println("break後要執行的語句"); } } |
return語句的使用方式。
public class MethodTest07 {
public static void main(String[] args){ // 靜態方法調用 method01(); String info = method02(); System.out.println(info); }
public static void method01(){ // 方法沒有返回值,如果使用return,return後面不能存在語句 //return; // 編譯可以通過,雖然最後的語句不會輸出 if(true){ return; }
System.out.println("HelloWord"); }
public static String method02(){ // 如果方法有返回值,必須存在return語句,後面的值類型必須和返回類型 // 相同,並且return語句後面不能跟其他語句 //return "HelloWord"; // 這種方式可以, /* if(true){ return "HelloWord"; }else{ return "OK"; } */ // 這種方式可以 if(true){ return "HelloWord"; }
return "OK"; // 方法有返回值,return語句一定是最後一條語句,否則編譯不能通過 //System.out.println("HelloWord"); } } |
1.3 方法的重載
需求:定義三個方法,分別是int類型、double類型和long類型實現乘積的方法,方法有返回值。
public class MethodTest08 {
public static void main(String[] args){
int product1 = productInt(10,20); System.out.println(product1); double product2 = productDouble(10.11,93.23); System.out.println(product2); long product3 = productLong(10L,20L); System.out.println(product3); // 注意:類型從小容量到大容量,自動類型轉換 productInt(10,20); productDouble(10,20); productLong(10,20); }
public static int productInt(int i1,int i2){
return i1 * i2; }
public static double productDouble(double d1,double d2){
return d1 * d2; }
public static long productLong(long l1,long l2){
return l1 * l2; } } |
上面的代碼實現了功能,但是存在的問題是每個方法的方法名稱不同,相同的是求兩個數值類型的值的乘積。另外,還需要記憶多個方法名,並且代碼不是特別易讀。如果方法實現的功能相同,只是參數類型或者參數的數量不同,可以採用方法重載的方式,如下例子。
public class MethodTest09 {
public static void main(String[] args){
int product1 = product(10,20); System.out.println(product1); double product2 = product(10.11,93.23); System.out.println(product2); long product3 = product(10L,20L); System.out.println(product3); }
public static int product(int i1,int i2){
return i1 * i2; }
public static double product(double d1,double d2){
return d1 * d2; }
public static long product(long l1,long l2){
return l1 * l2; } } |
修改後,三個方法的方法名稱相同,參數類型不同,返回值類型不同,但是調用時只需要傳入不同類型的參數,不需要指定調用哪個方法,系統會根據參數的類型自動調用相應的方法並將結果返回。並且不需要記憶更多的方法名稱。
方法重載的條件。
1.方法重載只出現在同一個類中
2.方法名相同
3.方法的參數類型,個數,順序至少有一個不同
4.方法的返回類型可以不同(不依靠返回類型來區分重載)
5.方法的修飾符可以不同,因爲方法重載和修飾符沒有任何關係
public class MethodTest10 {
public static void main(String[] args){
} // 方法重載必須在同一個類中 // 參數類型不同,是方法重載 public static void m1(int i1,int i2){} public static void m1(double d1,double d2){} // 參數數量不同,是方法重載 public static void m2(int i1){} public static void m2(double d1,double d2){} // 參數順序不同,是方法重載 public static void m3(int i1,double d1){} public static void m3(double d1,int i1){} // 參數類型相同,參數個數相同,不是方法重載,是方法重複 //public static void m4(int a){} //public static void m4(int b){} // 參數類型和個數相同,返回值類型不同,不是方法重載, // 方法重載和返回值類型無關 //public static int m5(int i){} //public static void m5(int i){} // 參數相同,訪問修飾符不同,不是方法重載,方法重載和訪問修飾符無關 //private static void m6(){} //public static void m6(){} } |
方法重載舉例。
public class MethodTest11 {
public static void main(String[] args){ // sun提供的方法重載例子 System.out.println(10); System.out.println(23.2); System.out.println(true); System.out.println('A'); } } |
1.4 類、方法、屬性(靜態變量)的關係
在一個類中,從類和成員的角度可分爲靜態的和成員的部分。靜態的部分可分爲靜態變量、靜態方法和靜態內部類等等。成員的部分可以分爲成員變量、成員方法和成員內部類等等。注意:也就是說,在類體中只存在兩部分內容,一是屬性(成員的或靜態的),二是方法(成員方法或靜態方法)或內部類。那麼可以執行的代碼只能存在與方法中,包括成員方法或靜態方法,所以,存在與方法中的代碼是爲了實現一個特定的功能,就像一個工廠生產特定的產品,方法的參數類似於工廠需要的生成原料,而方法的返回值相當於工廠生產出的產品。
public class MethodTest12 {
// 類體
// 成員屬性 int i = 10;
// 靜態變量 static String country = "中國";
// 成員方法 public void method01(int i){ // 方法體 // 可以執行的代碼 // 將參數做一些特殊的處理 System.out.println(i); }
// 靜態方法 public static int method02(int i1,int i2){ // 方法體 // 可以執行的代碼 // 實現特定功能,並返回特定的結果 return i1 + i2; }
public static void main(String[] args){ // 方法體 // 可以執行的代碼 // 程序的執行入口方法 } } |
1.5 調用方法的執行流程
1.5.1 棧的概念
當通過Java命令執行一個程序時,會啓動一個JVM,Java虛擬機進程,並創建程序運行環境,會創建程序運行的棧內存,下面的圖做了簡單的說明。
1.1.1 程序的執行過程
下面通過一個程序說明程序的執行過程。
public class MethodTest13 {
public static void main(String[] args){ // 調用method01()方法 method01(10); } // method01()方法調用method02()方法 public static void method01(int i){ method02(i); } // method02()方法調用method03()方法 public static void method02(int i){ method03(i); }
public static void method03(int i){ System.out.println("i: " + i); } // method04()方法沒有被調用 public static void method04(int i){ System.out.println(i); } } |
main()方法調用method01()方法,method01()方法調用method02()方法,method02()方法調用method03()方法,在method03()方法中將參數輸出。下面通過一個圖說明程序的執行流程。
注意:當方法被調用時,纔會在內存中爲方法分配空間。如果只是定義了方法,但是沒有被調用,不會爲方法在內存中分配空間。方法被調用時,Java虛擬機會爲方法在棧內存中爲方法分配空間。方法被調用時會發生壓棧操作,當方法執行結束後會發生彈棧操作。
一、 遞歸
方法的遞歸調用就是方法自身調用自身。通常在樹形結構訪問時用到,如:目錄的遞歸檢索,又如權限管理中權限目錄數的訪問等等。下面的例子是遞歸的簡單演示。
public class RecursionTest01 {
public static void main(String[] args){ // 調用method01()方法 method01(10); } // 方法method01() public static void method01(int i){ // 方法自己調用自己,無限調用下去沒有返回 method01(i); } } |
在上面的例子中,因爲遞歸沒有結束的條件,所以每調用一次method01()方法,就會在內存中開闢空間,產生壓棧操作,但沒有出棧操作,不會釋放內存,會導致棧內存溢出錯誤!所以遞歸程序必須要有結束條件。
不使用遞歸計算1~n整數的和。
public class RecursionTest02 {
public static void main(String[] args){
int sum = method01(10); System.out.println(sum); }
public static int method01(int n){ int sum = 0;
for(int i = 1;i <= n;i++){ sum += i; }
return sum; } } |
使用遞歸實現1~n的求和。
public class RecursionTest03 {
public static void main(String[] args){
int sum = sum(5); System.out.println(sum); }
public static int sum(int n){ // 如果參數n等於1,直接返回 if(n == 1){ return 1; }else{ // 否則,將參數n減一作爲參數調用自身,返回的值與參數n的和就是 // 最終的結果。 return n + sum(n - 1); } } } |
上面程序的執行流程:
練習:
1. 採用遞歸的方式求1~n的偶數的和。
public class RecursionTest04 {
public static void main(String[] args){
int sum = sum(5); System.out.println(sum); }
public static int sum(int n){ if(n % 2 != 0){ n -= 1; } if(n == 2){ return 2; }else{ return n + sum(n - 2); } } } |
2. 採用遞歸的方式求n!。
public class RecursionTest05 {
public static void main(String[] args){
int sum = product(5); System.out.println(sum); }
public static int product(int n){ if(n == 1){ return 1; }else{ return n * product(n - 1); } } } |
3. 不採用遞歸的方式實現n!。
public class RecursionTest06 {
public static void main(String[] args){
int sum = product(5); System.out.println(sum); }
public static int product(int n){ int product = 1; for(int i = 1;i <= n;i++){ product *= i; }
return product; } } |