【javaSE】java函数(方法)重载及函数递归
文章目录
函数的概念
什么是函数,在java中,函数也叫方法。
函数就是一个代码片段,在程序中,我们可能经常要去实现某个功能,我们可以把这个功能实现代码单独打包起来,需要用的时候直接去调用该函数,不需要再打一次代码。
总结起来就是:
- 是能够模块化的组织代码(当代码规模比较复杂的时候).
- 做到代码被重复使用, 一份代码可以在多个位置使用.
- 让代码更好理解更简单.
- 直接调用现有方法开发, 不必重复造轮子.
比如,我们判断一个数是奇数还是偶数
正常代码是这样
public static void main2(String[] args) { int a=3; if(a%2==0){ System.out.println(a+"是偶数"); }else{ System.out.println(a+"是奇数"); } }
那么怎么用函数实现呢
函数定义
基本语法
// 函数定义
修饰符 返回值类型 方法名称([参数类型 形参 …]){
函数体代码;
[return 返回值];
}
还是来判断一个数是奇数还是偶数
//主函数public class Test2 { public static void main(String[] args) { int a=3; evenOrOdd(3); } //判断奇偶数函数 public static void evenOrOdd(int a){ if(a%2==0){ System.out.println(a+"是偶数"); }else{ System.out.println(a+"是奇数"); } }
运行结果
函数命名
函数名字一般采用小驼峰命名
在实际开发中,函数命名一般都是英文命名,最好别用中文拼音命名,(可能会被鄙视看不起…)那什么是小驼峰呢,如上面判断是偶数或者奇数 evenOrOdd 第一个单词首字母小写,后面单词首字母大写。
注意
- 修饰符:现阶段直接使用public static 固定搭配
- 返回值类型:如果函数有返回值,返回值类型必须要与返回的实体类型一致,如果没有返回值,必须写成void
- 函数名字:采用小驼峰命名
- 参数列表:如果函数没有参数,()中什么都不写,如果有参数,需指定参数类型,多个参数之间使用逗号隔开
- 函数体:函数内部要执行的语句
- 在java当中,函数必须写在类当中
- 在java当中,函数不能嵌套定义
- 在java当中,函数方法声明一说
函数调用的执行过程
【函数调用过程】
调用函数—>传递参数—>找到函数地址
—>执行被调函数的函数体—>被调函数结束返回
—>回到主调函数继续往下执行
【注意事项】
定义方法的时候, 不会执行方法的代码. 只有调用的时候才会执行.
一个方法可以被多次调用.
代码示例: 计算 1! + 2! + 3! + 4! + 5!
public class TestMethod { public static void main(String[] args) { int sum = 0; for (int i = 1; i <= 5; i++) { //一次循环得到一个阶乘结果,一个一个加起来sum += fac(i); }System.out.println("sum = " + sum); }//计算某个数的阶乘public static int fac(int n) { System.out.println("计算 n 的阶乘中n! = " + n); int result = 1; for (int i = 1; i <= n; i++) { result *= i; }return result; } }// 执行结果 计算 n 的阶乘中 n! = 1 计算 n 的阶乘中 n! = 2 计算 n 的阶乘中 n! = 3 计算 n 的阶乘中 n! = 4 计算 n 的阶乘中 n! = 5 sum = 153
实参和形参的关系(重要)
形参是用来接收实参的值,形参的名字可以随意取,对函数都没有任何影响,形参只是函数在定义时需要借助的一个变量,用来保存函数在调用时传递过来的值。
看下面的一个代码
public class TestMethod { public static void main(String[] args) { int a = 10; int b = 20; swap(a, b); System.out.println("main: a = " + a + " b = " + b); }public static void swap(int x, int y) { int tmp = x; x = y; y = tmp; System.out.println("swap: x = " + x + " y = " + y); } }// 运行结果 swap: x = 20 y = 10 main: a = 10 b = 20
结果·为什么是这样呢?
实参a和b是main方法中的两个变量,其空间在main方法的栈(一块特殊的内存空间)中,而形参x和y是swap函数中的
两个变量,x和y的空间在swap方法运行时的栈中,因此:实参a和b 与 形参x和y是两个没有任何关联性的变量,在
swap方法调用时,只是将实参a和b中的值拷贝了一份传递给了形参x和y,因此对形参x和y操作不会对实参a和b产
生任何影响。
注意:对于基础类型来说, 形参相当于实参的拷贝. 即 传值调用
函数重载
什么是函数重载呢
在编程中,我们常常需要写函数实现某些功能,而有些函数的实现功能是一样的,但参数可能不一样,我们可以给这些函数命名相同,但参数不一样,这就是函数重载。
总结:在Java中,如果多个函数的名字相同,参数列表不同,则称该几种函数被重载了。
如下代码,
public class TestMethod { public static void main(String[] args) { add(1, 2); // 调用add(int, int) add(1.5, 2.5); // 调用add(double, double) add(1.5, 2.5, 3.5); // 调用add(double, double, double) }public static int add(int x, int y) { return x + y; } public static double add(double x, double y) { return x + y; }public static double add(double x, double y, double z) { return x + y + z; } }
为了返回某几个变量之和,可能需要返回两个int类型数据之和,或者三个int类型数据之和,或者又是浮点型数据之和,我们重载了add这个函数,这几个add函数功能都是一样的,但是参数的个数,类型,顺序会有所不同。
参数顺序不同,例如这个add(int a,double b);
———————————add(double a, int b);
注意:
- 方法名必须相同
- 参数列表必须不同(参数的个数不同、参数的类型不同、类型的次序必须不同)
- 与返回值类型是否相同无关
- 编译器在编译代码时,会对实参类型进行推演,根据推演的结果来确定调用哪个方法
函数可以重载的根本原因
在同一个作用域中不能定义两个相同名称的标识符。 比如函数中不能定义两个名字一样的变量,那为什么类中就可以定义方法名相同的方法呢?
编译器在编译Java程序时,会修改函数的名字,即是函数签名
函数签名即:经过编译器编译修改过之后方法最终的名字。具体方式:方法全路径名+参数列表+返回值类型,构成方
法完整的名字
如下图,函数double add(double, double);的函数签名(方法签名)
函数递归
生活中的故事
从前有坐山,山上有座庙,庙里有个老和尚给小和尚将故事,讲的就是:
"从前有座山,山上有座庙,庙里有个老和尚给小和尚讲故事,讲的就是:
“从前有座山,山上有座庙…”
“从前有座山……”
上面的两个故事有个共同的特征:自身中又包含了自己,该种思想在数学和编程中非常有用,因为有些时候,我们遇到的问题直接并不好解决,但是发现将原问题拆分成其子问题之后,子问题与原问题有相同的解法,等子问题解决之后,原问题就迎刃而解了。
函数递归概念
一个方法在执行过程中调用自身, 就称为 “递归”.
递归相当于数学上的 “数学归纳法”, 有一个起始条件, 然后有一个递推公式.,最重要的还有结束递归的条件
如 求出某个数的阶乘
public static void main(String[] args) { int n = 5; int ret = factor(n); System.out.println("ret = " + ret); }public static int factor(int n) { if (n == 1) { return 1; }return n * factor(n - 1); // factor 调用函数自身 }// 执行结果 ret = 120
程序执行过程
递归递归,就是先递出去,再归回来,如上图,factor(n)一个一个递出去(红线),最后遇到结束条件n==1,在一个一个归回来(绿线)
注意
- 将原问题划分成其子问题,注意:子问题必须要与原问题的解法相同
- 递归出口(即是结束递归条件)
斐波那契数列
斐波那契数列就是 1,1, 2,3,5,8,13…
第一二项都是1,接下来每一项都等于前两项之和
函数求出斐波那契数列第n项,是比较经典的递归题目练习;
斐波那契数列第n项 fib(n)=fib(n-1)+fib(n-2)
求出第五项斐波那契数就是5
代码如下
public static int fib(int n) { if (n == 1 || n == 2) { return 1; }return fib(n - 1) + fib(n - 2); }
当我们求 fib(36) 的时候发现, 程序执行速度极慢. 原因是进行了大量的重复运算,如上图所示
因此可以使用循环的方式来求斐波那契数列问题, 避免出现冗余运算.
如下代码,此时程序的执行效率大大提高了.
public static int fib(int n) { int last2 = 1; int last1 = 1; int cur = 0; for (int i = 3; i <= n; i++) { cur = last1 + last2; last2 = last1; last1 = cur; }return cur; }