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. spark Intellij IDEA开发环境搭建

    (1)创建Scala项目 File->new->Project,如下图 选择Scala 然后next 其中Project SDK指定安装的JDK,Scala SDK指定安装的Scala(这 ...

  2. nginx 重启

    /etc/init.d/nginx -s reload /etc/init.d/nginx reload

  3. centos 6.5下安装文件上传下载服务

    centos 6.5下安装文件上传下载服务 由于每次在CentOS中要下载一些配置文件到物理机,和上传一些文件到服务器,导致来回的开启ftp软件有点麻烦,这里我们可以使用文件上传下载服务,来解决上传和 ...

  4. java_web学习(1)理解JavaBean

    JavaBean简介       JavaBean是一种特殊的 Java 类,它遵从一定的设计模式,开发工具和其他组件可以根据这种模式来调用JavaBean. JavaBean可以设计得像Swing组 ...

  5. iOS 之 NSUserDefault

    1. 概念 NSUserDefaults是一种特殊的property list.是系统封装的属性列表,方便记录简单的数据. 2. 使用说明 2.1. 创建NSUserDefaults NSUserDe ...

  6. 给 Memo 排序的函数

    本例效果图: 代码文件: unit Unit1; interface uses   Windows, Messages, SysUtils, Variants, Classes, Graphics, ...

  7. cgLib生成动态代理

    package com.stono.cglib; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import ...

  8. 开发团队在TFS中使用Git Repository (二)

    使用Git作分支时,仅仅是对提交历史记录的一个引用,创建分支成本非常低,分支的切换快且简单.在分支管理方面,相对其他的版本管理工具,Git可谓是一骑绝尘. 开发过程中,我们可以针对任何的大小功能进行分 ...

  9. ubuntu linux 中安装 mysql

    三种安装方式: 1. 从网上安装 sudo apt-get install mysql-server.装完已经自动配置好环境变量,可以直接使用mysql的命令. 注:建议将/etc/apt/sourc ...

  10. Unsupported major.minor version 52.0错误解决 Ubuntu JDK8 安装配置

    Unsupported major.minor version 52.0错误一般是因为应用程序需要JDK8而ubuntu默认的是jdk7,所以需要切换到jdk8才能解决这个问题. 本文使用PPA方式安 ...