Java is Pass-by-Value, Never pass by reference

来源:互联网 发布:直播变脸软件 编辑:程序博客网 时间:2024/04/30 06:46

The Litmus Test

There's a simple "litmus test" for whether a language supports pass-by-reference semantics:

Can you write a traditional swap(a,b) method/function in the language?

A traditional swap method or function takes two arguments and swaps them such that variables passed into the function are changed outside the function. Its basic structure looks like

Figure 1: (Non-Java) Basic swap function structure

swap(Type arg1, Type arg2) {    Type temp = arg1;    arg1 = arg2;    arg2 = temp;}

If you can write such a method/function in your language such that calling

Figure 2: (Non-Java) Calling the swap function

Type var1 = ...;Type var2 = ...;swap(var1,var2);

actually switches the values of the variables var1 and var2, the language supports pass-by-reference semantics.

For example, in Pascal, you can write

Figure 3: (Pascal) Swap function

procedure swap(var arg1, arg2: SomeType);    var        temp : SomeType;    begin        temp := arg1;        arg1 := arg2;        arg2 := temp;    end;...{ in some other procedure/function/program }var    var1, var2 : SomeType;begin    var1 := ...; { value "A" }    var2 := ...; { value "B" }     swap(var1, var2);    { now var1 has value "B" and var2 has value "A" }end;

or in C++ you could write

Figure 4: (C++) Swap function

void swap(SomeType& arg1, Sometype& arg2) {    SomeType temp = arg1;    arg1 = arg2;    arg2 = temp;}...SomeType var1 = ...; // value "A"SomeType var2 = ...; // value "B"swap(var1, var2); // swaps their values!// now var1 has value "B" and var2 has value "A"

(Please let me know if my Pascal or C++ has lapsed and I've messed up the syntax...)

But you cannot do this in Java!


Now the details...

The problem we're facing here is statements like

In Java, Objects are passed by reference, and primitives are passed by value.

This is half incorrect. Everyone can easily agree that primitives are passed by value; there's no such thing in Java as a pointer/reference to a primitive.

However, Objects are not passed by reference. A correct statement would be Object references are passed by value.

This may seem like splitting hairs, bit it is far from it. There is a world of difference in meaning. The following examples should help make the distinction.

In Java, take the case of

Figure 5: (Java) Pass-by-value example

public void foo(Dog d) {    d = new Dog("Fifi"); // creating the "Fifi" dog}Dog aDog = new Dog("Max"); // creating the "Max" dog// at this point, aDog points to the "Max" dogfoo(aDog);// aDog still points to the "Max" dog

the variable passed in (aDog) is not modified! After calling foo, aDog still points to the "Max" Dog!

Many people mistakenly think/state that something like

Figure 6: (Java) Still pass-by-value...

public void foo(Dog d) {     d.setName("Fifi");}

shows that Java does in fact pass objects by reference.

The mistake they make is in the definition of

Figure 7: (Java) Defining a Dog pointer

Dog d;

itself. When you write that definition, you are defining a pointer to a Dog object, not a Dog object itself.




Follow up from stackoverflow.com

I posted the following as some clarification when a discussion on this article arose on http://stackoverflow.com.

The Java Spec says that everything in java is pass-by-value. There is no such thing as "pass-by-reference" in java.

The key to understanding this is that something like

Figure 14: (Java) Not a Dog; a pointer to a Dog

Dog myDog;

is not a Dog; it's actually a pointer to a Dog.

What that means, is when you have

Figure 15: (Java) Passing the Dog's location

Dog myDog = new Dog("Rover");foo(myDog);

you're essentially passing the address of the created Dog object to the foo method. (I say essentially b/c java pointers aren't direct addresses, but it's easiest to think of them that way)

Suppose the Dog object resides at memory address 42. This means we pass 42 to the method.

If the Method were defined as

Figure 16: (Java) Looking at the called method in detail

public void foo(Dog someDog) {    someDog.setName("Max");     // AAA    someDog = new Dog("Fifi");  // BBB    someDog.setName("Rowlf");   // CCC}

Let's look at what's happening.

the parameter someDog is set to the value 42

at line "AAA"
someDog is followed to the Dog it points to (the Dog object at address 42) that Dog (the one at address 42) is asked to change his name to Max
at line "BBB"
a new Dog is created. Let's say he's at address 74 we assign the parameter someDog to 74
at line "CCC"
someDog is followed to the Dog it points to (the Dog object at address 74) that Dog (the one at address 74) is asked to change his name to Rowlf then, we return

Now let's think about what happens outside the method:

Did myDog change?

There's the key.

Keeping in mind that myDog is a pointer, and not an actual Dog, the answer is NO. myDog still has the value 42; it's still pointing to the original Dog.

It's perfectly valid to follow an address and change what's at the end of it; that does not change the variable, however.

Java works exactly like C. You can assign a pointer, pass the pointer to a method, follow the pointer in the method and change the data that was pointed to. However, you cannot change where that pointer points.

In C++, Ada, Pascal and other languages that support pass-by-reference, you can actually change the variable that was passed.

If Java had pass-by-reference semantics, the foo method we defined above would have changed where myDog was pointing when it assigned someDog on line BBB.

Think of reference parameters as being aliases for the variable passed in. When that alias is assigned, so is the variable that was passed in.