Java中参数传递是传值还是传引用呢?很多人遇到这个问题都会马上给你抛出这个例子:

class Entry{
Integer value;
public Entry(Integer v){
this.value = v;
}
@Override
public String toString() {
return "Entry[value=" + value + "]";
}
}
public class CallByDemo{
public static void swap(int a,int b){
int temp = a;
a = b;
b = a;
}
public static void swap(Entry e1,Entry e2){
Integer temp = e1.value;
e1.value = e2.value;
e2.value = temp;
}
public static void main(String[] args) {
int a = 1;
int b = 2;
System.out.println("before:a="+a+",b="+b);
swap(a,b);
System.out.println("after :a="+a+",b="+b);
Entry e1 = new Entry(new Integer(1000));
Entry e2 = new Entry(new Integer(2000));
System.out.println("before:e1="+e1+"e2="+e2);
swap(e1,e2);
System.out.println("after :e1="+e1+"e2="+e2);
}
}

运行结果:

before:a=1,b=2

after :a=1,b=2

before:e1=Entry[value=1000]e2=Entry[value=2000]

after : e1=Entry [value=2000 ]e2=Entry[value=1000]

然后言之凿凿地抛出这个结论:

  1. 当参数为基本类型时为传值
  2. 当参数为对象引用类型为传引用

好像没有毛病啊,但是如果我把swap(Entry e1,Entry e2)改成这样呢?

public static void swap(Entry e1,Entry e2){
Entry temp = e1;//Integer temp = e1.value;
e1 = e2; //e1.value = e2.value;
e2 = temp;//e2.value = temp;
}

再次运行发现结果变成了这样:

before:a=1,b=2

after :a=1,b=2

before:e1=Entry[value=1000]e2=Entry[value=2000]

after : e1=Entry [value=1000 ]e2=Entry[value=2000]

什么?怎么会这样?

为了解释这个问题,我们不妨看一下Java运行时内存结构:

Java堆 (Java Heap)

  1. 作用:存放几乎所有的对象实例和数组
  2. 组成
    • 新生代(Young Generation)

      • Eden区:存放新创建的对象或短期的对象
      • Survivor区:存放GC后的幸存的或中期的对象
    • 老年代(Old Generation):存放GC多次后始终存在或者长期的对象及Survivor区放不下的大对象
    • 永久代(Permanent Generation):永久代在JDK8中被完全地移除
  3. 是否线程共享:是

Java虚拟机栈(JVM Stacks)

  1. 作用:存放栈帧
  2. 组成:栈帧
  3. 是否线程共享:线程私有的,生命周期和线程的相同

栈帧(Stack Frame)

- 作用:方法在执行的时候,都会有一个栈帧创建出来,用于存储局部变量表、操作数栈、动态链接、方法出口等信息

- 组成:

- 局部变量表(Local Variables):存放编译时可知的各种基本数据类型、对象引用

- 操作数栈(Operand Stacks):供方法调用时进行各种运算

- 动态链(Dynamic Linking):

- 方法出口

方法区(Method Area)

  1. 作用:存放被虚拟机加载的类的结构信息(如:字段和方法数据、方法的字节码、运行时常量池等)、常量,静态变量及类、实例、接口初始化时用到的特殊方法。
  2. 组成:方法区是堆的逻辑组成部分(有人称之为永久代 Permanent Generation)
  3. 是否线程共享:是

本地方法栈(Native Method Stacks)

  1. 作用:存放本地方法调用时的栈帧
  2. 组成:栈帧
  3. 是否线程共享:线程私有的,生命周期和线程的相同
  4. 虚拟机执行Native方法时使用,不同的虚拟机有不同的实现方法,HotSpot虚拟机的本地方法栈和虚拟机栈合二为一。

PC寄存器/程序计数器(pc Register)

  1. 作用:保存JVM正在执行方法的字节码指令的地址,如果该方法为native本地方法则为undefined
  2. 组成:一块至少能够保存一个本地指针或者returnAddress的值的内存空间
  3. 是否线程共享:每个线程都有一个独立的程序计数器,各个线程之间计数器互不影响,独立存储

OK,我们再来分析一下上面的问题:

其实呢,Java采用的是传值(call by value),形参只是实际参数的一个拷贝,形参不能修改实参的内容。

  1. 当值为基本数据类型时,swap(int,int)方法中的局部变量a,b接收传入的值并保存在与该方法对应的栈帧的局部变量表中。而main方法中的a,b保存在main方法对应的栈帧的局部变量表中,修改swap方法中的a,b对main方法中的a,b没有任何影响,所以交换失败。

  2. 当值为引用类型时,传入方法的也是它的一个拷贝,当然这个拷贝有点特殊,它是Java Heap中的对象(Entry_e1、Entry_e2)的一个引用。该引用也保存在对应的栈帧的局部变量表中,修改swap方法中的e1,e2的引用指向对main方法中的e1,e2没有任何影响,所以交换失败。但局部变量e1,e2可以通过引用改变Heap中的对象的状态,如第一段代码中在swap中的局部变量可以通过引用来修改Heap中的对象的value属性,从而达到交换属性中的目的。

    此外,需要注意的是Java中的某些类如:String、基本类型的包装类、BigInteger、BigDecimal是不可变的,即无法修改其内容。

最后总结一句:Java是方法调用是值传递!

call by value or reference ?的更多相关文章

  1. ASP.NET Core: You must add a reference to assembly mscorlib, version=4.0.0.0

    ASP.NET Core 引用外部程序包的时候,有时会出现下面的错误: The type 'Object' is defined in an assembly that is not referenc ...

  2. 【转】Django Model field reference学习总结

    Django Model field reference学习总结(一) 本文档包含所有字段选项(field options)的内部细节和Django已经提供的field types. Field 选项 ...

  3. (转) Qt 出现“undefined reference to `vtable for”原因总结

    由于Qt本身实现的机制所限,我们在使用Qt制作某些软件程序的时候,会遇到各种各样这样那样的问题,而且很多是很难,或者根本找不到原因的,即使解决了问题,如果有人问你为什么,你只能回答--不知道. 今天我 ...

  4. undefined reference to `__android_log_print'

    使用android studio 编写NDK代码时出现错误:undefined reference to `__android_log_print' 解决办法: eclipse       andro ...

  5. CentOS 6.5 编译 PHP-7 报错:undefined reference to `libiconv_open 无法编译 PHP libiconv

    ./configure --with-mysql=/backup/mysql --with-freetype-dir --with-jpeg-dir --with-png-dir --with-zli ...

  6. Qt - 错误总结 - 在自定义类头文件中添加Q_OBJECT 编译时报错(undefined reference to ‘vtable for xxThread)

    错误提示:在添加的QThread子类头文件添加Q_OBJECT时,编译程序,出现"undefined reference to 'vtable for xxThread'"错误提示 ...

  7. Conditional project or library reference in Visual Studio

    Conditional project or library reference in Visual Studio In case you were wondering why you haven’t ...

  8. Qt经典出错信息之undefined reference to `vtable for classname

    原文链接:Qt经典出错信息之undefined reference to `vtable for classname 这个出错信息太常见了,用过Qt两个月以上的朋友基本上都能自己解决了,因为太经典了, ...

  9. OpenCASCADE6.8.0 Reference Manual Serach Problem

    OpenCASCADE6.8.0 Reference Manual Serach Problem eryar@163.com 1. Problem 有网友反映OpenCASCADE6.8.0的Refe ...

  10. SQL SERVER 2005删除维护作业报错:The DELETE statement conflicted with the REFERENCE constraint "FK_subplan_job_id"

    案例环境: 数据库版本: Microsoft SQL Server 2005 (Microsoft SQL Server 2005 - 9.00.5000.00 (X64) ) 案例介绍: 对一个数据 ...

随机推荐

  1. JavaScript定时机制、以及浏览器渲染机制 浅谈

    昨晚,朋友拿了一道题问我: a.onclick = function(){ setTimeout(function() { //do something ... },0); }; JavaScript ...

  2. Hibernate懒加载的三种解决方案

    Hibernate懒加载的两种解决方案: 1.Hibernate.initialize(代理对象) 2.在*.hbm.xml映射文件中添加lazy="false"属性 3.使用op ...

  3. Delphi ADO数据操作封装类

    [delphi] view plaincopyprint? { 将数据集操作方面的东西全部封装成一个单独的类 TcustomAdoDataSet是TadoQuery.TadoTable.TadoDat ...

  4. 强行在MFC窗体中渲染Cocos2d-x 3.6

    [前言] 把Cocos2dx渲染到另一个应用程序框架中的方法,在2.x时代有很多大神已经实现了,而3.x的做法网上几乎找不着.这两天抽空强行折腾了一下,不敢独享,贴出来供大家参考. [已知存在的问题] ...

  5. redhat6 + 11G RAC 双节点部署

      一.配置网络环境 node1 [root@node1 ~]#vi/etc/sysconfig/network NETWORKING=yes NETWORKING_IPV6=no HOSTNAME= ...

  6. 如何用CSS快速布局(一)—— 布局元素详细

    要快速进行网页排版布局,则必须对布局的元素有清晰的了解,才不会总是在细节处出错.这一篇先详解有关布局的因素作为布局基础:块级元素and内联元素.盒模型.准确定位.元素对齐.样式继承.下一篇则重点描述快 ...

  7. W3Cschool学习笔记——CSS教程

    CSS 概述 CSS 指层叠样式表 (Cascading Style Sheets) 样式定义如何显示 HTML 元素 样式通常存储在样式表中 把样式添加到 HTML 4.0 中,是为了解决内容与表现 ...

  8. 毕向东udp学习笔记2

    项目功能:  发送端读取控制台输入,然后udp发送 接收端一直接收,直到输入为886 相对于笔记1,修改了发送端代码,实现发送控制台的内容,接收端循环接收,当输入886时,停止发送 发送端: impo ...

  9. (4)activiti之uel表达式

    有了前面几章,我们肯定有一定的困惑,activiti如何与实际业务整合,比如一条采购单,如何跟一个流程实例互相关联起来? 这里就需要使用到activiti启动流程实例时设置一个流程实例的busines ...

  10. mktime性能问题调查

    一.问题提出 会议中有同学提到使用mktime遇到一些问题: 1) 设置tm_isdst后速度很慢 2) 设置TZ环境变量提速极大 所以想调查下具体情况.   mktime真的这么慢?如果是,为什么? ...