- 浏览: 885621 次
- 性别:
- 来自: 武汉
文章分类
最新评论
-
小宇宙_WZY:
膜拜一下大神,解决了我一个大问题,非常感谢 orz
【解惑】深入jar包:从jar包中读取资源文件 -
JKL852qaz:
感谢,遇到相同的问题!
【解惑】深入jar包:从jar包中读取资源文件 -
lgh1992314:
为什么java中调用final方法是用invokevirtua ...
【解惑】Java动态绑定机制的内幕 -
鲁曼1991:
说的都有道理,protected只能被同一级包的类所调用
【解惑】真正理解了protected的作用范围 -
鲁曼1991:
...
【总结】String in Java
方法调用(call by)
是一个标准的计算机科学术语。方法调用根据参数传递的情况又分为值调用(
call by reference
)
和引用调用(
call by value
)
。江湖上有很多关于这两种调用的定义 ,最通常的说法是传递值的是值调用,传递地址的是引用调用。这其实很不恰当,这种
这些说法很容易让我们联想到Java的对象参数传递是引用调用,实际上,Java的对象参数传递仍然是值调用
。
我们首先用一段代码来证实一下为什么Java的对象参数传递
是值调用。
public class Employee { public String name=null; public Employee(String n){ this.name=n; } //将两个Employee对象交换 public static void swap(Employee e1,Employee e2){ Employee temp=e1; e1=e2; e2=temp; System.out.println(e1.name+" "+e2.name); //打印结果:李四 张三 } //主函数 public static void main(String[] args) { Employee worker=new Employee("张三"); Employee manager=new Employee("李四"); swap(worker,manager); System.out.println(worker.name+" "+manager.name); //打印结果仍然是: 张三 李四 } }
上面的结果让人很失望,虽然形参对象e1,e2的内容交换了,但实参对象worker,manager并没有互换内容。这里面最重要的原因就在于形参e1,e2是实参worker,manager的地址拷贝。
大家都知道,在Java中对象变量名实际上代表的是对象在堆中的地址(专业术语叫做对象引用 )。在Java方法调用的时候,参数传递的是对象的引用。重要的是,形参和实参所占的内存地址并不一样,形参中的内容只是实参中存储的对象引用的一份拷贝。
如果大家对JVM内存管理中Java栈 的局部变量区 有所了解的话(可以参见《 Java 虚拟机体系结构 》),就很好理解上面这句话。在JVM运行上面的程序时,运行main方法和swap方法,会在Java栈中先后push两个叫做栈帧 的内存空间。main栈帧中有一块叫局部变量区的内存用来存储实参对象worker和manager的引用。而swap栈帧中的局部变量区则存储了形参对象e1和e2的引用。虽然e1和e2的引用值分别与worker和manager相同,但是它们占用了不同的内存空间。当e1和e2的引用发生交换时,下面的图很清晰的看出完全不会影响worker和manager的引用值。
Java对象参数传递虽然传递的是地址(引用),但仍然是值调用。是时候需要给引用调用和值调用一个准确的定义了。
值调用(call by value) : 在参数传递过程中,形参和实参占用了两个完全不同的内存空间。形参所存储的内容是实参存储内容的一份拷贝。实际上,Java对象的传递就符合这个定义,只不过形参和实参所储存的内容并不是常规意义上的变量值,而是变量的地址。咳,回过头想想:变量的地址不也是一种值吗!
引用调用(call by reference) : 在参数传递的过程中,形参和实参完全是同一块内存空间,两者不分彼此。 实际上,形参名和实参名只是编程中的不同符号,在程序运行过程中,内存中存储的空间才是最重要的。不同的变量名并不能说明占用的内存存储空间不同。
大体上说,两种调用的根本并不在于传递的是值还是地址(毕竟地址也是一个值),而是在于形参和实参是否占用同一块内存空间。事实上,C/C++的指针参数传递也是值调用,不信试试下面的C代码吧!
#include<stdio.h> void swap(int *a1,int *b1){ int *t=a1; a1=b1; b1=t; } int main(){ int x1=100; int x2=200; int *a=&x1; int *b=&x2; printf("%d %d\n",*a,*b); swap(a,b); printf("%d %d\n",*a,*b); return 0; }
但C/C++是有引用调用的,这就是C/C++一种叫做引用的变量声明方法: int a; int &ra=a; 其中ra是a的别名,两者在内存中没有区别,占用了同一个内存空间。而通过引用(别名)的参数传递就符合引用调用的特点了。大家可以去试试
void swap(int &a1,int &b1);的运行结果。
评论
....打印出来是[222,333],如果按照你讲的应该是【111,222,33】才对啊
jdk1.5
这里补充一个知识:一个java程序运行过程中,所有方法开辟的对象的内容都保存在唯一的一段内存中,这个内存我们叫“堆”,而每个方法运行时的局部变量和对象的引用全部短暂的保存在JVM为方法创建的独立的栈桢中,每个方法对应一个栈桢,所有栈桢存放在一段内存空间中,我们叫“堆栈”。
我们分析一下过称:
(1)main函数首先在内存堆中开辟了一个ArrayList的对象list,里面存放的是[111,222,333],这个对象list在堆中有一个地址,不妨假设是0x1111。
(2)然后要注意了:main函数调用update函数传递的是对象list的引用,也就是0x1111这样一个值,这个值保存在内存栈空间的update栈桢中。,这个值传给了update的参数list,也就是说在update这个方法的栈桢中也开辟了一个空间,存放的是main传递过来的0x1111这个值。
(3) 在update运行时也会在内存堆中开辟一个ArrayList的对象result,里面存放的是[111,222],这个对象的地址我们假设为Ox2222。这个值也是存放在update的栈桢中。
此时要注意了,update栈桢中有两个值,一个是形参list的0x1111,另一个是刚开辟的result的Ox2222。
(4) 当执行list.remove("111")的时候,JVM是将栈桢中0x1111地址所指向的内存堆中的对象[111,222,333]中的一个元素"111"删除。这句执行之后,Ox1111地址中的内容已经少了一个"111"了。
(5) 比较迷惑的是下面的赋值语句:list = result;
这句话是引用赋值,只是将update栈桢中原本存放Ox1111这个值的位置赋值成了Ox2222。而并没有将Ox2222所指向的内存堆中的[111,222]对象全部覆盖掉Ox1111所指向的内存堆中的[222,333],注意Ox1111所指向的对象内容已经被第(4)步remove掉了一个。
更重要的是,main主函数栈桢中的存放Ox1111这个值的位置并没有被赋值掉,所以在main中最后打印的仍然是Ox1111所指向的对象[222,333]
这次方法调用仍然是引用传递,是值传递,而非地址传递。想搞清楚JVM内部的运行情况,建议看《深入JVM》。
麻烦问lz,多线程下,访问这个updatelist方法的话,update的形参 List 会在方法栈区,开辟多个引用保存main传递进来的引用地址么? 还是说只开辟一个引用,多线程下需要考虑同步的问题.
....打印出来是[222,333],如果按照你讲的应该是【111,222,33】才对啊
jdk1.5
这里补充一个知识:一个java程序运行过程中,所有方法开辟的对象的内容都保存在唯一的一段内存中,这个内存我们叫“堆”,而每个方法运行时的局部变量和对象的引用全部短暂的保存在JVM为方法创建的独立的栈桢中,每个方法对应一个栈桢,所有栈桢存放在一段内存空间中,我们叫“堆栈”。
我们分析一下过称:
(1)main函数首先在内存堆中开辟了一个ArrayList的对象list,里面存放的是[111,222,333],这个对象list在堆中有一个地址,不妨假设是0x1111。
(2)然后要注意了:main函数调用update函数传递的是对象list的引用,也就是0x1111这样一个值,这个值保存在内存栈空间的update栈桢中。,这个值传给了update的参数list,也就是说在update这个方法的栈桢中也开辟了一个空间,存放的是main传递过来的0x1111这个值。
(3) 在update运行时也会在内存堆中开辟一个ArrayList的对象result,里面存放的是[111,222],这个对象的地址我们假设为Ox2222。这个值也是存放在update的栈桢中。
此时要注意了,update栈桢中有两个值,一个是形参list的0x1111,另一个是刚开辟的result的Ox2222。
(4) 当执行list.remove("111")的时候,JVM是将栈桢中0x1111地址所指向的内存堆中的对象[111,222,333]中的一个元素"111"删除。这句执行之后,Ox1111地址中的内容已经少了一个"111"了。
(5) 比较迷惑的是下面的赋值语句:list = result;
这句话是引用赋值,只是将update栈桢中原本存放Ox1111这个值的位置赋值成了Ox2222。而并没有将Ox2222所指向的内存堆中的[111,222]对象全部覆盖掉Ox1111所指向的内存堆中的[222,333],注意Ox1111所指向的对象内容已经被第(4)步remove掉了一个。
更重要的是,main主函数栈桢中的存放Ox1111这个值的位置并没有被赋值掉,所以在main中最后打印的仍然是Ox1111所指向的对象[222,333]
这次方法调用仍然是引用传递,是值传递,而非地址传递。想搞清楚JVM内部的运行情况,建议看《深入JVM》。
List<String> result = new ArrayList<String>();
result.add("111");
result.add("222");
list.remove(0);
list = result;
}
public static void main(String[] args) throws Exception {
List<String> list = new ArrayList<String>();
list.add("111");
list.add("222");
list.add("333");
update(list);
System.out.println(list);
}
打印出来是[222,333],如果按照你讲的应该是【111,222,33】才对啊
jdk1.5
其实,现在已经很少再讲什么引用传递和值传递的概念了。只有在大学里面一些老教授喜欢提这两个术语。
有个小问题:值调用(call by value) : 在参数传递过程中,形参和实参占用了两个完全不同的内存空间。形参所存储的内容是实参存储内容的一份拷贝。
内容其实是一样的,谈不上拷贝,因为它们没有分清彼此的依据,对吗
所谓的传值和引用,不过都是传值,传递一个“指针”
发表评论
-
NIO
2010-08-05 10:36 0在JDK1.4以前,I/O输入输出处理,我们把它称为旧 ... -
【总结】Java线程同步机制深刻阐述
2010-05-16 10:21 5914全文转载:http://www.iteye ... -
【JDK优化】java.util.Arrays的排序研究
2010-05-12 21:06 9093作者题记:JDK中有很多算法具有优化的闪光点,值得好好研究。 ... -
【JDK优化】 Integer 自动打包机制的优化
2010-03-12 19:14 4056我们首先来看一段代码: Integer i=100; In ... -
【总结】Java与字符编码问题详谈
2009-12-30 09:11 9331一、字符集和字符编码方式 计算机只懂得0/1两种信号 ... -
【解惑】 正确理解线程等待和释放(wait/notify)
2009-12-29 13:40 19634对于初学者来说,下面这个例子是一个非常常见的错误。 /** ... -
【解惑】JVM如何理解Java泛型类
2009-12-16 11:08 12180//泛型代码 public class Pair<T& ... -
【解惑】正确的理解this 和 super
2009-12-05 09:46 4399转载: 《无聊 ... -
【解惑】真正理解了protected的作用范围
2009-11-21 18:00 4976一提到访问控 ... -
【总结】String in Java
2009-11-21 17:52 10765作者:每次上网冲杯Java时,都能看到关于String无休无止 ... -
【解惑】真正理解了protected的作用范围
2009-11-16 17:11 585一提到访问控制符protected,即使是初学者 ... -
总结Java标准类库中类型相互转化的方法
2009-11-09 21:57 210组一: ☆ String → byte[ ... -
方法没覆盖住带来的烦恼
2009-11-05 09:18 100Object类是所有类的祖宗,它的equals方法比较的 ... -
【解惑】数组向上转型的陷阱
2009-11-03 11:44 1828问题提出: 有两个类Manager和Em ... -
【总结】java命令解析以及编译器,虚拟机如何定位类
2009-11-01 16:25 5731学Java有些日子了,一直都使用IDE来写程序。这 ... -
【解惑】剖析float型的内存存储和精度丢失问题
2009-10-26 15:10 15728问题提出:12.0f-11.9f=0.10 ... -
【解惑】领略内部类的“内部”
2009-10-19 15:38 3523内部类有两种情况: (1) 在类中定义一个类(私有内部类 ... -
【解惑】深入jar包:从jar包中读取资源文件
2009-10-08 21:13 65275我们常常在代码中读取一些资源文件(比如图片,音乐,文 ... -
【解惑】理解java枚举类型
2009-09-26 09:37 3340枚举类型是JDK5.0的新特征。Sun引进了一个全新的关键字e ... -
编写自己的equals方法
2009-09-20 14:18 129在我的《令人头疼的"相等"关 ...
相关推荐
Java Puzzlers 中文版(Java解惑) Java 谜题 1——表达式谜题 谜题 1:奇数性 下面的方法意图确定它那唯一的参数是否是一个奇数。...在任何负整数上调用该方法都回返回 false ,不管该整数是偶数还是奇数。
在任何负整数上调用该方法都回返回 false ,不管该整数是偶数还是奇数。 这是 Java 对取余操作符(%)的定义所产生的后果。该操作符被定义为对于所有的 int 数值 a 和所有的非零 int 数值 b,都满足下面的恒等式: ...
如果你碰到一个特别热心的老员工,事无巨细地给你讲,随时在你身边答疑解惑, 那简直是天大的好运气, 现实是大家都很忙,没人给你讲解。很快就要深入项目做开发了,怎么办呢?我在加入新公司后,就遇到了悲催的情况...
4.2 Rsession让Java调用R更简单 126 4.3 解惑rJava R与Java的高速通道 132 4.4 Node.js与R跨平台通信 137 第5章 R的服务器实现 143 5.1 R语言服务器程序Rserve详解 143 5.2 Rserve的R语言客户端RSclient 149 ...
4.2 Rsession让Java调用R更简单 126 4.3 解惑rJava R与Java的高速通道 132 4.4 Node.js与R跨平台通信 137 第5章 R的服务器实现 143 5.1 R语言服务器程序Rserve详解 143 5.2 Rserve的R语言客户端RSclient 149 ...
群里面有我优秀的同事和朋友,我们在有时间的时候会主动为大家解答各种问题,从Python语言入门到Web应用开发,从数据分析到机器学习,每个领域都有该领域的大拿为大家解惑答疑,小伙伴们可以加群进行交流。...
群里面有我优秀的同事和朋友,我们在有时间的时候会主动为大家解答各种问题,从Python语言入门到Web应用开发,从数据分析到机器学习,每个领域都有该领域的大拿为大家解惑答疑,小伙伴们可以加群进行交流。...
群里面有我优秀的同事和朋友,我们在有时间的时候会主动为大家解答各种问题,从Python语言入门到Web应用开发,从数据分析到机器学习,每个领域都有该领域的大拿为大家解惑答疑,小伙伴们可以加群进行交流。...
群里面有我优秀的同事和朋友,我们在有时间的时候会主动为大家解答各种问题,从Python语言入门到Web应用开发,从数据分析到机器学习,每个领域都有该领域的大拿为大家解惑答疑,小伙伴们可以加群进行交流。...
群里面有我优秀的同事和朋友,我们在有时间的时候会主动为大家解答各种问题,从Python语言入门到Web应用开发,从数据分析到机器学习,每个领域都有该领域的大拿为大家解惑答疑,小伙伴们可以加群进行交流。...
群里面有我优秀的同事和朋友,我们在有时间的时候会主动为大家解答各种问题,从Python语言入门到Web应用开发,从数据分析到机器学习,每个领域都有该领域的大拿为大家解惑答疑,小伙伴们可以加群进行交流。...
群里面有我优秀的同事和朋友,我们在有时间的时候会主动为大家解答各种问题,从Python语言入门到Web应用开发,从数据分析到机器学习,每个领域都有该领域的大拿为大家解惑答疑,小伙伴们可以加群进行交流。...
群里面有我优秀的同事和朋友,我们在有时间的时候会主动为大家解答各种问题,从Python语言入门到Web应用开发,从数据分析到机器学习,每个领域都有该领域的大拿为大家解惑答疑,小伙伴们可以加群进行交流。...
群里面有我优秀的同事和朋友,我们在有时间的时候会主动为大家解答各种问题,从Python语言入门到Web应用开发,从数据分析到机器学习,每个领域都有该领域的大拿为大家解惑答疑,小伙伴们可以加群进行交流。...
群里面有我优秀的同事和朋友,我们在有时间的时候会主动为大家解答各种问题,从Python语言入门到Web应用开发,从数据分析到机器学习,每个领域都有该领域的大拿为大家解惑答疑,小伙伴们可以加群进行交流。...