Java方法的参数传递机制与递归总结

1、方法的参数传递机制

1.1、形参和实参

若方法含有参数:

  • 形参(formal parameter):在定义方法时方法名后面括号中声明的变量称为形式参数(简称形参)即形参出现在方法定义时。
  • 实参(actual parameter):调用方法时方法名后面括号中的使用的值/变量/表达式称为实际参数(简称实参)即实参出现在方法调用时。
1.2、 参数传递机制:值传递

Java里方法的参数传递方式只有一种:值传递。 即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响。

  • 形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参

  • 形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参

1.3、 举例

1、形参是基本数据类型

案例:编写方法,交换两个整型变量的值

public class ValueTransferTest1 {
	
	public static void main(String[] args) {
		
		
		int m = 10;
		int n = 20;
		
		System.out.println("m = " + m + ", n = " + n);
		//交换m和n的值
//		int temp = m;
//		m = n;
//		n = temp;
		
		ValueTransferTest1 test = new ValueTransferTest1();
		test.swap(m, n);
		
		System.out.println("m = " + m + ", n = " + n);
	}
	
	public void swap(int m,int n){
		int temp = m;
		m = n;
		n = temp;
	}
	
}

内存解析:

在这里插入图片描述

2、形参是引用数据类型

public class ValueTransferTest2 {
	public static void main(String[] args) {
		
		Data d1 = new Data();
		d1.m = 10;
		d1.n = 20;
		
		System.out.println("m = " + d1.m + ", n = " + d1.n);
		
		//实现 换序
		
		ValueTransferTest2 test = new ValueTransferTest2();
		test.swap(d1);
		
		System.out.println("m = " + d1.m + ", n = " + d1.n);
		
	}
	
	public void swap(Data data){
		int temp = data.m;
		data.m = data.n;
		data.n = temp;
	}
}
class Data{
	int m;
	int n;
}

内存解析:

在这里插入图片描述

1.4 练习

练习1:判断如下程序输出的结果

public class AssignNewObject {
    public void swap(MyData my){
        my = new MyData(); //考虑堆空间此新创建的对象,和main中的data对象是否有关
        int temp = my.x;
        my.x = my.y;
        my.y = temp;
     
    }

    public static void main(String[] args) {
        AssignNewObject tools = new AssignNewObject();
        
        MyData data = new MyData();
        data.x = 1;
        data.y = 2;
        System.out.println("交换之前:x = " + data.x +",y = " + data.y);//
        tools.swap(data);//调用完之后,x与y的值交换?
        System.out.println("交换之后:x = " + data.x +",y = " + data.y);//
    }
}

class MyData{
    int x ;
    int y;
}

练习2:如下操作是否可以实现数组排序

public class ArrayTypeParam {

    //冒泡排序,实现数组从小到大排序
    public void sort(int[] arr){
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if(arr[j] > arr[j+1]){
                    int temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
    }
    //打印数组的元素
    public void print(int[] arr){
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i]+" ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        ArrayTypeParam tools = new ArrayTypeParam();

        int[] nums = {4,3,1,6,7};
        System.out.println("排序之前:");
        tools.print(nums);

        tools.sort(nums);//对nums数组进行排序

        System.out.println("排序之后:");
        tools.print(nums);//输出nums数组的元素

    }
}

练习3:通过内存结构图,写出如下程序的输出结果

//栈:每个方法在调用时,都会有以栈帧的方法压入栈中。栈帧中保存了当前方法中声明的变量:方法内声明的,形参
//堆:存放new出来的"东西":对象(成员变量在对象中)、数组实体(数组元素)。 
//注意:变量前如果声明有类型,那么这就是一个新的刚要定义的变量。如果变量前没有声明类型,那就说明此变量在之前已经声明过。
public class TransferTest3 {
    public static void main(String args[]) {
        TransferTest3 test = new TransferTest3();
        test.first();
    }
    public void first() {
        int i = 5;
        Value v = new Value();
        v.i = 25;
        second(v, i);
        System.out.println(v.i);
    }
    public void second(Value v, int i) {
        i = 0;
        v.i = 20;
        Value val = new Value();
        v = val;
        System.out.println(v.i + " " + i);
    }
}

class Value {
    int i = 15;
}

内存解析:

在这里插入图片描述

练习4:貌似是考查方法的参数传递

在这里插入图片描述

	//法一:
    public static void method(int a, int b) {
        // 在不改变原本题目的前提下,如何写这个函数才能在main函数中输出a=100,b=200? 
        a = a * 10;
        b = b * 20;
        System.out.println(a);
        System.out.println(b);
        System.exit(0);
    }

    //法二:
    public static void method(int a, int b) {

        PrintStream ps = new PrintStream(System.out) {
            @Override
            public void println(String x) {

                if ("a=10".equals(x)) {
                    x = "a=100";
                } else if ("b=10".equals(x)) {
                    x = "b=200";
                }
                super.println(x);
            }
        };

        System.setOut(ps);

    }

练习5:将对象作为参数传递给方法

(1)定义一个Circle类,包含一个double型的radius属性代表圆的半径,一个findArea()方法返回圆的面积。
(2)定义一个类PassObject,在类中定义一个方法printAreas(),该方法的定义如下:public void printAreas(Circle c, int time),在printAreas方法中打印输出1到time之间的每个整数半径值,以及对应的面积。例如,times为5,则输出半径1,2,3,4,5,以及对应的圆面积。
(3)在main方法中调用printAreas()方法,调用完毕后输出当前半径值。程序运行结果如图所示。

在这里插入图片描述

2、 递归(recursion)方法

举例1:

在这里插入图片描述

举例2:

从前有座山,山上有座庙,庙里有个老和尚,老和尚在给小和尚讲故事,讲的啥?
      从前有座山,山上有座庙,庙里有个老和尚,老和尚在给小和尚讲故事,讲的啥?
          从前有座山,山上有座庙,庙里有个老和尚,老和尚在给小和尚讲故事,讲的啥?
              从前有座山,山上有座庙,庙里有个老和尚,老和尚在给小和尚讲故事,讲的啥?...
    
老和尚没了,庙塌了,小和尚还俗结婚了。

递归方法调用:方法自己调用自己的现象就称为递归。

**递归的分类:**直接递归、间接递归。

  • 直接递归:方法自身调用自己。

    public void methodA(){
    	methodA();
    }
    
  • 间接递归:可以理解为A()方法调用B()方法,B()方法调用C()方法,C()方法调用A()方法。

    public static void A(){
    	B();
    }
    
    public static void B(){
    	C();
    }
    
    public static void C(){
    	A();
    }
    

说明

  • 递归方法包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。
  • 递归一定要向已知方向递归,否则这种递归就变成了无穷递归,停不下来,类似于死循环。最终发生栈内存溢出

举例:

举例1:计算1 ~ n的和

public class RecursionDemo {
	public static void main(String[] args) {
        RecursionDemo demo = new RecursionDemo();
		//计算1~num的和,使用递归完成
		int num = 5;
      	// 调用求和的方法
		int sum = demo.getSum(num);
      	// 输出结果
		System.out.println(sum);
		
	}
  	/*
  	  通过递归算法实现.
  	  参数列表:int 
  	  返回值类型: int 
  	*/
	public int getSum(int num) {
      	/* 
      	   num为1时,方法返回1,
      	   相当于是方法的出口,num总有是1的情况
      	*/
		if(num == 1){
			return 1;
		}
      	/*
          num不为1时,方法返回 num +(num-1)的累和
          递归调用getSum方法
        */
		return num + getSum(num-1);
	}
}

代码执行图解:

在这里插入图片描述

举例2:递归方法计算n!

public int multiply(int num){
	if(num == 1){
		return 1;
	}else{
		return num * multiply(num - 1);
	}
}

在这里插入图片描述

举例3:已知有一个数列:f(0) = 1,f(1) = 4,f(n+2)=2*f(n+1) + f(n),其中n是大于0的整数,求f(10)的值。

public int f(int num){
	if(num == 0){
		return 1;
	}else if(num == 1){
		return 4;
	}else{
		return 2 * f(num - 1) + f(num - 2);
	}
}

举例4:已知一个数列:f(20) = 1,f(21) = 4,f(n+2) = 2*f(n+1)+f(n),其中n是大于0的整数,求f(10)的值。

public int func(int num){
	if(num == 20){
		return 1;
	}else if(num == 21){
		return 4;
	}else{
		return func(num + 2) - 2 * func(num + 1);
	}
}

举例5:计算斐波那契数列(Fibonacci)的第n个值,斐波那契数列满足如下规律,

1,1,2,3,5,8,13,21,34,55,....

即从第三个数开始,一个数等于前两个数之和。假设f(n)代表斐波那契数列的第n个值,那么f(n)满足:
f(n) = f(n-2) + f(n-1);

	//使用递归的写法
    int f(int n) {//计算斐波那契数列第n个值是多少
        if (n < 1) {//负数是返回特殊值1,表示不计算负数情况
            return 1;
        }
        if (n == 1 || n == 2) {
            return 1;
        }
        return f(n - 2) + f(n - 1);
    }

    //不用递归
    int fValue(int n) {//计算斐波那契数列第n个值是多少
        if (n < 1) {//负数是返回特殊值1,表示不计算负数情况
            return 1;
        }
        if (n == 1 || n == 2) {
            return 1;
        }
        //从第三个数开始,  等于 前两个整数相加
        int beforeBefore = 1; //相当于n=1时的值
        int before = 1;//相当于n=2时的值
        int current = beforeBefore + before; //相当于n=3的值
        //再完后
        for (int i = 4; i <= n; i++) {
            beforeBefore = before;
            before = current;
            current = beforeBefore + before;
            /*
            假设i=4
                beforeBefore = before; //相当于n=2时的值
                before = current; //相当于n=3的值
                current = beforeBefore + before; //相当于n = 4的值
            假设i=5
                beforeBefore = before; //相当于n=3的值
                before = current; //相当于n = 4的值
                current = beforeBefore + before; //相当于n = 5的值
                ....
             */
        }
        return current;
    }

举例6:面试题

面试,遇到一个一个双重递归调用的问题,我琢磨了一下,完全不知道为什么。打断点了,也还是没看懂为什么程序会那样走。您有空可以看一下,求指教。

在这里插入图片描述

	private int count = 0;

    public int recursion(int k) {
        count++;
        System.out.println("count1:" + count + "  k:" + k);
        if (k <= 0) {
            return 0;
        }
        return recursion(k - 1) + recursion(k - 2);//287
        //return recursion(k - 1);//11
        //return recursion(k - 1) + recursion(k - 1);//2047
    }

剖析:

在这里插入图片描述

总结说两句:

  1. 递归调用会占用大量的系统堆栈,内存耗用多,在递归调用层次多时速度要比循环慢的多,所以在使用递归时要慎重。

  2. 在要求高性能的情况下尽量避免使用递归,递归调用既花时间又耗内存。考虑使用循环迭代

  • 13
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值