方法的基本用法
简单来说方法:方法就是一个代码片段,类似于 C 语言中的函数
- 存在的意义:
- 能够模块化的组织代码
- 做到让代码被重复使用,一份代码可以用在多个位置
- 让代码更好理解、更简单
- 直接调用现有方法,不必重复造轮子
定义语法
- 基本语法
// 方法定义 public static 方法返回值 方法名称([参数类型 形参 ...]){ 方法体代码; [return 返回值]; } // 方法调用 返回值变量 = 方法名称(实参...);
- 示例:整数相加方法实现和调用
class Test { public static void main(String[] args) { int a = 10; int b = 20; // 方法的调用 int ret = add(a, b); System.out.println("ret = " + ret); } // 方法的定义 public static int add(int x, int y) { return x + y; } } // 执行结果 ret = 30
注意事项
定义方法时,不会执行代码,调用时才执行
方法定义时,参数可以没有,但如果有一定要指定类型
方法定义时,返回值可以没有,但如果没有,返回类型应该写出 void
当方法被调用时,会将实参赋值给形参(一份临时拷贝)(方法定义时的参数为“形参”,方法调用时的参数为“实参”)
当方法执行时遇到 return 语句,方法结束,不会往下执行方法语句
方法定义必须在类中,一个方法可以被多次调用
方法的定义可以写在 main 方法的上方或者下方(Java 中没有“函数声明”的概念)
方法的调用需要开辟栈帧,方法结束栈帧就随即结束
实参和形参
同样的与C语言一样的是:当方法被调用时,会将实参赋值给形参(一份临时拷贝)
示例:
public class Test{ public static void main(String[] args){ int a = 10; int b = 20; swap(a,b); System.out.println("a = " + a " b = " + b); } public static void swap(int a, int b){ int tmp = a; int a = b; int b = tmp; } } // 运行结果为 a = 10 b = 20
注:swap 方法里只将形参的值互换了,但是实参的值没有互换
在 Java 中想做到修改实参的值我们需要用到引用类型(引用可以理解为"地址")
- 示例:使用数组来实现交换数值
public class Test{ public static void main(String[] args){ int[] arr = {10, 20}; swap(arr); System.out.println("a = " + arr[0] " b = " + arr[1]); } public static void swap(int[] arr){ int tmp = arr[0]; arr[0] = arr[1]; arr[1] = tmp; } } // 运行结果为 a = 20 b = 10
方法的重载
什么是重载
定义:同一个方法名字相同,提供不同类型的实现,称为重载
规则:
- 方法名相同
- 参数列表不同(参数的个数或者参数的类型不同)
- 返回值不作要求
重载要解决的问题
- 示例:两个数据求和
public class Test{ public static void main(String[] args){ int a1 = 10; int b1 = 20; int ret1 = add(a1, b1); System.out.println("ret1 = " + ret1); double a2 = 1.5; double b2 = 2.4; double ret2 = a2 + b2; System.ouy.println("ret2 =" + ret2); } public static int addInt(int a, int b){ return a + b; } public static double addDouble(double a, double b){ return a + b; } }
对于求不同类型的数据和,在上面我们写了addInt和addDouble两个方法,但是 Java 认为 addInt 这样的名字不友好,不如直接就叫 add,由此重载便派上用场了
- 求和重载:
class Test { public static void main(String[] args) { int a = 10; int b = 20; int ret = add(a, b); System.out.println("ret = " + ret); double a2 = 10.5; double b2 = 20.5; double ret2 = add(a2, b2); System.out.println("ret2 = " + ret2); double a3 = 10.5; double b3 = 10.5; double c3 = 20.5; double ret3 = add(a3, b3, c3); System.out.println("ret3 = " + ret3); } 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; } }
方法递归
什么是递归
定义:一个方法在执行过程中调用自身,就称为“递归”
简单来看递归即是"递推和回归":满足条件时进行递推调用方法,不满足时开始回归
对于没有限制条件的方法调用,则会出现栈溢出(方法的调用会开辟空间,而内存是有限的)
递归执行过程分析
示例:求N的阶乘
public static void main(String[] args) { int n = 5; int ret = factor(n); System.out.println("ret = " + ret); } public static int factor(int n) { System.out.println("函数开始, n = " + n); if (n == 1) { System.out.println("函数结束, n = 1 ret = 1"); return 1; } int ret = n * factor(n - 1); System.out.println("函数结束, n = " + n + " ret = " + ret); return ret; } // 执行结果 函数开始, n = 5 函数开始, n = 4 函数开始, n = 3 函数开始, n = 2 函数开始, n = 1 函数结束, n = 1 ret = 1 函数结束, n = 2 ret = 2 函数结束, n = 3 ret = 6 函数结束, n = 4 ret = 24 函数结束, n = 5 ret = 120 ret = 120
- 执行过程图:
递归练习
- 例1 按顺序打印一个数字的每一位(例如 1234 打印出 1 2 3 4)
public static void print(int num) { if (num > 9) { print(num / 10); } System.out.println(num % 10); }
- 例2 递归求 1 + 2 + 3 + ... + 10
public static int sum(int num) { if (num == 1) { return 1; } return num + sum(num - 1); }
- 例3 写一个递归方法,输入一个非负整数,返回组成它的数字之和. 例如,输入
1729, 则应该返回1+7+2+9, 它的和是19
public static int sum(int num) { if (num < 10) { return num; } return num % 10 + sum(num / 10); }
- 例4 求斐波那契数列的第 N 项
public static int fib(int n) { if (n == 1 || n == 2) { return 1; } return fib(n - 1) + fib(n - 2); }
当我们求数字较大的项时, 程序执行速度极慢(进行了大量的重复运算)
可以使用循环的方式来求斐波那契数列问题, 避免出现冗余运算
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; }
什么时候使用:问题递归非递归都能写且没有多大的问题时,选择递归
总结递归特点
优点:
1. 简洁
2.在树的前序,中序,后序遍历算法中,递归的实现明显要比循环简单得多
缺点:
1.递归由于是函数调用自身,而函数调用是有时间和空间的消耗的:每一次函数调用,都需要在内存栈中分配空间以保存参数、返回地址以及临时变量,而往栈中压入数据和弹出数据都需要时间。->效率
2.递归中很多计算都是重复的,由于其本质是把一个问题分解成两个或者多个小问题,多个小问题存在相互重叠的部分,则存在重复计算,如fibonacci斐波那契数列的递归实现。->效率
3.调用栈可能会溢出,其实每一次函数调用会在内存栈中分配空间,而每个进程的栈的容量是有限的,当调用的层次太多时,就会超出栈的容量,从而导致栈溢出。->性能