引言
我相信能点到这里来的童鞋们对计算机编程中的函数肯定已经有或多或少的认知,首先我们需要知道的是:计算机编程语言中的“函数”和它隔壁领域——数学中的同名同姓的亲戚“函数”并没有关系,它们只是名字一样,但是其关系可能跟广东人和福建人的关系一样,好像有点关系,但其实又没什么关系
函数的定义(what)
肯定有不少同学使用过一些平台的趣味编程学习软件,你在左边输入代码,右边窗口的小人就根据你的指示左拐右拐前进后退拾取道具,其中比较著名的就是Apple家用来学习swift语言的Playgrounds了
上图为Playgrounds应用界面
而左边系统为你提前准备好的行为合集就是函数,而动作(collectGem、moveForward)则是函数的名称,这也就得出来结论:函数是为了达到特定目的而设计的有名称的代码集合。
Java中如何定义函数(how)
在Java中定义函数有固定的格式,与主函数类似,函数的定义格式:访问修饰符 函数的返回值数据类型(如无返回值用void) 函数名称 (参数列表)。晦涩难懂的文字很不容易理解,对吧?下面笔者用一个例子直观的看看函数的定义:
public class funCtion{
public static void main(String[] args){
}
public static double sum(double a,double b){
return a + b ;
}
}
在上面的代码块中可以看到,我们定义了一个名为sum的函数,其中可以引入两个参数a、b,并且返回a与b的和,这就完成了一个最简单的函数的定义。
函数的调用
就像我们做晚饭,菜都已经改刀好了(定义函数),是时候把菜放进锅里烹饪了。想要在程序中使用自己自定义的函数,与C语言类似,我们需要对函数进行调用,Playgrounds就是通过连续调用独立功能的函数完成一系列动作
(又把playgrounds拿出来鞭尸)
这里我们引用与上一节的同一个例子继续举例:
public class funCtion{
public static void main(String[] args){
System.out.println(sum(10,20));
}
public static void sum(double a,double b){
return a + b ;
}
}
在上面这个例子中,我们之间把函数的返回的结果打印出来,这段代码运行的结果应该是 30.0 因为我们定义的参数类型是double类型。而返回的 a+b直接计算成结果变成返回值被打印出来。
函数的分类
函数可以根据标准的不同分为不同类型的参数:
1、按参数分类
- 有参数的函数:有参数的函数需要在定义时,根据所需变量的数据类型在函数的参数列表中提取定义,以便在函数调用时传参数进函数。
- 无参数的函数:无参数的函数则无需定义,直接调用函数即可执行动作。
2、按返回值分类
- 有返回值的函数:有返回值的函数我们则需要在定义函数时,将函数的返回值数据类型填成相应的数据类型,并将返回值返回给调用者。
- 无返回值的函数:无返回值的函数我们在定义函数时,需要在函数的返回值数据类型填为void,表示为无返回值,且在函数的最后应该用return null;来结尾(函数遇到return才表示运行结束)。
3、按定义者分类
- 官方函数:官方打包主JDK里本身自带的函数,例如经常使用的System.out.print();
- 第三方函数:第三方科技公司自己创建的函数,由于Java是开源的,所以会有成千上万的第三方函数。
- 自定义函数:由用户自己定义函数,我们一整篇文章都在讲这个
局部变量
先说结论:我们在自定义函数中定义的临时变量叫做局部变量,这个局部变量可以和其他函数中的变量重名,而且这个变量也无法在函数运行结束后对其他函数产生影响。
现在我们用一个例子来更直观的体现这个概念
public class fun02{
public static main(String[] args){
int a = 100 ;//定义了一个main中的局部变量a
test01(a);
System.out.println(a);
}
public static void test01(int num){
int a =10;//定义了一个test01函数中的局部变量a
a += 100;
System.out.println(a);
a += num;
System.out.println(a);
System.out.println(num++);
}
}//请问这个函数输出的结果是什么??
这个程序的结果是:110、210、101、100,注意程序运行的顺序根据主函数而定,所以是先调用了函数test01再执行了main主函数中的打印命令,这解释了这道题答案的同时,也为我们引出了下一节的内容,函数中调用内存的模型。
函数中调用内存的模型
这很重要,函数在内存中是如何调用的,在内存中,我们的程序运行在内存的“栈”区域,在这个区域中的数据存储模型是这样的
这种首先去除最近添加的数据的方法称为“后进先出”(Last In First Out),或称“LIFO”。
在我们的函数调用的时候,数据会即时压栈进行运算,运算完后弹栈离开,所以函数内定义的局部变量不会相互影响。
函数重载(overload)
函数重载就是若干个函数拥有相同的函数名称,但是参数的数据类型不同或者参数的个数不同让这些函数成为了相互独立的函数(即使它们的名称相同)。使用函数重载技巧可以提高好代码的利用率,精简代码。ps:只有强类型编程语言才存在函数重载。
回到主题,举个例子:
public class test03{
public static main(String[] args){
fun01(1.0,2.0);
fun01(3);
fun01(4,5);
}
public static void fun01(int a){
System.out.println("正在执行函数1");
}
public static void fun01(int a,int b){
System.out.println("正在执行函数2");
}
public static void fun01(double a,double b){
System.out.println("正在执行函数3");
}
}
这个例子显示的结果是:正在执行函数3、正在执行函数2、正在执行函数1
递归
Java中递归的定义就是一个函数自己调用自身。这是一种解决问题的思想,递归思想能够把复杂的问题大幅简单化,但同时也会带来另一个问题,消耗大量的内存占用,因为在函数中调用函数,数据就会不停的压入栈中(翻看上一小节),必须要等整个函数调用结束后才会依次弹出,在这个过程中会占用大量内存。现在我们来看看递归思想的优点。
public class test03{
public static main(String[] args){
System.out.println(getCount1());
System.out.println(getCount2());
}
public static int getCount1(int n){
int count =0;
for(int i=1; i<=n; i++){
count +=i ;
}
return count;//
}
public static int getCount2(int n){
return n + getCount2(n-1);
}
}
只用了一行代码就完成了d为+1的等差数列求和,可见递归思想的强大。
笔者现在把与本篇关联的思维导图放出来,方便大家理解。