JavaSE回顾(三):方法的基本用法以及重载和递归

一、方法的基本用法

方法就是指将一段代码封装在一个结构体之中,实现某种功能,可以被重复调用的代码块。

方法的意义

  • 模块化的组织代码,使程序变得更简短而清晰。
  • 有利于程序维护。
  • 可以提高程序开发的效率。
  • 提高了代码的重用性。

基本语法

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



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

方法中实参和形参的关系
形参:就是形式参数,用于定义方法的时候使用的参数,是用来接收调用者传递的参数的。形参只有在方法被调用的时候,虚拟机才会分配内存单元,在方法调用结束之后便会释放所分配的内存单元。
实参:就是实际参数,用于调用时传递给方法的参数。实参在传递给别的方法之前是要被预先赋值的。

代码示例1: 交换两个整型变量

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 

上面的swap方法是交换两个整数的值,但是在主函数中调用后a和b的值并没有发生改变,为什么呢?
因为对于基础类型来说, 形参相当于实参的拷贝,即 传值调用。

代码示例2:交换数组中两个整数的值

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 

在这段代码中可以发现arr[0]和arr[1]的值发生了交换,为什么这段代码可以发生交换呢?
因为在Java中数组元素的变量名保存的数组中元素的地址,交换实际上是改变了指针的指向,所以看起来是完成了数值的交换。

结论:

  • 当方法中参数是基本数据类型时实参和形参之间是按值传递
  • 方法中参数是引用类型 时实参和形参按引用传递

二、方法的重载

Java中方法的重载,是指若同一个类中,两个或两个以上方法名相同,但是调用的方法参数的个数、顺序或类型不同的方法,则称为方法的重载。

重载的规则
在同一个类中:

  • 方法名相同。
  • 方法的参数不同(参数个数或者参数类型,顺序不同)。
  • 方法的返回值类型不影响重载,相同或者不同都可以。

重载示例1

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);
    } 
    //第一个add方法,两个int类型相加
    public static int add(int x, int y) {
	return x + y;
    }
    //第二个add方法,两个double类型相加
    public static double add(double x, double y) {
	return x + y;
    }
    //第三个add方法,三个add类型相加
    public static double add(double x, double y, double z) {
	return x + y + z;
    }
} 

方法的名字都叫 add. 但是有的 add 是计算 int 相加, 有的是 double 相加; 有的计算两个数字相加, 有的是计算三个数字相加。调用时会根据传的参数的不同,自动调用合适的方法。
同一个方法名字, 提供不同版本的实现, 称为方法重载。

重载示例2

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)
public static double add(int x, int y) {
^
1 个错误 

当两个方法的名字相同, 参数也相同, 但是返回值不同的时候, 不构成重载。

关于方法重载和重写的区别:
1.方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性;
2.重载发生在一个类中,重写发生在子类与父类之间;
3.同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者顺序不同)则视为重载;重写要求子类被重写方法与父类被重写方法有相同的参数列表,有兼容的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。
4.重载对返回类型没有特殊的要求,不能根据返回类型进行区分。

三、方法的递归

如果一个方法在执行过程中调用了自己本身,就称作“递归”。

使用递归时的注意事项:
1.一定要有一个终止的条件,保证递归能够停止下来,否则就会发生内存溢出。
2. 在递归中虽然有限定条件,但是递归次数不能太多,否则也会发生栈内存溢出;
3. 构造方法禁止递归,因为构造方法是创建对象使用的,不能让对象一直创建下去把。

经典递归代码:求斐波那契数列的第 N 项
斐波那契数列为1、1、2、3、5、8、13、21、34……此数列从第3项开始,每一项都等于前两项之和,递推公式为F(n)=F(n-1)+F(n-2),n≥3,F(1)=1,F(2)=1。
代码:

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

代码很简洁,但是有一个问题,随着N变大,程序执行速度极慢,原因是进行了大量的重复运算。
比如可以用一段代码来测一下:

class Test {
    public static int count = 0;
    public static void main(String[] args) {
	System.out.println(fib(40));
	System.out.println(count);
    }
    public static int fib(int n) {
	if (n == 1 || n == 2) {
	return 1;
        }
        if (n == 3) { 
        count++;
        }
    return fib(n - 1) + fib(n - 2);
    }
}
// 执行结果
102334155
39088169 // fib(3) 重复执行了 3 千万 次

通过上面的代码可以看到计算到第40个时,仅仅是fib(3)就被重复执行了三千万次。可想而知运行速度有多慢。
对于上面的代码,可以改为使用循环的方式来求, 避免出现冗余运算。

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;
} 

此时程序的执行效率大大提高了 。

递归小结:

  • 优点:代码简洁、清晰,并且容易验证正确性。
  • 缺点:它的运行需要较多次数的函数调用,如果调用层数比较深,需要增加额外的堆栈处理,比如参数传递需要压栈等操作,会对执行效率有很大影响。但是,对于某些问题天然就是使用递归方式定义的(例如斐波那契数列, 二叉树等), 此时使用递归来解就很容易,使用非递归代码会很难看。有些问题使用递归和使用非递归(循环)都可以解决时. 那么此时更推荐使用循环, 因为相比于递归, 非递归程序更加高效。
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值