Java中的函數(方法)

每個語言裏面基本上都有函數,不過現在好多語言裏面已經慢慢的修改了它的名稱,不叫函數了,而是叫做方法。爲了方便起見,下面我還是以“函數”這個名稱來說。我們一直在用函數,但是如果突然問你,爲什麼會有函數的存在?或者說函數的作用是什麼?想必這個問題不好回答吧,下面我們就具體來看看吧!

函數

  • 函數的定義

1.函數的存在意義

函數是爲了完成某些特定的功能而實現的一種結構,它主要解決的是那些重複且具有獨立功能的代碼段。

正如前面第三章練習題當中,我們講了一道石頭剪刀布的題。在那道題中,我們用了兩個switch語句來對com和usr這兩個對象具體的功能進行了說明,其實我們可以發現這兩個switch語句除了對象不一樣其他部分基本都是一樣的,簡言之就是它的功能是一樣的。但是寫兩個switch語句又使得代碼有點長,造成了代碼冗餘,因此我們引出了“函數”這個概念。

這也就意味着我們可以將這些具有獨立功能的代碼段進行封裝,得到一個函數,這樣不僅可以降低代碼冗餘,也降低了主函數的代碼量,而且也使得程序更加清晰明瞭。我們在需要用到這個函數的時候,只需要在主函數中調用它,然後傳入適當的參數即可。這是不是很方便呢?

2.函數的格式

函數訪問權限   函數類型   返回值類型   函數名(參數列表){

                  函數體

                  return返回值;

}

訪問權限:指的就是函數的使用範圍(內部和外部),它包括四個,分別是public、protected、默認不寫、private。

函數類型:指的是函數的分類,說的就是函數的具體使用場景和場合。包括static靜態函數、默認不寫成員函數、                                    abstract抽象函數、native本地函數、synchronized同步函數等。

函數名:程序員給這一段代碼自定義的名稱。

參數列表:由若干個( 參數類型,參數名...)組成,主要用於接受外界傳遞給函數的的一些數據。

函數體:就是具有獨立功能的代碼塊。

return:return僅僅表示結束當前函數,如果函數有返回值,則需要在函數結束前將返回值返回給調用者。

返回值:指的是這一具有獨立功能的代碼塊的運算結果,這個運算結果需要傳遞給外界(配合return使用)。

返回值類型:就是返回值的數據類型(可兼容即可)。

 

一個簡單的函數舉例:

public static void main(String[] args){
    System.out.println("Welcome to Java!");
}

     public 公共的,代表訪問權限。

     static靜態的,代表函數類型。

     void無返回值,代表返回值類型,但是不代表沒有返回。

     main主函數的名稱。

     String[ ]表示一個字符串數組。

     args參數名,這個可以修改,之所以要有參數就是爲了後面運行程序時可以傳參數。

PS:如果一個函數中沒有return,是因爲該函數沒有返回值,所以return可以忽略不寫,但是不代表不存在

但是如果函數有返回值,那麼return必須寫,return+返回值。

  • 函數的分類

有返回值有參數的函數;

有返回值無參數的函數;

無返回值有參數的函數;

無返回值無參數的函數;

PS:有返回值的函數,參與運算、輸出、賦值;無返回值的函數,只能調用,不能對返回結果進行額外的操作。

  • 函數的傳參

實際參數(實參)——就是在調用函數的時候,給函數傳遞的數據(常量、變量)。

形式參數(形參)——就是定義函數的時候,參數列表當中的數據。

局部變量——在函數當中創建的變量都稱爲是局部變量

一個問題:實參到底是將什麼傳遞給了形參?

答案:常量在常量池當中的地址,對象在堆內存當中的地址。

舉個例子:

首先,有兩個函數,一個是main函數,一個是pow函數。main函數開始運行,創建兩個整型變量a和b,然後在常量池當中找到4的地址,把它們給a和b。緊接着開始運行pow函數,pow函數中,創建三個變量a、b、sum,與此同時main函數當中的實際參數a、b把他們當中存儲的數據的地址傳遞給pow函數當中的形式參數a和b,然後pow函數計算出a^b的結果256,然後在常量池當中找到256的地址,把它給sum,然後執行return sum,這也就說明pow函數結束了。在結束之前sum把256的地址又傳遞給main函數,與此同時,main函數當中在創建一個double型的變量c來存儲256的地址。這就是函數傳參的過程,其實挺簡單的。

  • 函數棧

函數的運行是基於棧(一個先進後出的容器結構)內存運行的。我們可以將每一個函數理解爲是函數幀,位於棧頂的函數幀優先運行,主函數main肯定是第一個進棧的。return表示結束當前函數,也就是當前函數彈棧。

舉一個函數在棧中運行的具體實例:

public static void main(String[] args){
    int a=4;
    int b=4;
    double c=pow(a,b);
    System.out.print(c);
}

public static double pow(double a,int b){
    if(b==0){
        return 1;
    }
    double sum=1;
    for(int i=1;i<=Math.abs(b);i++){
        sum*=a;
    }
    return b>0?sum:1/sum;
}

我們可以看到,代碼中有兩個函數,下面就通過一幅動圖來具體說明函數在棧中運行的具體過程。

  • 函數的重載

函數的重載指的就是同一個類中出現的同名函數,它與函數權限、返回值類型、參數名沒有關係,只和參數類型的排列組合有關係(此外要注意一下參數類型的向下兼容問題)。另外,函數重載的好處就在於我們可以擴展函數的功能(函數重名,但是參數類型不一樣,執行內容也可以不一樣)。

舉個例子:看下面這個代碼。

class Chongzai{
    public static void main(String[] args){
        int a=3;
        int b=4;
        System.out.println(add(a,b));
        double d=3.14;
        double e=5.44;
        System.out.println(add(d,e));
        System.out.println(add(9,3.14));
        System.out.println(add(3.14,9));
    }
    //1.對兩個小數進行加法運算
	public static double add(double a,double b){
        System.out.println("double+double");
        return a+b;
    }
    //2.對兩個字符串進行拼接
    public static String add(String a,String b){
        System.out.println("String+String");
        return a+b;
    }
    //3.
    public static double add(int a,double b){
        System.out.println("int+double");
        return a+b;
    }
    //4.
    public static double add(double a,int b){
        System.out.println("double+int");
        return a+b;
    }
    //5.對兩個整數進行加法運算
    public static int add(int a,int b){
        System.out.println("int+int");
        return a+b;
    }
}

運行結果:

 

int+int
7
double+double
8.58
int+double
12.14
double+int
12.14

main函數在執行到第一個輸出語句時,開始在底下其他的函數當中找合適自己的函數,因爲這個輸出語句裏面add函數中的參數a,b都是int類型的,所以找到了第5個函數,傳入的兩個參數也都是int類型的;緊接着執行到第二個輸出語句時,又開始找適合自己的函數,因爲此時add函數中的參數的d,e都是double類型的,所以找到了第1個函數,傳入的兩個參數也都是double類型的;同理,第三個輸出語句add函數中的參數一個是int類型,一個是double類型,於是就找到了第3個函數;第四個輸出語句add函數中的參數一個是double類型的,一個是int類型的,於是找到了第4個函數。

因此,在尋找適合自己函數的時候,要遵循以下流程:
1.看是否有確切的參數定義 int+int,查看是否有(int,int)
2.看是否有可兼容的參數定義 int+int,查看是否有(double,double)
3.如果可兼容的參數定義有多個int+int,(double,int)或(int,double) 此時報錯“引用不明確”。

  • 函數的遞歸調用

函數遞歸的體現就是函數自身調用函數自身。一般而言,但凡能夠被迭代(循環)解決的問題,遞歸都可以解決,但是遞歸能解決的問題,迭代就不一定了。在某些問題上,遞歸所寫的代碼要比迭代寫的代碼少;在某些問題上,迭代是寫不出來的,所以只能用遞歸。這就是它兩之間的區別和聯繫。

遞歸其實是分治法的一種實現方式(實現思路),分治法是一種算法思想,它主要解決的問題是:將大問題進行拆分,拆分成若干個小的問題進行求解,最終將每個小問題的解進行合併。其實,分治法就是一種暴力破解法(窮舉),也是一種搜索最優答案的算法。

遞歸遞歸,先遞後歸。我們可以把遞歸分成三個部分,即前進段(就是講問題從大化小)、結束段(就是問題無法再繼續化小,則處理當前的問題)、返回段(就是將小問題都處理完畢之後,向上進行返回(有些問題是不需要返回的)。

舉個例子:我們分別用迭代和遞歸的思想來求斐波那契數列的前10項和。 

問題:斐波那契數列,求前10項。
        前10項:1 1 2 3 5 8 13 21 34 55 ......
        f(n)指的是斐波那契的第n項
        f(n)=f(n-1)+f(n-2) f(1)=1 f(2)=1
                    f(5)
                f(4)        f(3)
            f(3)    f(2) f(2)   f(1)
        f(2)    f(1)

class Test01{
    public static void main(String[] args){
        for(int i=1;i<=10;i++){
            System.out.println(fibo(i));//用遞歸的思想求前10項
        }
        System.out.println("===================");
        fiboIterator(10);//用迭代的思想求前10項
    }
    //迭代函數
    public static void fiboIterator(int n){
        int a=1;
        int b=1;
        System.out.println(a);
        System.out.println(b);
        int count=2;
        int c;
        while(true){
            c=a+b;
            System.out.println(c);
            count++;
            if(count==n){
                return;
            }
            a=b;
            b=c;
        }
    }
    //遞歸函數
    public static int fibo(int n){
        if(n==1||n==2){
            return 1;
        }
        return fibo(n-1)+fibo(n-2);
    }
}

運行結果:

1
1
2
3
5
8
13
21
34
55
===================
1
1
2
3
5
8
13
21
34
55

通過上面這個例子我們可以看出,迭代代碼比較簡潔,代碼量很少,但是如果計算量比較大的話,它越往後計算越慢。但是遞歸不會出現這個問題,兩者各有利弊。

 

  • 常用函數

我們常用的函數大致可以分爲兩類,Math類和String類,我大概舉幾個例子。

1.Math類函數:

class Test01{
    public static void main(String[] args){
	//返回比任何其他值都更接近e(即自然對數的底數)的值。
        System.out.println(Math.E);//2.718281828459045

	//返回比任何其他值都更接近pi(即圓的周長與直徑之比)的值。
        System.out.println(Math.PI);//3.141592653589793

	//返回指定數字(-2)的絕對值。
	System.out.println(Math.abs(-2));//2

        //返回最小的(最接近正無窮大)double值,該值小於等於參數,並等於某個整數。
        System.out.println(Math.ceil(-2.1));//-2.0

        //返回最大的(最接近正無窮大)double值,該值小於等於參數,並等於某個整數。
        System.out.println(Math.floor(2.3));//2.0

        //返回sqrt(x?+y?),即sqrt(3?+4?)。
        System.out.println(Math.hypot(3,4));//5.0

	//返回兩個值中較大的一個。
	System.out.println(Math.max(2,10));//10

	//返回兩個值中較小的一個。
	System.out.println(Math.min(1,9));//1

	//返回第一個參數的第二個參數次冪的double值,即2^4。
	System.out.println(Math.pow(2,4));//16.0

	//返回正確舍入的值的double類型正平方根,即根號下9。
	System.out.println(Math.sqrt(9));//3.0

	//返回一個帶正號的隨機值,該值大於等於0,且小於1。
	System.out.println(Math.random());//0.06910097634766355

        //返回最接近參數並等於某一整數的double值,即四捨五入。
        System.out.println(Math.rint(3.5));//4.0
    }
}

2.String類函數

class Test02{
    public static void main(String[] args){
        String s = "ababABcdcd";
		
	//1、2、3、4爲查詢相關操作
	//5、6、7、8、9、10、11爲判斷相關操作
	//12、13、14、15爲修改相關操作
		
        //1.獲取此字符串指定角標處的字符
        System.out.println(s.charAt(0));//a
		
	//2.在字符串中從左到右查找指定元素第一次出現的位置   
        System.out.println("abcccccd".indexOf('c'));//2
        System.out.println("abcccccd".indexOf("cd"));//6
		
	//3.返回此字符串的長度。
	System.out.println(s.length());//10
		
	//4.返回一個新字符串,它是此字符串的一個子字符串。
        System.out.println(s.substring(0,3));//[0,3)//aba
		
        //5.判斷指定子串是否包含在此字符串中,如果包含返回true。
        System.out.println(s.contains("ab"));//true
		
	//6.測試此字符串是否以指定的前綴開始。
	System.out.println(s.startsWith("ab"));//true
		
        //7.測試此字符串是否以指定的後綴結束。
        System.out.println(s.endsWith("cd"));//true

        //8.按照字典順序比較兩個字符串的大小,返回值有負數 0 正數
        //負數 前者在後者ASCII之前
        //0    前者和後者相等
        //正數 前者在後者ASCII之後
	/*
        abc
        abd c-d=-1
        */
        System.out.println("abc".compareTo("abd"));//-1  
        System.out.println("abc".compareTo("abc"));//0
		
        //9.比較兩個字符串是否相等(比的是內容)
        System.out.println("abc".equals("abc"));//true
		
        //10.將此String與另一個String比較,不考慮大小寫。
        System.out.println("ABC".equalsIgnoreCase("abc"));//true
		
	//11.當且僅當 length()爲 0 時返回 true。
        System.out.println("".isEmpty());//true
		
	//12.返回一個新的字符串,它是通過用newChar替換此字符串中出現的所有oldChar得到的。
        System.out.println(s.replace('a','c'));//cbcbABcdcd
		
	//13.使用默認語言環境的規則將此String中的所有字符都轉換爲大寫。
        System.out.println(s.toUpperCase());//ABABABCDCD
		
	//14.使用默認語言環境的規則將此String中的所有字符都轉換爲小寫。
	System.out.println(s.toLowerCase());//abababcdcd
		
	//15.返回字符串的副本,忽略前導空白和尾部空白。
        System.out.println("  abc abc abc   ".trim());//abc abc abc
    }
}

 

好了,到這裏爲止關於Java中函數(方法)的內容我就已經說完了,後面還會持續更新其他內容,歡迎大家觀看!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章