问:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递? 
  答:是值传递。Java 编程语言只有值传递参数。当一个对象实例作为一个参数被传递到方法中时,参数的值就是该对象的引用一个副本。指向同一个对象,对象的内容可以在被调用的方法中改变,但对象的引用(不是引用的副本)是永远不会改变的。

  Java参数,不管是原始类型还是引用类型,传递的都是副本(有另外一种说法是传值,但是说传副本更好理解吧,传值通常是相对传址而言)。

  如果参数类型是原始类型,那么传过来的就是这个参数的一个副本,也就是这个原始参数的值,这个跟之前所谈的传值是一样的。如果在函数中改变了副本的值不会改变原始的值.
  如果参数类型是引用类型,那么传过来的就是这个引用参数的副本,这个副本存放的是参数的地址。如果在函数中没有改变这个副本的地址,而是改变了地址中的 值,那么在函数内的改变会影响到传入的参数。如果在函数中改变了副本的地址,如new一个,那么副本就指向了一个新的地址,此时传入的参数还是指向原来的 地址,所以不会改变参数的值。
基本类型参数传递:不改变值
       
引用类型参数传递:改变值
       
 
 
 

无论是什么语言,要讨论参数传递方式,就得从内存模型说起,主要是我个人觉得从内存模型来说参数传递更为直观一些。闲言少叙,下面我们就通过内存模型的方式来讨论一下Java中的参数传递。

这里的内存模型涉及到两种类型的内存:栈内存(stack)和堆内存(heap)。基本类型作为参数传递时,传递的是这个值的拷贝。无论你怎么改变这个拷贝,原值是不会改变的。看下边的一段代码,然后结合内存模型来说明问题:

1
2
3
4
5
6
7
8
9
10
11
12
public class ParameterTransfer {
     public static void main(String[] args) {
        int num = 30;
        System.out.println("调用add方法前num=" + num);
        add(num);
        System.out.println("调用add方法后num=" + num);
     }
  
     public static void add(int param) {
          param = 100;
     }
}                

这段代码运行的结果如下:

1
2
调用add方法前num=30
调用add方法后num=30

程序运行的结果也说明这一点,无论你在add()方法中怎么改变参数param的值,原值num都不会改变。

  下边通过内存模型来分析一下。

  当执行了int num = 30;这句代码后,程序在栈内存中开辟了一块地址为AD8500的内存,里边放的值是30,内存模型如下图:

  执行到add()方法时,程序在栈内存中又开辟了一块地址为AD8600的内存,将num的值30传递进来,此时这块内存里边放的值是30,执行param = 100;后,AD8600中的值变成了100。内存模型如下图:

  地址AD8600中用于存放param的值,和存放num的内存没有任何关系,无论你怎么改变param的值,实际改变的是地址为AD8600的内存中的值,而AD8500中的值并未改变,所以num的值也就没有改变。
   以上是基本类型参数的传递方式,下来我们讨论一下对象作为参数传递的方式。

  先看下边的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
public class ParameterTransfer {
     public static void main(String[] args) {
          String[] array = new String[] {"huixin"};
          System.out.println("调用reset方法前array中的第0个元素的值是:" + array[0]);
          reset(array);
          System.out.println("调用reset方法后array中的第0个元素的值是:" + array[0]);
     }
  
     public static void reset(String[] param) {
          param[0] = "hello, world!";
     }
}   

运行的结果如下:

1
2
调用reset方法前array中的第0个元素的值是:huixin
调用reset方法后array中的第0个元素的值是:hello, world!

当对象作为参数传递时,传递的是对象的引用,也就是对象的地址。下边用内存模型图来说明。

  当程序执行了String[] array = new String[] {"huixin"}后,程序在栈内存中开辟了一块地址编号为AD9500内存空间,用于存放array[0]的引用地址,里边放的值是堆内存中的一个地址,示例中的值为BE2500,可以理解为有一个指针指向了堆内存中的编号为BE2500的地址。堆内存中编号为BE2500的这个地址中存放的才是array[0]的值:huixin。

  当程序进入reset方法后,将array的值,也就是对象的引用BE2500传了进来。这时,程序在栈内存中又开辟了一块编号为AD9600的内存空间,里边放的值是传递过来的值,即AD9600。可以理解为栈内存中的编号为AD9600的内存中有一个指针,也指向了堆内存中编号为BE2500的内存地址,如图所示:

  这样一来,栈内存AD9500和AD9600(即array[0]和param的值)都指向了编号为BE2500的堆内存。

  在reset方法中将param的值修改为hello, world!后,内存模型如下图所示:

  改变对象param的值实际上是改变param这个栈内存所指向的堆内存中的值。param这个对象在栈内存中的地址是AD9600,里边存放的值是BE2500,所以堆内存BE2500中的值就变成了hello,world!。程序放回main方法之后,堆内存BE2500中的值仍然为hello,world!,main方法中array[0]的值时,从栈内存中找到array[0]的值是BE2500,然后去堆内存中找编号为BE2500的内存,里边的值是hello,world!。所以main方法中打印出来的值就变成了hello,world!

  

小结:

  无论是基本类型作为参数传递,还是对象作为参数传递,实际上传递的都是值,只是值的的形式不用而已。第一个示例中用基本类型作为参数传递时,将栈内存中的值30传递到了add方法中。第二个示例中用对象作为参数传递时,将栈内存中的值BE2500传递到了reset方法中。当用对象作为参数传递时,真正的值是放在堆内存中的,传递的是栈内存中的值,而栈内存中存放的是堆内存的地址,所以传递的就是堆内存的地址。这就是它们的区别。

补充一下,在Java中,String是一个引用类型,但是在作为参数传递的时候表现出来的却是基本类型的特性,即在方法中改变了String类型的变量的值后,不会影响方法外的String变量的值。关于这个问题,可以参考如下两个地址:

http://freej.blog.51cto.com/235241/168676

http://dryr.blog.163.com/blog/static/58211013200802393317600/

我觉得是这两篇文章中提到的两个原因导致的,一个是String实际上操作的是char[],可以理解为String是char[]的包装类。二是给String变量重新赋值后,实际上没有改变这个变量的值,而是重新new了一个String对象,改变了新对象的值,所以原来的String变量的值并没有改变。

 

java中方法的参数传递机制的更多相关文章

  1. 深入理解Java中方法的参数传递机制

    形参和实参 我们知道,在Java中定义方法时,是可以定义参数的,比如: public static void main(String[] args){ } 这里的args就是一个字符串数组类型的参数. ...

  2. 【Java基础】12、java中方法的参数传递机制

    问:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?  答:是值传递.Java 编程语言只有值传递参数.当一个对象实例作为一个 ...

  3. java中方法的参数传递机制(值传递还是引用传递)

    看到一个java面试题: 问:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?  答:是值传递.Java 编程语言只有值传递参 ...

  4. java中方法的参数传递机制_一个对象被当作参数传递到一个方法后

    一个例子: 在Boy.java类中 在Girl.java类中      在marry方法中的this指的是这个方法所属的对象的引用,在这里指的是girl这个对象 在BoyGirlTest.java测试 ...

  5. 我的Java开发学习之旅------>Java语言中方法的参数传递机制

    实参:如果声明方法时包含来了形参声明,则调用方法时必须给这些形参指定参数值,调用方法时传给形参的参数值也被称为实参. Java的实参值是如何传入方法?这是由Java方法的参数传递机制来控制的,Java ...

  6. Java方法之参数传递机制

    目录 Java方法之参数传递机制 基本数据类型 引用数据类型 综合练习 总结 Java方法之参数传递机制 Java方法中如果声明了形参,在调用方法时就必须给这些形参指定参数值,实际传进去的这个值就叫做 ...

  7. Java高频经典面试题(第一季)四:方法的参数传递机制

    考点? 方法的参数传递机制 String,包装类等对象的不可变性 方法的参数传递机制: ①形参是基本数据类型 传递数据值 ②实参是引用数据类型 传递地址值 特殊的类型:String.包装类等对象不可变 ...

  8. 《Java中方法的参数传递方式只有一种:值传递》

    //方法的参数传递机制(1):基本类型做形参的传递. class PrimitiveTransferTest { public static void swap(int a,int b) { //下面 ...

  9. java中的动态代理机制

    java中的动态代理机制 在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface).另一个则是 Proxy(Class),这一个类和接口是实现 ...

随机推荐

  1. Java基础——封装

    最近学习Java面向对象方面的知识点,一直没时间更新博客,因为这块的知识点真的蛮绕的.一个知识点一个知识点的往外冒,而且对于我这个初学者来说区分构造器和方法就花费了一整天的时间.现在准备再重新过一遍知 ...

  2. 配置lnmp

    ===================准备篇: 1.配置防火墙,开启80端口.3306端口vi /etc/sysconfig/iptables-A INPUT -m state –state NEW ...

  3. Redis客户端管理工具的安装及使用

    1.下载及安装 请到官网下载:www.treesoft.cn,要最新的版本treeNMS, window系统下载直接解压,就可以用了,免安装,免布署. 2.登录及连接参数配置 登录后,要配置连接参数信 ...

  4. VMWare、KVM、Virtualbox克隆或复制Linux虚拟机后eth0找不到的解决方案

    快速处理办法: cat /etc/sysconfig/network-scripts/ifcfg-eth0 sed -i '/UUID/d' /etc/sysconfig/network-script ...

  5. react实现双向绑定

    双向绑定功能是在项目中比较常见的一个需求,传统的js实现方式是添加监听函数的方式,Vue框架实现很简单,因为它本身就是基于双向绑定实现的,接下来我将讲解一下如何使用react实现双向绑定的功能是 首先 ...

  6. Mac: 易用设置

    Mac是一个类unix系统,因此很多命令是类似于Linux的.例如其中的Terminal, shell等等.但是软件源鉴于国内的网络环境,是不方便配置的. 中文输入法快速切换. 在不同的语言中可以用组 ...

  7. google和oracle闹掰,Java 会不会被抛弃?

    眼花缭乱的编程语言 程序界的语言实在太多,但有一种语言不得不说,那就是java语言,Java语言是Android系统的主要开发语言,现在和Google的关系不是很好,但是他会被淘汰吗?下面简单地分析一 ...

  8. javascript四舍五入tofixed

    //toFixed() 方法可把 Number 四舍五入为指定小数位数的数字. $("#renjunlirun").val(zongLiRun.toFixed(2));//金额

  9. C++学习笔记——大杂烩

    C++学习笔记--大杂烩                                                         by方阳 版权声明:本文为博主原创文章,转载请指明转载地址 h ...

  10. HTML5 Web SQL 数据库操作

    Web SQL 数据库 API 并不是 HTML5 规范的一部分,但是它是一个独立的规范,引入了一组使用 SQL 操作客户端数据库的 APIs. 以下是规范中定义的三个核心方法: openDataba ...