一、方法是什么?
1.1定义
方法就是一个代码片段. 类似于 C 语言中的 “函数”.
方法存在的意义(不要背, 重在体会):
- 是能够模块化的组织代码(当代码规模比较复杂的时候).
- 做到代码被重复使用, 一份代码可以在多个位置使用.
- 让代码更好理解更简单.
- 直接调用现有方法开发, 不必重复造轮子
1.2基本语法
// 方法定义
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
注意事项:
- public 和 static 两个关键字在此处具有特定含义, 我们暂时不讨论, 后面会详细介绍.
- 方法定义时, 参数可以没有. 每个参数要指定类型
- 方法定义时, 返回值也可以没有, 如果没有返回值, 则返回值类型应写成 void(比如主方法)
- 方法定义时的参数称为 “形参”, 方法调用时的参数称为 “实参”(调用add时候的x,y).
- 方法的定义必须在类之中, 代码书写在调用位置的上方或者下方均可.
- Java 中没有 “函数声明” 这样的概念(只有方法名称和参数,没有具体的实现)
1.3方法调用过程
基本规则:
定义方法的时候, 不会执行方法的代码. 只有调用的时候才会执行.
当方法被调用的时候, 会将实参赋值给形参.
参数传递完毕后, 就会执行到方法体代码.
当方法执行完毕之后(遇到 return 语句), 就执行完毕, 回到方法调用位置继续往下执行.
class Test {
public static void main(String[] args) {
int a = 10;
int b = 20;
System.out.println("第一次调用方法之前");
int ret = add(a, b);
System.out.println("第一次调用方法之后");
System.out.println("ret = " + ret);
System.out.println("第二次调用方法之前");
ret = add(30, 50);
System.out.println("第二次调用方法之后");
System.out.println("ret = " + ret);
}
public static int add(int x, int y) {
System.out.println("调用方法中 x = " + x + " y = " + y);
return x + y;
}
}
// 执行结果
一次调用方法之前
调用方法中 x = 10 y = 20
第一次调用方法之后
ret = 30
第二次调用方法之前
调用方法中 x = 30 y = 50
第二次调用方法之后
ret = 80

关于实参和形参(重点)
代码示例如下:
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 x, int y) {
int tmp = x;
x = y;
y = tmp;
}
}
// 运行结果
a = 10 b = 20


解决办法:引用数据类型
1.4方法返回值
比如图例要求1—100中第一个3的倍数,可以用return提前结束方法
二、 方法的重载
2.1定义(重点,需要记忆)
方法重载发生"在同一个类中",定义了若干个方法名称相同,参数列表不同(参数的类型或者个数不同)的一组方法,与返回值无关!!!这样的一组方法称为重载方法
下图第二个重载方法是参数的个数不相同。
2.2重载规则
针对同一个类:
方法名相同
方法的参数不同(参数个数或者参数类型)
方法的返回值类型不影响重载。
关于println的提示
没有返回值的方法是不可以用println去打印的
三、方法的递归
3.1定义
一个方法在执行内部过程中调用自身, 就称为 “递归”.
注意:递归不能只看名字去辨别(第二个方法内部调用的是max的重载后的方法,是两个方法。只有自己调用自己才可以)
程序能实现方法递归的核心原因∶当在程序中调用另一个函数时,当前程序会暂缓执行,直到调用函数调用结束,当前程序才会继续执行。
3.2调用递归的场景(重难点)

思维模式(重点掌握):
在写递归函数时,千万不要纠结这个函数内部到底咋实现,而要注意这个方法的语义(这个函数到底有什么功能),你就假设,这个方法已经别人写好了,你只是调用这个方法而已。
示例如下:
求5的阶乘:可以分成54321,符合递归思路拆成若干个子问题,终止条件就是拆到1就不能再求阶乘值了。这是思考问题的思路,同下面具体实现代码有区别。
求5的阶乘值(具体化,写代码):实际上就是把问题拆分成5*4的阶乘值,4的阶乘值交给子方法。
5就相当与num,4的阶乘值交给子方法factor(num-1),不要去想factor(num-1)怎么实现,假设这个方法已经被别人写好了,你就直接调用即可。
代码示例如下:函数的语义(传入一个正整数就能求出这个整数的阶乘值)

3.3递归过程分析
这个方法的语义,作用:传入一个正整数就可以求出1+2+…+num的和
拆分问题:num+递归的方法(num-1)
终止条件:num拆分到1时就是加1
public static int functor(int num){
//边界条件
if(num==1){
return 1;
}
//语义拼接
return num+functor(num-1);
}
}
练习1
写出一个方法,这个方法输入一个非负整数,返回组成这个数的数字之和。
例如1729 =1+7+2+9
public static int functor(int num){
//终止条件
//这个数如果是个个位数直接返回它的值
if(num<10){
return num;
}
//拆分问题
//我可以知道这个数的个位是多少,其它位不知道,交给子方法
//拆分为这个数的个位加上其他位之和
return num%10+functor(num/10);
}
}
递归展开图:
递是向下的红色剪头,归是返回的绿色箭头
练习2(重难点掌握,和常规递归思路不同)

难点:首先要找到最高位打印,其次还要打印低位(例子在代码倒数两行)
解析:语义,传入一个正整数,按顺序输出这个数字
这个方法没有返回值,应该关注打印内容,不用在意返回值,但它也有归的过程
public static void functor(int num) {
//终止条件,无需借助任何办法就能知道的结果
//num<10就是个位数,直接打印本身
if(num<10){
System.out.println(num+" ");
//return的原因是直接结束函数的调用
return;
}
//方法执行到这里知道起码num是个二位数
//根据方法的语义,不能先打印个位数
//所以除了个位之外的高位数,我们交给子方法去处理
functor(num/10);
//高位已经交给子方法打印好了,还需要打印当前个位
System.out.println(num%10+" ");
}
}
练习3(和练习2作对比)
/**
* 关注方法语义:传入一个正整数可以按低位到高位顺序打印
* @author hide_on_bush
* @date 2022/5/16
*/
public static void printNumReverse(int num){
//终止条件还是个位数
if(num<10){
System.out.println(num+" ");
return;
}
//现在只知道个位数,先打印个位数
System.out.println(num%10+" ");
//剩下的其他位置,交给子方法去处理
//与练习2刚好相反
printNumReverse(num/10);
}
}

3.4斐波那契数列
关于斐波那契数列介绍:
添加链接描述
注意方法的语义:传一个正整数求出该对应的斐波那契数的值,这个正整数代表的是在这个数列中,这个斐波那契数位于第几位。
public static int fib(int n) {
//终止条件
//该数列第一位和第二位都是1
if (n == 1 || n == 2) {
return 1;
}
//斐波那契数=当前位置的前两位之和
//关注语义,理解写法含义
return fib(n - 1) + fib(n - 2);
}
练习(重点,青蛙跳台阶,fibo的应用)
一只青蛙一次可以跳上 1 级台阶,也可以跳上2 级。求该青蛙跳上一个n 级的台阶总共有多少种跳法
可以画个图,当n=3时找规律
/**
* 关注方法语义:传入一个正整数n可以求出青蛙到这n层一共有几种可能性
* @author hide_on_bush
* @date 2022/5/16
*/
public static int Jump(int n){
//终止条件
//只剩最后一层台阶,一种跳法
//只剩两层,两种跳法
if(n==2||n==1){
return n;
}
//此时假设只剩最后一步到达第n层,n-1和n-2就是之前的台阶高度
//剩下最后一层和最后两层之前的情况交给子方法处理
//还是要关注方法的语义
return Jump(n-1)+Jump(n-2);
}
}
3.5汉诺塔问题

注意事项:
终止条件:就是只有一个盘子,直接移动到C
当n=1时
当n>=2时
只可能出现下面这三种情况,不可能有别的情况

/**
* 关注方法语义:传入n个盘子,按按汉诺塔规则移动
* A上的盘子移动到C,B作为辅助
* @author hide_on_bush
* @date 2022/5/16
*/
public static void hanoiTower(int nDisks,char A,char B,char C){
//终止条件一个盘子,一步到位,
//把盘子从A移到C上
if(nDisks==1){
move(nDisks,A,C);
return;
}
//核心1:把n-1上的小盘子从A移动到B(辅助作用)
//A是源头,B是终点
hanoiTower(nDisks-1,A,C,B);
//核心2:此时A上只剩第n个,也就是最大的盘子,再从A移动到C
move(nDisks,A,C);
//核心3:B上所有的小盘子移动到C上,A是辅助,不做任何操作
hanoiTower(nDisks-1,B,A,C);
}
/**
* 将编号n的盘子从Source移动到Dest
* @author hide_on_bush
* @date 2022/5/16
*/
private static void move(int nDisks, char sourceTower, char destTower) {
System.out.println("编号为"+nDisks+"从"+sourceTower+"移动到"+destTower);
}
总结:不要去纠结方法内部如何实现的,掌握核心语义,并结合图去分析



