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);的运行结果。

通过本文大家应该知道Java方法参数是引用调用还是值调用了吧。

(0)

相关推荐

  • java eclipse 启动参数

    在参考了一些资料之后,我总结了一些比较常用的启动时Command Arguments,如果有不正确的地方希望大家予以指出. -arch [processor architecture] 描述:指定所使用的处理器的类别 举例:eclipse -arch x86或eclipse -arch sparc -application [id] 描述:指定要运行的应用,id为扩展org.eclipse.core.applications扩展点的插件id加扩展id 举例:例如有个插件id为edu.sdu.ap

  • java解析出url请求的路径和参数键值对类(解析出url请求的路径,包括页面)

    复制代码 代码如下: package RequestPackage; import java.util.HashMap; import java.util.Map; public class CRequest { /** * 解析出url请求的路径,包括页面 * @param strURL url地址 * @return url路径 */ public static String UrlPage(String strURL) { String strPage=null; String[] arr

  • java向多线程中传递参数的三种方法详细介绍

    在传统的同步开发模式下,当我们调用一个函数时,通过这个函数的参数将数据传入,并通过这个函数的返回值来返回最终的计算结果.但在多线程的异步开发模式下,数据的传递和返回和同步开发模式有很大的区别.由于线程的运行和结束是不可预料的,因此,在传递和返回数据时就无法象函数一样通过函数参数和return语句来返回数据.本文就以上原因介绍了几种用于向线程传递数据的方法,在下一篇文章中将介绍从线程中返回数据的方法. 欲先取之,必先予之.一般在使用线程时都需要有一些初始化数据,然后线程利用这些数据进行加工处理,并

  • Java 8中如何获取参数名称的方法示例

    前言 在Java 8之前的版本,代码编译为class文件后,方法参数的类型是固定的,但参数名称却丢失了,这和动态语言严重依赖参数名称形成了鲜明对比.现在,Java 8开始在class文件中保留参数名,给反射带来了极大的便利. 示例: public class GetRuntimeParameterName { public void createUser(String name, int age, int version) { // } public static void main(Strin

  • Java如何实现URL带请求参数(get/post)及得到get和post请求url和参数列表的方法

    具体代码如下所示: public static String sendGet(String url,String param) { String result = ""; try{ String urlName = url + "?"+param;// URL U = new URL(urlName); URLConnection connection = U.openConnection(); connection.connect(); BufferedReade

  • javascritp添加url参数将参数加入到url中

    将对象参数加入到url中,如果原来url中有则覆盖 js代码 function addToUrl(obj){ var aprotocol = location.protocol; var ahost = location.host; var apath = location.pathname; var asearch = location.search; var ahash = location.hash; var result = ''; console.log(obj); var joinO

  • Java获取代码中方法参数名信息的方法

    前言 大家都知道随着java8的使用,在相应的方法签名中增加了新的对象Parameter,用于表示特定的参数信息,通过它的getName可以获取相应的参数名.即像在代码中编写的,如命名为username,那么在前台进行传参时,即不需要再编写如@Parameter("username")类的注解,而直接就能进行按名映射. 如下的代码参考所示: public class T { private interface T2 { void method(String username, Stri

  • java根据方法名称取得反射方法的参数类型示例

    复制代码 代码如下: /** * 根据方法名称取得反射方法的参数类型(没有考虑同名重载方法使用时注意) * @param obj         类实例   * @param methodName  方法名 * @return * @throws ClassNotFoundException */public static Class[]  getMethodParamTypes(Object classInstance,  String methodName) throws ClassNotF

  • Java方法的可变参数类型实例分析

    Java方法中的可变参数类型是一个非常重要的概念,有着非常广泛的应用.本文就以实例形式对此加以分析.具体如下: 一般来说,许多Java初学者在看到下面的这段代码的时候,都会问一个问题:dealArray方法里那三个小点点是什么啊? public class TestVarArgus { public static void dealArray(int... intArray){ } public static void main(String args[]){ } } 这就是本文所要讨论的问题:

  • Java方法参数是引用调用还是值调用?

    方法调用(call by) 是一个标准的计算机科学术语.方法调用根据参数传递的情况又分为值调用( call by reference ) 和引用调用( call by value ) .江湖上有很多关于这两种调用的定义 ,最通常的说法是传递值的是值调用,传递地址的是引用调用.这其实很不恰当,这种 这些说法很容易让我们联想到Java的对象参数传递是引用调用,实际上,Java的对象参数传递仍然是值调用 . 我们首先用一段代码来证实一下为什么Java的对象参数传递是值调用. public class

  • Java 方法的定义与调用详解

    目录 1.方法概述 2.方法的定义和调用 2.1 方法定义 2.2 方法调用 2.3 方法的调用过程 2.4 方法练习 3.带参数方法的定义和调用 3.1带参数方法定义 3.2带参数方法调用 1.方法概述 什么是方法 方法(method)是将具有独立功能的代码块组织成为一个整体,使其具有图书功能的代码集 注意: 方法必须先创建才可以使用,该过程称为方法定义 方法创建后并不是直接运行的,需要手动使用后才执行,该过程称为方法调用 2.方法的定义和调用 2.1 方法定义 格式: public stat

  • Java方法参数装配顺序详解

    自动化装配的确有很大的便利性,但是却并不能适用在所有的应用场景,比如需要装配的组件类不是由自己的应用程序维护,而是引用了第三方的类库,这个时候自动装配便无法实现,Spring对此也提供了相应的解决方案,那就是通过显示的装配机制--Java配置和XML配置的方式来实现bean的装配. 从左到右依次装配,参数的值一旦确定,即使后面修改了该值,方法拿到的值也不会随之变化了. 代码如下 class Solution { public int a; @Override public String toSt

  • Java方法参数传递如何实现

    Java方法参数 首先回顾一下程序设计语言中有关将参数传递给方法的一些专业术语. 1. 按值调用(call by value)表示方法接收的是调用者提供的值. 2. 按引用调用(call by reference)表示方法接收的是调用者提供的变量地址. 一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变脸值."call by"是一个标准的计算机科学术语,它用来描述各种程序设计语言中方法参数的传递方式. Java采用按值调用.也就是说,方法得到的是所有参数的一个拷贝,

  • 在Java中按值调用和按引用调用

    目录 一.为什么按值调用和按引用调用? 二.按值调用 三.引用调用 四.为什么Java中只有按值调用,没有按引用调用? 五.Java中的引用调用,但如何调用? 六.Java对象引用 七.结论 一.为什么按值调用和按引用调用? 方法或函数可以通过两种方式调用.一种是按值调用,另一种是按引用调用,这两种方式通常根据作为输入或参数传递给它们的值的类型来区分. 在我们开始之前,让我澄清一件事,在Java中,只有按值调用(值传递),没有按引用调用. 二.按值调用 按值调用是一种方法,通过复制给定变量(或常

  • Java 带参数与带返回值的方法的定义和调用

    目录 带参数方法的定义和调用 形参和实参 带参数方法练习 带返回值的方法的定义和调用 带返回值的方法定义 带返回值的方法调用 带参数方法的定义和调用 形参和实参 形参:方法定义中的参数 相当于变量定义格式,例int number 实参:方法调用中参数 等同于变量或常量,例如10   , number 带参数方法练习 需求: 设计一个方法用于打印两个数中最大数,数据来自于方法参数 思路: 1.定义一个方法,用于打印两个书中的最大数,例如getMax() public static void get

  • C#调用Java方法实例详解

    C#可以直接引用C++的DLL和转换JAVA写好的程序.最近由于工作原因接触这方面比较多,根据实际需求,我们通过一个具体例子把一个JAVA方法转换成可以由C#直接调用的DLL C#调用c++ C#调用C++的例子网上很多,以一个C++的具体方法为例. C++代码 // 获取一帧图像数据 MVSMARTCAMCTRL_API int __stdcall MV_SC_GetOneFrame(IN void* handle, IN OUT unsigned char *pData , IN unsig

  • 深入理解Java中的构造函数引用和方法引用

    JDK 8 见证了一个特殊特性的出现:构造函数引用和方法引用.在本文中, Adrian D. Finlay 探讨了开发人员如何释放构造函数引用的真正潜力. 方法引用的一些背景 如果你还不知道 Java 构造函数本身就是特殊的方法,那么阅读方法引用的基本示例将对读者有所帮助,通过了解这些内容,可以了解构造函数引用是什么. 「方法引用为已经有名称的方法提供易读的 lambda 表达式.」 「它们提供了一种无需执行就可以引用方法的简单方式.」 以上引自<Java 8 编程参考官方教程(第 9 版)>

  • 详解Java方法method的定义与调用及重载

    目录 方法的定义和调用 什么是方法 方法的声明格式 方法的调用方式 方法的详细说明 总结 方法的重载 什么是方法重载 构成方法重载的条件 总结 方法的定义和调用 什么是方法 方法(method)就是一段用来完成特定功能的代码片段,类似于其它语言的函数(function). 方法用于定义该类或该类的实例的行为特征和功能实现. 方法是类和对象行为特征的抽象.方法很类似于面向过程中的函数.面向过程中,函数是最基本单位,整个程序由一个个函数调用组成.面向对象中,整个程序的基本单位是类,方法是从属于类和对

  • Java方法调用解析静态分派动态分派执行过程

    方法调用 在程序运行时,进行方法调用是最普遍,最频繁的操作 方法调用不等于方法执行: 方法调用阶段唯一的任务就是确定被调用的方法版本,即调用哪一个方法 不涉及方法内部的具体运行过程 Class文件的编译过程不包括传统编译中的连接步骤 Class文件中的一切方法调用在Class文件里面存储的都是符号引用,而不是方法在在实际运行时内存布局中的入口地址,即之前的直接引用: 这样使得Java具有更强大的动态扩展能力 同时也使得Java方法调用过程变得相对复杂 需要在类加载期间,甚至会到运行期间才能确定目

随机推荐