黑马程序员技术交流社区

标题: 关于递归中的局部变量 [打印本页]

作者: mengxiang1993    时间: 2015-2-22 18:39
标题: 关于递归中的局部变量
如果递归调用,形参不是重名了吗?
我记得局部变量好像不可以重名的。
还有就是递归会造成栈内存和什么内存溢出呢?


作者: 万合天宜    时间: 2015-2-22 19:30
形参只是用来传递的吧,关键是实参不同,这块我也不太清楚哦。还有堆栈溢出是因为递归次数太多了,一般递归是不会的。
作者: mengxiang1993    时间: 2015-2-22 19:50
万合天宜 发表于 2015-2-22 19:30
形参只是用来传递的吧,关键是实参不同,这块我也不太清楚哦。还有堆栈溢出是因为递归次数太多了,一般递归 ...

形参就是局部变量啊,会在栈内存中声明变量,名字相同难道也可以,一般在一个方法内,声明相同的名字的局部变量,会报错的
作者: Ansel    时间: 2015-2-22 20:07
我是看评论的
作者: alvis2015    时间: 2015-2-22 20:46
mengxiang1993 发表于 2015-2-22 19:50
形参就是局部变量啊,会在栈内存中声明变量,名字相同难道也可以,一般在一个方法内,声明相同的名字的局 ...

我说说我的看法吧,不知道对不对,仅供参考:
形式类型:是你在定义方法时括号里面的参数。
如:public void setName(String name){this.name = name;}
name:就是形式参数。

实际类型:在调用方法是传进去的参数。
如: setName("张三");
"张三":就是实际类型。

形参首先是个参数,变量是一个值可以改变的量。
在递归方法中:
         方法括号内的整体叫做形参,这个参数作为一个变量只在方法中声明了一次。
         在递归调用的过程中,这个变量的值(即实际参数)传递给形参(即括号内)。
也就是说递归是在说一个过程,是一个自己调用自己的过程,而不是循环声明变量。

作者: aawenwei    时间: 2015-2-22 21:02
楼上说的有道理。。。。。。。。。。。
作者: mengxiang1993    时间: 2015-2-22 21:16
alvis2015 发表于 2015-2-22 20:46
我说说我的看法吧,不知道对不对,仅供参考:
形式类型:是你在定义方法时括号里面的参数。
如:public vo ...

那么造成栈内存溢出的原因是什么?难道不是一直在产生新的局部变量吗?
作者: fantacyleo    时间: 2015-2-22 21:26
每次递归调用,都会在栈中开辟新空间,用于保存新的局部变量值,栈的长度于是不断增长,超过一定长度就产生溢出了
作者: wdhm5423    时间: 2015-2-22 21:37
本帖最后由 wdhm5423 于 2015-2-22 21:53 编辑

递归过程,变量在内存中堆栈,难以释放。
  1. public class digui {
  2.         static String[] str={"1"};
  3.         public static void main(String[] args){
  4.                 byte[] b=new byte[1024*1024*4];//每次创建4M的字节
  5.                 main(str);//递归
  6.         }
  7. }
复制代码

以上例子中,递归每次创建4M字节,作为一个变量堆在内存中不能释放,我电脑4G,4G/4M=1024次,运行2、3秒报内存溢出。
异常:Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
这就是一个内存溢出的例子,递归次数太多会造成内存溢出,所以递归过程要考虑内存溢出问题(此处跟形参没有关系)。

作者: mengxiang1993    时间: 2015-2-22 21:46
wdhm5423 发表于 2015-2-22 21:37
递归过程,变量在内存中堆栈,难以释放。

以上例子中,递归每次创建4M字节,作为一个变量堆在栈内存中不能 ...

为什么跟形参没关系呢?形参属于局部变量,会在栈内存中开辟内存吧
作者: mengxiang1993    时间: 2015-2-22 21:47
fantacyleo 发表于 2015-2-22 21:26
每次递归调用,都会在栈中开辟新空间,用于保存新的局部变量值,栈的长度于是不断增长,超过一定长度就产生 ...

嗯,这会涉及到同一个方法局部变量重名吧,这个怎么解释?
作者: challenge    时间: 2015-2-22 21:48
我是来看答案的。
作者: wdhm5423    时间: 2015-2-22 21:57
mengxiang1993 发表于 2015-2-22 21:46
为什么跟形参没关系呢?形参属于局部变量,会在栈内存中开辟内存吧

我这边太懒了,直接用main递归,定义了个静态变量作为参数,每次调用不会另外开辟内存。
作者: wdhm5423    时间: 2015-2-22 22:37
本帖最后由 wdhm5423 于 2015-2-23 09:04 编辑

public class digui {
public static void main(String[] args) throws InterruptedException{
  hanshu(1);
}

public static int hanshu(int n) throws InterruptedException{
  System.out.println(n);
  int m=n+1;
  Thread.sleep(1000);//等一秒
  return hanshu(m);
  
}
}
重新写了个递归,引用五楼说的
在递归方法中:
         方法括号内的整体叫做形参,这个参数作为一个变量只在方法中声明了一次。
         在递归调用的过程中,这个变量的值(即实际参数)传递给形参(即括号内)。
形参只创声明了一次,其他都是实参的值传给形参。
上面代码中,你是否理解为局部变量m每次递归都会创建,所以重名?
这里局部变量的作用域只是在当前实参值m传给形参的函数内有效。下一次递归,就是换了一个实参值(m+1),重新定义的局部变量m也只是在当前实参值(m+1)传给形参的函数内有效,这就变成了两个不同的局部(也就是作用域),局部变量不重名是在同一个作用域内不重名,所以不存在重名的说法。

作者: sofeel    时间: 2015-2-23 00:12
一,先谈参数。
形参、实参都是参数,不同在于实参是有具体指向的引用,形参需要实参来激活。当实参把自己的引用对象介绍给形参时,形参也开始指向了该对象。形参的生命周期依赖于所寄生的函数,而函数被调用后会在main方法区内开辟空间,main方法是静态的,即空间是限量的。递归的原理是,当前的函数体没执行完,又调用了一次....,那么,直到递归的条件不满足时,才会罢休,依次关闭倒数第一个方法体,第二个方法体.......直到关闭最初的那个方法体。请问:形参被激活时的方法体时那个?最初的那个方法体。这个形参是生的早死的迟,显然,它是递归过程中的“全局变量”。
二、内存溢出问题。
最初创建的方法体不会关闭,直到递归条件探底,众多的方法体才会倒着相继执行结束而关闭。那么,这个递归条件就必须有限制,才能防止内存溢出。

作者: sofeel    时间: 2015-2-23 00:39
内存溢出补充下:
       String s="",  大家都知道“”这是一个String类型的对象,它是有自己空间的。递归过程中虽然始终只有一个变量,存在于最初的方法体中。可仍然有内存溢出的危险,原因在于:函数体有自己的空间,尽管除了第一个函数体外的函数体是空的,就像那个空字符串一样。
      函数体是空的,为什么依然会开辟空间呢?因为数据有可能会在该空间内参与运算。如果变量经过该空间后,能活着回来,变量会路过这里,即会在此间参与运算;如果变量经过该空间后,死在了外面,程序就挂了,那这空间算是白开了这么久!
作者: fantacyleo    时间: 2015-2-23 00:53
mengxiang1993 发表于 2015-2-22 21:47
嗯,这会涉及到同一个方法局部变量重名吧,这个怎么解释?

每次函数调用都开辟新空间,怎么会互相干扰呢?就好比有2个叫张三的学生,只要他们在不同的班级,老师上课点名就不会遇到重名问题。所以同名变量不要紧,只要他们是存放于不同位置的即可
作者: alvis2015    时间: 2015-2-23 09:07
mengxiang1993 发表于 2015-2-22 21:16
那么造成栈内存溢出的原因是什么?难道不是一直在产生新的局部变量吗? ...

栈内存溢出的原因好像是递归函数每一次调用,都要在内存中开辟出一个空间来,如果递归不返回的话,就会一直在栈内存中开辟新的空间,这个过程中没有产生新的局部变量,局部变量仍然只有你在函数体中声明的一个,而在每次调用递归函数体的时候,在内存中函数的参数值是固定的,是你声明的变量的值传递进去的。
作者: alvis2015    时间: 2015-2-23 09:19
alvis2015 发表于 2015-2-23 09:07
栈内存溢出的原因好像是递归函数每一次调用,都要在内存中开辟出一个空间来,如果递归不返回的话,就会一 ...

看代码
  1. public static void test(){
  2.       test();
  3. }
复制代码

上面这段代码也是递归,递归方法中没有声明变量,但内存仍会溢出。
内存溢出的原因你就明白了
作者: alvis2015    时间: 2015-2-23 10:22
alvis2015 发表于 2015-2-23 09:19
看代码

上面这段代码也是递归,递归方法中没有声明变量,但内存仍会溢出。

按说方法内存是存放在方法区中的,具体我也不懂得,总之方法在调用的时候,会在栈内存总开辟一段空间用于方法的运行。内存这个地方我也还有些不懂,共同学习。嘿嘿
作者: mengxiang1993    时间: 2015-2-23 10:24
sofeel 发表于 2015-2-23 00:12
一,先谈参数。
形参、实参都是参数,不同在于实参是有具体指向的引用,形参需要实参来激活。当实参把自己 ...

方法区,也叫静态域。存放在对象中用static定义的静态成员(即静态变量,同理,如果该静态变量是基本类型则将变量名和值存入静态域,如果是引用类型则指向new出的对象)。静态方法也存在方法区?
为什么还分main方法区?
作者: lwj123    时间: 2015-2-23 12:21
万合天宜 发表于 2015-2-22 19:30
形参只是用来传递的吧,关键是实参不同,这块我也不太清楚哦。还有堆栈溢出是因为递归次数太多了,一般递归 ...

形参只是一个接收数据的一个东西,最重要的是你在递归里改变的作为递归调用的实参不同即可。
每一次递归都会开辟一段新的内存空间,如果次数过多,就不宜做递归,这样会发生内存溢出
作者: 万合天宜    时间: 2015-2-23 15:42
lwj123 发表于 2015-2-23 12:21
形参只是一个接收数据的一个东西,最重要的是你在递归里改变的作为递归调用的实参不同即可。
每一次递归 ...

搜嘎~~了解了解了
作者: franksight    时间: 2015-2-23 16:11
形参名字是什么不重要的,一个函数循环递归,会不断消耗系统资源啊,内存是有限的
作者: liaohongjie    时间: 2015-2-23 18:34
因为一直调用自己会不停创建新的空间,所以当内存不够时,就会出现内存溢出。
作者: sofeel    时间: 2015-2-23 19:24
mengxiang1993 发表于 2015-2-23 10:24
方法区,也叫静态域。存放在对象中用static定义的静态成员(即静态变量,同理,如果该静态变量是基本类型 ...

new出的对象存在堆中。被static修饰的成员存在方法区中,方法区分静态和一般。
这些所做的仅仅是把数据存起来。
存好之后,要开始运算。运算都是在栈内存的main中,因为它是程序的入口。存储和运算是分开的。

我是猜想的。你可以继续思考,不要受我影响。

作者: 袁丙轩    时间: 2015-2-23 19:31
过来看看
作者: mengxiang1993    时间: 2015-2-23 19:49
lwj123 发表于 2015-2-23 12:21
形参只是一个接收数据的一个东西,最重要的是你在递归里改变的作为递归调用的实参不同即可。
每一次递归 ...

我现在就是想知道到底是哪块的内存溢出了?静态方法是存在方法区?




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2