JavaSE · 方法的使用 · 方法重载 · 方法递归 · 斐波那契数列第N项 · 递归求和

本文详细介绍了Java中的方法,包括其基本用法,如定义、调用和参数关系。讲解了方法的重载,如何通过不同的参数实现方法的多版本。此外,还深入探讨了方法的递归概念及其执行过程,并给出了多个递归实例。最后,文章强调了在选择递归和循环解决编程问题时的效率考虑。
摘要由CSDN通过智能技术生成

一、方法的基本用法

什么是方法(method)

方法就是一个代码片段,类似于 C 语言中的 “函数”.

方法的存在意义:
1. 能够模块化的组织代码.
2. 做到代码被反复使用,一份代码可以在多个位置使用.
3. 让代码更好理解.
4. 直接调用存在的方法开发,不必重复造轮子.

方法定义语法

基本语法

// 方法定义
public static 方法返回值 方法名称([参数类型 形参 ...]){
	方法体代码;
	[return 返回值];
}

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

代码示例:实现一个方法实现两个整数相加

public class Test01 {
    public static int add(int x, int y){
        return x + y;
    }

    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        int ret = add(a,b);
        System.out.println(ret);
    }
}

注意事项

  1. public 和 static 两个关键字在此处具有特定含义.
  2. 方法定义的时候,参数可以没有,每个参数要指定类型.
  3. 方法定义的时候,可以没有返回值,返回值类型要写成 void.
  4. 方法定义的参数称为 “形式参数”,方法调用传递进入的参数称为 “实际参数”.
  5. 方法的定义必须要写在类之中,在调用位置的上方或者下方都可以.
  6. Java 中没有 “函数声明” 这样的概念.

方法调用的执行过程

基本规则

  1. 定义方法的时候,不会执行方法的代码,只有在调用的时候执行.
  2. 当方法调用的时候,会把实际参数传递给形式参数.
  3. 参数传递完毕,才能执行方法的代码.
  4. 当方法执行完毕之后(遇到 return 语句), 就执行完毕, 回到方法调用位置继续往下执行.
  5. 一个方法可以被调用多次.

代码示例1:计算两个整数相加

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

原因分析
上面这段代码没有完成数据的交换.
对于基础类型来说,形参相当于实参的拷贝. 即 传值调用

类似于:

int a = 10;
int b = 20;

int x = a;
int y = b;

int tmp = x;
x = y;
y = tmp;

对 x 和 y 的修改,不影响 a 和 b.

解决方法:基本类型不行,就使用引用类型参数

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

没有返回值的方法

方法的返回值是可选的. 有些时候可以没有的.

代码示例

class Test {
	public static void main(String[] args) {
		int a = 10;
		int b = 20;
		print(a, b);
	}
	
	public static void print(int x, int y) {
		System.out.println("x = " + x + " y = " + y);
	}
}

之前交换两个整数的方法,也是没有返回值的.


二、方法的重载

有些时候我们需要用一个函数,同时兼容多种参数.
我们就可以使用到方法重载.

重载要解决的问题

代码示例

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);
	}
		
	public static int add(int x, int y) {
		return x + y;
	}
}

// 编译出错
Test.java:13: 错误: 不兼容的类型:double转换到int可能会有损失

由于参数类型不匹配,所以不能直接使用现有的 add 方法.

那么是不是需要创建这样的代码才能满足需求?
代码示例

class Test {
	public static void main(String[] args) {
		int a = 10;
		int b = 20;
		int ret = addInt(a, b);
		System.out.println("ret = " + ret);
		
		double a2 = 10.5;
		double b2 = 20.5;
		double ret2 = addDouble(a2, b2);
		System.out.println("ret2 = " + ret2);
	}
	public static int addInt(int x, int y) {
		return x + y;
	}
	public static double addDouble(double x, double y) {
		return x + y;
	}
}

这样的写法,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;
	}
}

方法的名字都叫 add. 但有的 add 是计算 int,有的是计算 double;有的是计算两个数字,有的是计算三个数字.

同一个方法名字,提供不同版本的实现,称为 方法重载.


重载的规则

1. 方法名相同.
2. 方法的参数不同(参数个数 或者 参数类型).
3. 方法的返回值类型不影响重载.

代码示例

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;
	}
	public static double add(int x, int y) {
		return x + y;
	}
}

// 编译出错
Test.java:13: 错误: 已在类 Test中定义了方法 add(int,int)

当两个方法的名字相同,返回值类型不同,但参数相同,不构成重载.


三、方法递归

递归的概念

一个方法在执行过程中调用自身,就称为 “递归”.

递归相当于数学上的 “数学归纳法”,有一个起始条件,然后有一个递推公式.

例如, 我们求 N!
起始条件: N = 1 的时候, N! 为 1. 这个起始条件相当于递归的结束条件.
递归公式: 求 N! , 直接不好求, 可以把问题转换成 N! => N * (N-1)!

代码示例:递归求 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) {
	  if (n == 1) {
	    return 1;
	 }
	  return n * factor(n - 1); // factor 调用函数自身
}

// 执行结果
ret = 120

递归执行过程分析

递归的程序的执行过程不太好理解,要想理解递归,必须先理解清楚 “方法的执行过程”,尤其是 “方法结束后,回到调用位置继续向下执行”.

代码示例:递归求 N 的阶乘。

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

public static void main(String[] args) {
    int num = 3;
    int ret = fac(num);
    System.out.println(ret);
}

执行过程图
画得有点丑…
程序先执行红线,去找结束条件;再执行绿线,把结果 return 返回。
在这里插入图片描述

递归练习题1:按顺序打印一个数字的每一位.

 public static void print(int n){
     if (n > 9) {
         print(n / 10);
     }
     System.out.println(n % 10);
 }

递归练习题2:递归求 1 + 2 + 3 + … + 10.

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

递归练习题3:写一个递归方法,输入一个非负整数,返回组成它的数字之和. 例如,输入 1729, 则应该返回1+7+2+9,它的和是19.

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

递归练习题4:求斐波那契数列的第 N 项.

 //斐波那契数列的第 N 项
 //1 1 2 3 5 8
 public static int fib(int n){
     if (n < 3) {
         return 1;
     }
     return fib(n - 1) + fib(n - 2);
 }

当我们求 fib(40) 的时候发现,程序执行速度极慢. 原因是进行了大量的重复计算.

    //斐波那契数列的第 N 项
    //1 1 2 3 5 8
    public static int count = 0;
    public static int fib(int n){
        if (n < 3) {
            return 1;
        }
        if (n == 3) {
            count++;
        }
        return fib(n - 1) + fib(n - 2);
    }
    public static void main(String[] args) {
        int num = 40;
        System.out.println(fib(num));
        System.out.println(count);
    }

//执行结果
102334155
39088169		//fib(3) 重复执行了 3 千万次.

可以使用循环的方式来求斐波那契数列问题,避免出现冗余.

 public static int fib1(int n){
     if (n < 3) {
         return 1;
     }
     int tmp = 0;
     int last1 = 1;
     int last2 = 1;
     for (int i = 3; i <= n; i++){
         tmp = last1 + last2;
         last1 = last2;
         last2 = tmp;
     }
     return tmp;
 }

效率大大滴提升了.


递归小结

递归是一种重要的编程解决问题的方式.

有些问题天然就是使用递归方式定义的(例如斐波那契数列, 二叉树等), 此时使用递归来解就很容易.

有些问题使用递归和使用非递归(循环)都可以解决. 那么此时更推荐使用循环, 相比于递归, 非递归程序更加高效.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值