文章目录

  • 1. 方法的基本用法
  • 1.1 什么是方法
  • 1.2 方法的定义
  • 语法
  • 1.3 方法调用的过程
  • 规则(同C)
  • 1.4 实参与形参
  • 2. 方法的重载(Overload)
  • 2.1 方法重载存在的意义
  • 2.1 如何使用方法重载
  • 2.3 方法重载的规则
  • 3. 方法的递归
  • 3.1 递归的概念
  • 3.2 递归的执行过程
  • 3.3 练习
  • 例1:打印数字的每一位(1234)
  • 例2:求1~10的和
  • 例3:写一个递归方法,输入一个非负整数,返回组成它的数字之和.
  • 例4:求斐波那契数列第N项
  • 用循环实现求斐波那契数列第N项
  • 总结:

1. 方法的基本用法

1.1 什么是方法

C语言中的函数,在Java中叫做"方法"
方法的作用和意义(同C):

  • 让某一功能模块化,使得这个模块能够被复用,提高开发的效率
  • 提高代码的可读性(某个方法有具体的名称)

例子:如果一道题让我们求1~100的和,我们可以直接在主函数中写出来:

int sum = 0;
for (int i = 1; i <= 100 ; i++) {
    sum += i;
}
System.out.println(sum);

结果:5050

但如果要求我们写1~n的和呢?每次都要重新修改,未免有些麻烦了.所以我们可以使用方法来模块化这个累加的功能

1.2 方法的定义

语法

// 方法定义 
public static 方法返回值 方法名称([参数类型 形参 ...]){

//方法体代码;

[return 返回值];//视具体情况 
}

// 方法调用 返回值变量 = 方法名称(实参...);

以累加求和为例:

public class TestDemo {
    public static void main(String[] args) {
        int n = 100;
        int sum = Sum(n);//方法的调用
        System.out.println(sum);
    }

    public static int Sum(int n){
          //返回值类型  方法名  形参
        int sum = 0;
        for (int i = 1; i <= n ; i++) {
            sum += i;
        }
        return sum;//返回值
    }
}

结果:5050

方法的使用方法和C大致相同,区别在于:

  • 方法不需要另外声明,只要将方法写在类(public class)和main函数之间即可
  • public class的特定含义后续会学习

1.3 方法调用的过程

规则(同C)

  • 只有方法被调用,其代码才会被执行
  • 方法被调用时,形参是实参的一份临时拷贝.参数传递后才会执行方法的具体代码,是纵向执行的.
  • 遇到return语句(不论方法是否有返回值),方法都会结束执行,回到主函数的当前位置

1.4 实参与形参

例:交换两个整型变量的值

public static void main(String[] args) {
        int a = 1;
        int b = 2;
        Swap(a, b);
        System.out.println(a);
        System.out.println(b);
    }

    public  static void Swap(int a, int b){
        int tmp = a;
        a = b;
        b = tmp;
    }

结果:1 2

在学习C语言后,我们知道,这个函数并没有交换两个变量,得传地址才能交换.但Java中没有指针变量的概念,对于**基础类型,只能传值调用方法,形参是实参的一份临时拷贝
如何解决:传递
引用类型(区别于基础类型)**参数,比如数组

public static void main(String[] args) { 
        int[] arr = {1, 2}; 
        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; 
    }

结果:2 1

2. 方法的重载(Overload)

2.1 方法重载存在的意义

例:加法方法

public static void main(String[] args) {
        int a = 1;
        int b = 2;
        int ret1 = Add(a, b);
        System.out.println(ret1);

        float c = 1.5f;
        float d = 2.5f;
        float ret2 = Add(c, d);
        System.out.println(ret2);
    }

    public static int Add(int a, int b){
        return a + b;
    }

错误:java: 不兼容的类型: 从float转换到int可能会有损失

即使将ret2的值强转为float,编译器依然显示以上错误
原因是参数类型不匹配
方法重载的意义:

解决了同一个方法需要兼容多组不同的参数的问题

如何解决?

2.1 如何使用方法重载

public static void main(String[] args) {
        int a = 1;
        int b = 2;
        int ret1 = Add(a, b);
        System.out.println(ret1);

        float c = 1.5f;
        float d = 2.5f;
        float ret2 = Add(c, d);
        System.out.println(ret2);
    }

    public static int Add(int a, int b){
        return a + b;
    }
    public static float Add(float a, float b){
        return a + b;
    }

结果:
3
4.0

可以发现:调用的两个方法的名字是一样的,为什么编译器不会像C一样报错呢?
编译器是如何识别方法的?

  • 每一个方法都有自己的签名:

菜鸟教程中对重载的描述:

重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
注:参数类型列表即参数个数或类型

也就是说,方法的参数类型列表也是方法身份的一部分,不同于C,仅将函数名称作为函数的身份标识

2.3 方法重载的规则

在同一个类中

  • 方法名相同
  • 参数类型列表不同
  • 返回值类型不影响重载,可以改变

代码实例见2.1

附:Java 8语法规则–8.4.9. Overloading

3. 方法的递归

3.1 递归的概念

方法的递归同C中函数的递归,即函数自身调用自身.
需要注意的是,递归并不能无限地调用自身.递归在数学中的体现是"数学归纳法"
递归的条件:

  • 找到起始条件
  • *找到递推公式
  • *有一个不断趋近的(结束)条件,且存在一种简单情况,使得递归结束

例:求N!(假如N=3)
起始条件: N = 1!
结束条件 N = 3!
递推公式: N! = N * (N - 1)
如:3! = 3 * 2! = 3 * 2 * 1! = 3 * 2 * 1

例:用方法求N!

public static void main(String[] args) {
        int n = 5;

        int recursionNum = Recur(n);
        System.out.println(recursionNum);
    }

    public static int Recur(int n){
        if(n == 1)
            return 1;
        return n * Recur(n - 1);
    }

结果:120

3.2 递归的执行过程

代码示例见3.1例 执行过程图解:以计算3!为例

java 单独使用nacos可以吗_递归


需要注意的是:并不是变调用方法,边返回值(或执行该层对应的语句).

从这里可以体会到:我们口头上说的(即形象理解的)起始条件就是程序的终止条件,另一半反之.
例如:求3!
形象的理解:3! = 1 * 2 * 3
实际上程序是先把所有能够调用的地方全部调用完毕,才会返回值
3! = 3 * f(2)
= 3 * 2 * f(1)
= 3 * 2 * 1

小结

我们理解的起始和终止条件和程序真正地起始和终止条件是相反的

3.3 练习

例1:打印数字的每一位(1234)

起始条件:1234
终止条件:最后一位小于10(0~9)
递归公式:打印最后一位+去掉最后一位

public static void main(String[] args) {
        int n = 1234;
        printNum(n);
    }
    public static void printNum(int n){
        if( n > 9){
            printNum(n / 10);
        }
        System.out.print(n % 10 + " ");
    }

结果:1 2 3 4

此处体现了3.2提到的要点:方法调用到终止后,方法才会从最深的递归开始返回或者执行语句

例2:求1~10的和

起始条件:10
终止条件:1
递归公式:f(N) = N + f(N - 1)

public static void main(String[] args) {
        int n = 10;
        int num = addNum(n);
        System.out.println(num);
    }

    public static int addNum(int n){
        if( n == 1 ){
            return 1;
        }
        return n + addNum(n-1);
    }

结果:55

例3:写一个递归方法,输入一个非负整数,返回组成它的数字之和.

如1234,结果为1+2+3+4=10
起始条件:1234
终止条件:最后一位小于10(0~9)
递归公式:取出最后一位,边取边模10

public static void main(String[] args) {
        int n = 1234;
        int num = numAdd(n);

        System.out.println(num);
    }

    public static int numAdd(int n){
        if(n < 10){
            return n;
        }
        return n % 10 + numAdd(n / 10);
    }

结果:10

例4:求斐波那契数列第N项

假设N为5
起始条件:第1项为1,第2项为1
终止条件:同上
递归公式:从第三项开始,当前项等于前两项之和

public static void main(String[] args) {
        int n = 5;
        int num = fib(n);
        System.out.println(num);
    }

    public static int fib(int n){
        if(n == 1 || n == 2){
            return 1;
        }
        return fib(n - 1) + fib(n - 2);
    }

结果:5

补充:斐波那契数列用递归容易理解,但效率很低.

用循环实现求斐波那契数列第N项

思路:
利用递归公式:f(N) = N + f(N - 1),(N>2)
将三个数看成一个整体,每次循环走一步,三个数同时往后走一步
(这里很像三指针的迭代)

  • 需要注意三个数往后迭代的顺序(为什么?)
    求6!
public static void main(String[] args) {
        int num1 = 1;
        int num2 = 1;
        int num3 = 0;
        for(int i = 3; i <= 6; i++){//要从第三项开始
            int cur = num3;

            num3 = num1 + num2;
            num1 = num2;
            num2 = num3;
        }
        System.out.println(num3);
    }

ps:面试的时候不要写递归的方法

总结:

  1. Java中的方法实际上就是C语言中的函数.最大的不同点就在于方法的重载等(目前仅学习了重载),Java将形参类型列表也当做方法身份的标识
  2. 递归的要素需要在写之前就想好,磨刀不误砍柴工.起始/终止条件,递归条件
  3. 重点理解递归的调用和返回(没有返回就执行语句)是两个不同的方向.我形象的认为递归的调用方向是向下的,复杂程度取决于调用层次的深度,返回或执行语句是自下而上的,不断积累.实际上我们理解的递归是自下而上的.

日志

5/5/2022