學到這裏,你會發現我們寫的代碼都放在主函數裏,現在我們所寫的代碼都是比較簡單的,如果要寫的代碼很多呢,全放在主函數是不好的,在這裏我們要學會分離和封裝,將引入函數的介紹。
什麼是函數呢?函數主要用於封裝具有獨立功能的一段代碼,減少代碼的冗餘,提高代碼的複用率。
我們先來看一個問題:假如需要分別求出從1到 10、從 8 到 37以及從35到 49的整數和,按照以往的編寫方式:
class Demon01{
public static void main(String[] args){
int sum = 0;
for (int i = 1; 1 <= 10; i++)
sum +=i;
System.out.println("Sum from 1 to 10 is " + sum);
sum = 0;
for (1nt i= 8; 1 <= 37; i++)
sum +=i;
System.out.println("Suw from 8 to 37 is " + sum);
sum = 0;
for (Int i= 35; i <= 49; i++)
sum += i;
System.out.println("Sum from 35 to 49 is M" + sum);
}
}
我們會發現雖然求和的區間不同,但是我們還是寫了很多看似相同的代碼,而且這些區間是無規律的,也不能用循環來寫,因此我們就要用到函數將其功能進行封裝,將求一個區間的和定義爲一個函數,並且給它傳入兩個參數做爲區間,然後我們只需要調用這個函數並傳給它兩個實際的參數就能實現該功能。在此之前我們先來了解一下函數的定義格式:
權限修飾符 類型修飾符 返回值類型 函數名(參數列表){
函數體
return 返回值;
}
權限修飾符:標記誰能用,誰不能用,有三個( public—任何類和方法都能訪問、 protected—只能當前類和函數訪問 、 private—都不能訪問)
類型修飾符:標記函數的一些特殊含義(static—目前使用最多的、 native、 synchronized(後面再進行介紹))
函數名:給該段代碼起名稱
參數列表(可有可無):用於接收調用者傳遞進來的數據 以便函數內部使用
定義方式: (參數類型1 形參1,參數類型2 形參2....)
函數體:就是所封裝的那個具有獨立功能的那段代碼
return:僅僅表示結束當前函數(函數彈棧)
如果函數有返回值 則return必須寫出來
如果函數沒有返回值,則return可以不明顯的寫出來 但是一定存在(實際是隱藏了)
返回值:這段代碼計算的結果 (結果可有可無)
返回值類型:就是返回值是的數據類型(兼容即可)、 如果沒有返回值 則返回值類型寫void
函數在哪兒用?——只能在該函數裏(該函數自身調用該函數叫遞歸)、其他函數裏調用該函數。
因此我們在定義一個函數時,要明確:
1.函數要幹嘛?
2.函數需要什麼參數?(參數隨着函數進棧而存在, 隨着函數出棧而消亡)
3.函數是否有返回值?
無返回值的函數 只能調用 不能打印、賦值、運算
有返回值的函數 除了調用 參與打印、賦值、運算
函數的運行時基於棧的,函數運行叫作函數入棧, 函數結束叫作函數出棧
實際參數:實際傳遞給函數的數據 可以是變量 也可以是常量
形式參數:用於接收調用者傳遞來的數據,形式參數使其所屬函數的局部變量
局部變量:在函數中定義的變量
所以我們可以用函數的方法解決上面代碼重複的問題:
class Demon02{
public static void main(String[] args){
sum(1,10);
sum(8,25);
sum(35,49);
}
public static int sum(int a ,int b){
int sum = 0;
for(int i = a;i<=b;i++){
sum+=i;
}
return sum;
}
}
再來明確一下函數的內存調用:
(圖中標號代表了執行的流程),先介紹一下,在運行一個程序時,首先加mian()函數,因爲mian()函數時程序的開始,前面已經介紹了函數是在“棧內存”中開始的,所以首先將mian()加載到我們的棧內存中,圖中紅色圓點代表加載進來的mian()函數。然後mian()函數調用sum()函數並傳遞給了該函數兩個實際參數1和10,此時注意,我們的實際參數並不是直接付給變量了,而是將所有的實際參數都存放在了“常量池”中,並且每個參數都有一個存放的地址,此時程序中變量傳遞的都是參數的地址,所以執行②和③,此時加載第一個調用的sun()函數,並將兩個參數的地址傳遞給兩個變量,在sum()函數中定義的其他變量所賦的初值都是傳遞的是參數的地址,當sum()函數計算完成後,將結果返回,此時sum()函數彈棧,再調用下一次的sum()函數進行參數傳遞。與第一次執行流程一樣。
接下來我們來用函數解決問題:
import java.util.Scanner;
class Demon03{
public static void main(String[] args){
Scanner scanner =new Scanner(System.in);
System.out.print("請輸入一個數字:");
long number=scanner.nextLong();
System.out.println(sumDigits(number)); //調用該函數
}
public static int sumDigits(long n){ //定義一個函數,專門用來計算該數各個數的和
int sum=0; //每此調用都有一個計算結果
while(true){
sum+=n%10; //模10取最後一位相加
n/=10; //除去後面的位數
if(n==0){
break; //所有位數都已相加跳出while循環
}
}
return sum; //將結果返回
}
}
//計算一個整數n的m次冪 2^3 2^-3
class Demo04{
public static void main(String[] args){
System.out.println(pow(2,4)); //對函數不同傳參的條用,查看程序運行結果是否正確
double a=pow(3,2)+10;
a=pow(2,-4);
System.out.println(a);
//System.out.println(pow(0,0));
System.out.println(pow(0,1));
System.out.println(pow(0,-9));
System.out.println(pow(2,0));
}
public static double pow(int n,int m){ //定義一個計算一個整數n的m次冪的函數,返回值爲double類型,因爲整數的負數次冪是分數
if(n==0&&m==0){ //首先判斷特殊情況,0的0次冪是沒有意義的,也沒有正常返回值
//沒有正常返回值了!只能拋異常
throw new ArithmeticException("0 and 0"); //這裏可以先用拋異常的方式提示用戶輸入的不正確
}
if(n==0){ //其次0的任何次冪(除了0)都爲0
return 0;
}
if(m==0){ //任何數(除了0)的0次冪都爲1
return 1;
}
double multi=1;
for(int i=1;i<=Math.abs(m);i++){ //如果和m都不爲0,則將n乘m次,
multi*=n;
}
if(m>0){
return multi; //如果m大於0,表示是整數次冪,爲正整數
}else{
return 1/multi; //如果m小於0,表示是負數次冪,爲分數
}
}
}
}
/*
迴文素數是指一個整數即爲素數又爲整數,比如131
編寫一個程序,提示用戶輸入一個整數,判斷該數是否是迴文素數,並給出相應的結果
解題步驟:1.提示用戶輸入一個數字
2.判斷是否是迴文素數
3.判斷是否是素數且迴文
4.判斷是否是迴文數
5.計算翻轉數字
6.計算是否是素數
*/
import java.util.Scanner;
class Demon05{
public static void main(String[] args){
int number=initData(); //先調用提示函數將用戶輸入的整數保存到一個變量中
if(isHuiWenSuShu(number)){ //判斷該數是否是迴文素數並給出相應的結果
System.out.println("是"); //是迴文素數條件爲真
}else{
System.out.println("不是");
}
}
public static int initData(){ //此函數用於提示用戶輸入整數,並將用戶輸入的整數返回
Scanner scanner=new Scanner(System.in);
System.out.print("請輸入一個數字:");
return scanner.nextInt();
}
public static boolean isHuiWenSuShu(int num){ //此函數用於判斷是否是素數且迴文
return isHuiWen(num)&&isSuShu(num); //將判斷結果返回
}
public static boolean isHuiWen(int num){ //此函數用於判斷該數是否是迴文數
int revNum=reverse(num);
return revNum==num;
}
public static int reverse(int num){ //此函數用於翻轉數字
int sum=0;
while(true){
sum=sum*10+num%10;
num/=10;
if(num==0){
break;
}
}
return sum;
}
public static boolean isSuShu(int num){ //計算是否爲素數
boolean flag=true; //是素數結果爲真
for(int i=2;i<=num/2;i++){ //查找因子,只要除了1和本身外還有其他因子,就不是素數
if(num%i==0){
flag=false; //將flag置爲假,這是哨兵法,之前講for循環有講過
break; //立即跳出循環,不再執行
}
}
return flag;
}
}
函數重載
至此我們再來看一個很簡單的問題:計算兩個數相加,並且還是寫一個函數封裝,在主函數調用。
在之前我們所寫代碼的思路中,可能就會寫一個sum()函數,然後它的返回值爲int類型,現在有一個問題,我們並不會只計算整數的和,可能還有小數等精度較高的函數,如果返回值爲int類型,程序就會保存,提示精度丟失的問題。可能會有人想,那將返回值類型置爲double類型不就ok了?但是在我們平時使用的計算器中,整數計算的結果後面會有小數點嗎?顯然是沒有的,在這裏我們將引入函數重載的內容。
什麼是函數重載?表面上來看:一個類中有若干個重名函數,比如上面問題我們就可以定義爲兩個名爲sum()的函數
class Demon06{
public static void main(String[] args){
System.out.println(sum(12,67));
System.out.println( sum(6.9,89.6));
}
public Static int sum(int a, int b){
return a+b;
}
public static double sum(double a,double b){
return a+b;
}
}
此時程序中這兩個名叫sum的函數是重載關係
重載和函數的權限修飾符沒有關係、重載和函數的類型修飾符有關係、重載和函數的返回值類型沒有關係、和函數名有關係( 前提是重名)、和函數參數名沒關係 ,和類型有關係。目前而言 重載的前提是函數重名
具體到底是不是重載關係 看參數類型的排列組合(個數,順序),比如以下幾個函數都是重載關係
add( double a, double b) 與 add( int a,int b)
add(int a,double b,int c) 與 add(double a,int b,int c) 與 add(int a,int b,double c)
至此函數已經介紹完畢,希望對您有所幫助!