Java参数传递方式

原文:http://blog.sina.com.cn/s/blog_59ca2c2a0100qhjx.html,我作了些修改并增加了一个实例,增加对比

本文通过内存模型的方式来讨论一下Java中的参数传递。

1、基本类型参数的传递方式

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

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;
 }

}

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

调用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的值也就没有改变。

2、对象类型参数的传递方式

1)先看下边的示例代码:

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!";
 }

}

运行的结果如下:

调用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!

2)我们再来看个例子

class Dog{
	int x,y;
	int z=1;
	Dog(int x,int y){
		this.x=x;
		this.y=y;
	}
	void move1(Dog d){
		d.x=d.x+10;
		d.y=d.y+10;
	}
	void move2(Dog d){
		Dog d2=new Dog(100, 100);
		d=d2;

	}
}
public class UseDog {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Dog d1=new Dog(10,10);
		d1.move2(d1);
		System.out.print(d1.x+","+d1.y+":");
		d1.move1(d1);
		System.out.println(d1.x+","+d1.y+";");
	}

}

输出结果为:10,10:20,20;

先解释10,10:当执行d1.move2(d1)时,d1(引用地址)传入move2中,栈内存开辟空间存入。之后新建对象d2,并将d2(引用地址)赋给d,那么d就指向了对象d2。因此并未改变d1所指向的堆内存值。

再解释20,20:当执行d1.move1(d1)时,d1(引用地址)传入move1中,栈内存开辟空间存入,即d指向了d1所指向的堆内存值。这时在move1中改变d所指向的对象的值,那么d1所指向的对象值也改变了。因为d和d1指向的是同一个对象。

3、总结

无论是基本类型作为参数传递,还是对象作为参数传递,实际上传递的都是值,只是值的的形式不用而已。第一个示例中用基本类型作为参数传递时,将栈内存中的值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变量的值并没有改变。

时间: 2024-10-19 05:01:59

Java参数传递方式的相关文章

java参数传递方式-值传递

demo1: public static void methodOne(int a) { a++; System.out.println("methodOne a="+a); } public static void main(String[] args) { int a = 100; methodOne(a); System.out.println("main a="+a); } //说明Java参数为值传递 输出结果为: methodOne a=101 main

java方法参数传递方式只有----值传递!

在通常的说法中,方法参数的传递分为两种,值传递和引用传递,值传递是指将实际参数复制一份传递到方法中, 在方法中的改动将不会影响到实际参数本身,而引用传递则是指传递的是实际参数本身,在方法中的改动将会影响到实 际参数本身.但是,在java中只有值传递,没有引用传递!那么,为什么当方法参数是基本数据类型时表现是值传递, 而当是引用类型时表现的是引用传递形式呢? Java内存区域中含有java堆和虚拟机栈两个内存区域(并不是只是将java内存区分为这两个内存区域,此外还有程 序计数器,本地方法栈以及方

Java和C参数传递方式的比较

java中的形参是复制实参的一份拷贝(对于引用类型则是复制引用的拷贝,在栈中的拷贝),所以在函数中改变形参是无法改变实参的值的,改变引用只是将形参所代表的引用指向另外的新的对象,而实参的引用还指向原来的对象,改变形参引用的成员当然会影响实参引用成员的值,因为他们的引用都指向同一个对象.[Java只有值传递Java只有值传递Java只有值传递,重要的事情说3遍] 示例代码: public class ExchangeValue { public static void main(String[]

Java函数参数传递方式详解

在阅读本文之前,根据自己的经验和理解,大家可以先思考并选择一下Java函数的参数传递方式: A. 是按值传递的? B. 按引用传递的? C. 部分按值部分按引用? 此处暂不宣布正确答案,我们通过一个简单的例子让大家自己找答案: 1. 先定义一个类型Value Java代码   public static class Value { private String value = "value"; public String getValue() { return value; } pub

java中参数传递方式话题终结实例

java新手入门面临的一个经典的话题,本文意在终结这个话题,java中有说法:Java里面参数传递都是按值传递,怎么理解这句话?用文字说明恐怕不容易说明白,说明白恐怕也难以想明白. 前提 先明确一下,按值还是按引用的概念,它是来自c++语言,引用不是汉语词典中的一个词,而是c++的概念--"&"这个符号还记得吧? 为什么有这个话题呢?其一,是对按引用传递理解不透彻:其二,诸多java书籍及讨论论点并没有切中要害.?? 一句话概括,按值传参还是按引用传参,既然是参数传递方式,那么

Java学习(八)方法参数传递方式

package cn.com.postel.wbb.javaextends; /** * java中的方法的参数传递方式只有一种:值传递. * 所谓的值传递就是将实际数值复制一份传入到方法内,而参数本身不会受到影响. * @author wubinbin * */ public class ValueTrans { public static void main(String[] args) { int a = 1; int b = 2; swap(a, b); System. out.prin

Java-Spring MVC:JAVA之常用的一些Spring MVC的路由写法以及参数传递方式

ylbtech-Java-Spring MVC:JAVA之常用的一些Spring MVC的路由写法以及参数传递方式 1.返回顶部 1. 常用的一些Spring MVC的路由写法以及参数传递方式. 这是一些测试代码: package com.my.controller; import java.io.PrintWriter; import java.util.Date; import javax.servlet.http.HttpServletRequest; import javax.servl

【整理】--C++三种参数传递方式

在C++中,共有三种参数传递方式: 按值传递(pass by value) 地址传递(pass by pointer) 引用传递(pass by reference) (1)按值传递的过程为:首先计算出实参表达式的值,接着给对应的形参变量分配一个存储空间,该空间的大小等于该形参类型的,然后把以求出的实参表达式的值一一存入到形参变量分配的存储空间中,成为形参变量的初值,供被调用函数执行时使用.这种传递是把实参表达式的值传送给对应的形参变量,故称这种传递方式为“按值传递”. 使用这种方式,调用函数本

C语言函数参数传递方式

C语言中函数的参数传递方式有两种:传值.传地址. 1.传值调用 在被调用函数中开辟出一个临时的内存空间,在主调用函数进入被调用函数前,会将实参进行拷贝,拷贝的数据放在该临时内存空间中,供被调用函数使用.当被调用函数返回时,该临时内存空间即被释放. 1 #include<stdio.h> 2 3 int test_func(char *q) 4 { 5 long lAddr=&q; // 被调用时,光标选中“&q”,显示结果(随机)为:0x002df7ac {0x013058b8