Java中的参数传递到底是值传递还是引用传递?

我相信这个问题困扰了很多的java学习者,即使是一些资深的java程序员,有的时候也难免在这些方面遇到了confusion。

我的问题来源于两本书《Think In Java 3rd Edition》和《Core Java 2: Volume I - Fundamentals》。

在TIJ中的P89页有这样一段话:

"The method argument list specifies what information you pass into the method. As you might guess, this infomation --like evrthing else in Java -- takes the form of objects. So, what you must specify in the argument list are the types of the objects to pass in and the name to use for each one. As in any situation in Java where you seem to be handing objects around, you are actually passing references.(底角注:With the usual exception of the aforementioned "special" data types boolean, char, byte ,short, int, long, float, and double. In general, though, you pass objects, which really means you pass references to objects.)"。请注意红色字体部分。由此可见,该书作者认为java中的参数传递都是引用传递。

在TIJ的P111页有这样一个实例程序:

public class PassObject {

	public static void main(String[] args) {

		Letter x = new Letter();

		x.c = 'a';

		System.out.println(x.c);

		f(x);

		System.out.println(x.c);

	}

	

	static void f(Letter l)

	{

		l.c = 'z';

	}



}

class Letter

{

	char c;

}

 

程序的输出结果为:

a

z

书中对这个程序的explanation是这样的:In many programming languages, the method f() would appear to be making a copy of its argument Letter y inside the scope of the method. But once again a reference is being passed,so the line

y.c='z';

is actually changing the object outside of f().

而在Core Java 2: Volume I - Fundamentals》中也有这样一段话:

The Java programming language always uses call by value. That means, the method gets a copy of all parameter values. In particular, the method cannot modify the contents of any parameter variables that are passed to it.

Many programming languages (in particular, C++ and Pascal) have two methods for parameter passing: call by value and call by reference. Some programmers (and unfortunately even some book authors) claim that the Java programming language uses call by reference for objects. However, that is false. Because this is such a common misunderstanding, it is worth examining a counterexample in detail. 

Let's try to write a method that swaps two employee objects:    

public static void swap(Employee x, Employee y) // doesn't work

    {

       Employee temp = x;

       x = y;

       y = temp;

    }

If the Java programming language used call by reference for objects, this method would work:     

Employee a = new Employee("Alice", . . .);     

Employee b = new Employee("Bob", . . .);     

swap(a, b);     // does a now refer to Bob, b to Alice?

    

However, the method does not actually change the object references that are stored in the variables a and b. The x and y parameters of the swap method are initialized with copies of these references. The method then proceeds to swap these copies.    

 // x refers to Alice, y to Bob     

Employee temp = x;     

x = y;     

y = temp;     

// now x refers to Bob, y to Alice  

  

But ultimately, this is a wasted effort. When the method ends, the parameter variables x and y are abandoned. The original variables a and b still refer to the same objects as they did before the method call.

   由此可见,该书作者认为Java中的参数传递都是值传递。

   众所周知,这两本书都是Java的权威。那出现这两种截然不同的观点,到底谁的正确呢?为什么会出现在这样的的issue呢?在TIJ中有个附录A对此有比较详细的介绍。但是作者还是比较坚定的认为:

By now you should be reasonably comfortable with the idea that when you're "passing" an object, you're actually passing a reference.

    这样看来,是不是Java中的参数传递有的时候是传递引用而有的时候传递的是值的拷贝呢?记得曾经在一篇帖子中看到过有另一种说法。“Java中传递对象都是引用传递方式,而传递Java的primitive类型(boolean,byte,short,int,long,float,double)时采用值传递方式。

    以下是我自己测试的的几个程序:

public class Assignment {

	public static void main(String[] args) {

		Number n1 = new Number();

		Number n2 = new Number();

		n1.i = 9;

		n2.i = 47;

		System.out.println(n1.i+" "+n2.i);

		n1=n2;

		System.out.println(n1.i+" "+n2.i);

		n1.i=27;

		System.out.println(n1.i+" "+n2.i);

		inrementIt(n1);

		System.out.println(n1.i+" "+n2.i);

	}

	

	static void inrementIt(Number n)

	{

		n.i++;

	}



}

程序的输出结果为:

9 47 47 47 27 27 28 28

从这个程序的输出结果看来,将n1传递给方法inrementIt()后,改变了n1的值(同时也改变了n2的值),这里的理解应该是引用传递。而我们再看下面这个例子:

public class VarOrRefTest {

 public static void main(String args[]) {

  Integer interger1, interger2;

  int i, j;

  interger1 = new Integer(10);

  interger2 = new Integer(50);

  i = 5;

  j = 9;

  System.out.println("Before Swap, Interger1 is " + interger1);

  System.out.println("Before Swap, Interger2 is " + interger2);

  swap(interger1, interger2);

  System.out.println("After Swap Interger1 is " + interger1);

  System.out.println("After Swap Interger2 is " + interger2);

  System.out.println("Before Swap i is " + i);

  System.out.println("Before Swap j is " + j);

  swap(i, j);

  System.out.println("After Swap i is " + i);

  System.out.println("After Swap j is " + j);



 }



 public static void swap(Integer ia, Integer ib) {

  Integer temp = ia;

  ia = ib;

  ib = temp;

 }



 public static void swap(int li, int lj) {

  int temp = li;

  li = lj;

  lj = temp;

 }



 }

}

程序的输出结果为:

Before Swap, Interger1 is 10 Before Swap, Interger2 is 50 After Swap Interger1 is 10 After Swap Interger2 is 50 Before Swap i is 5 Before Swap j is 9 After Swap i is 5 After Swap j is 9

这样看来,该程序中的swap函数都没有起到交换作用,也就是说,无论传递的是object类型还是primite类型的参数,都没能改变方法外的原有值。这里的参数传递方式更像是值传递。

我们知道,Java中取消了指针,目的是让程序更加容易理解和健壮。但是,在这个问题上好像让我们更加的迷惑,往往会出现一些让人意想不到的结果。

为了最大程度地避免这种问题的发生,TIJ书中建议我们在编程的过程中是尽量避免方法改变传入的参数值。更多的只是“用一下”这个参数,就像是“如果我要拧一下我的螺丝,我只是用一下扳手,而不是去把扳手改变”。

我自己的理解更多的是倾向与把Java中所有的参数传递都当成值传递方式。也就是说,所有的参数都是一个拷贝。因为我认为,即使传递的是一个对象,那么在传递过程中也是传递的是这个对象的引用的拷贝。

如果有哪位高人能够给一个更为确切的答案,小弟一定虚心领教。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值