//C++ Example
#include
using namespace std;
int doHello (std::string&);
int main() {
std::string str1 = \"perry\";
cout <<
In C++ there are two parameter passing forms, call by value and call by reference. (The better name for call-by-value is call-by-new-L-value, but I digress.)
In Java programs all parameters are passed by value. [There are some people that think that Java passes objects by reference, but they're wrong—see the long answer below—Java passes the reference value of Objects, but that is not the same thing at all.]
The value of a Java Object (and what is passed) is a pointer to the object (confusingly, but accurately, called a reference to the object), and calling a Java object method, for example, obj.meth()
, de-references the pointer value of obj
before calling the method on the object found there.
In C++ the same call would look like: obj->meth()
.
If you manipulate the object (for example, by calling a method on it) passed as a parameter to a method in Java, this may modify the object, and if the caller still has a variable holding that object it can see these changes. If you merely assign to the (local) parameter variable, you simply update the pointer stored in the local variable—the object is unaffected, and the caller will not notice this.
So, the short answer to your question is that Java passes by value, and the call-by-reference mechanism of C++ is not available in Java.
To understand what's going on here we need to understand the mechanics of variables and parameter passing a bit more.
Generally speaking, a variable in a programming language is an identifier that is associated with a value. Variables that denote values manipulated at runtime (we are here ignoring macro variables, constants that are 'compiled away', and so forth) have to have two values associated with them. Here is an example that explains why:
int a = 0;
a = a + 1;
a
is the variable, and in the line a = a + 1;
it is referred to twice. Once, (on the right hand side of the =
) the value 0
is meant and once, (on the left hand side of the =
) the location of the value of a
is meant (that is, where the integer value of a
is stored). Only by accessing the location can the program update the value which is contained in it.
These two values associated with a variable are called the R-value and the L-value. (These names were invented by Christopher Strachey; I'm a fan.) Other constructs in the language refer to one or other of these values depending upon context. For example:
a[x-1] = x;
refers to the R-value of x
twice (even though one is on the left-hand-side of the =
), but only the L-value of a
(or at least one of the elements of the array a
).
In most programming languages, we explicitly manipulate the R-values of variables, and leave the compiler (from the form of the program) to manage the L-values automatically for us. We have to understand them a bit (in C, in particular with arrays, we assume a lot about L-values), but most of the time we don't have to know the mechanics.
Look at this call:
int p=1;
obj.meth(p);
…
void meth(int a) {…a…}
What happens to the parameters here? First the R-value of p
is calculated, then, when meth
is 'entered', a new variable a
is constructed. It is given a new L-value (this location is normally on a stack of some sort) and the R-value of the parameter is stored in the location.
Now, when a
appears in meth
it has the same R-value that p
had (initially) and a new L-value. Changes to a
's R-value make no difference to the variable p
. This is called call-by-value (more properly, call-by-new-L-value).
Look at this C++ call:
int p=1;
obj.meth(p);
…
void meth(int& a) {…a…}
Here the L-value of the parameter p
is calculated, and when the variable a
is constructed it is “given” the same L-value as p
. Then any use of a
in the body of meth
is just like a use of p
. Assignment, for example will alter the stored value in p
as well as (apparently) the value in a
.
Java doesn't do this.
Java does not have pass by reference (which you were using in the C++ code) at all. The references are passed by value. (The values of str
and str1
aren't objects at all, they're references - it really helps to keep the two concepts very separate.)
Typically you would use a return value to return a new reference if you need to:
str1 = doHello(str1);
Note that String is slightly different to List etc, because strings are immutable. To modify a collection (well, any mutable collection) you don't need to create a new one, you just modify the object via the original reference:
public static void addHello(List<String> items)
{
items.add("Hello");
}
You could then call this like so:
List<String> list = new ArrayList<String>();
addHello(list);
System.out.println(list.get(0)); // "Hello"
The difference between mutating an existing object and changing the value of a variable to refer to a different object is crucial. If you want to leave the existing collection alone and create a new one, you'd have to do that explicitly:
public static List<String> withHello(List<String> items)
{
List<String> newList = new ArrayList<String>(items);
newList.add("Hello");
return newList;
}
You'd then call it like this:
List<String> empty = new ArrayList<String>();
List<String> newList = withHello(empty);
System.out.println(empty.size()); // Prints 0
System.out.println(newList.size()); // Prints 1
Does this answer everything you needed?
The code you have written in java is equivalent to this C++ code:
int main() {
std::string *s1;
s1 = new std::string("perry");
std::cout << s1->c_str() << std::endl;
doMojo( s1 );
std::cout << s1->c_str() << std::endl; // not pieterson
}
void doMojo( std::string *str ) {
str = new std::string("pieterson");
std::cout << str->c_str() << std::end;
}
perhaps now you see what happens. The C++ references actually wrap around the pointers.. so without the references your code would look like this:
int main() {
std::string *s1;
s1 = new std::string("perry");
std::cout << s1->c_str() << std::endl;
doMojo( s1 );
std::cout << s1->c_str() << std::endl; // now pieterson!
}
void doMojo( std::string *&str ) {
str = new std::string("pieterson");
std::cout << str->c_str() << std::end;
}
notice the innocuous ampersand:
void doMojo( std::string *&str ) {
That really is the secret. This, and the previous answer that explains about immutability of java strings should make you appretiate the difference between java and c++ references.
Cheers,
jrh.
String in java is immutable, it can not be modified once created. Using assignment in doHello() actually create a new string object instead of modifying the existing one. In result, the 2 java references(one in main, the other in doHello) to string reference to 2 different strings respectively. That's why you see different results between C++ and Java.