Pass by "Reference Value"? Some clarification needed - java

I know that in Java, everything is passed by value. But for objects, it is the value of the reference to the object that is passed. This means that sometimes an object can get changed through a parameter, which is why, I guess, people say, Never modify parameters.
But in the following code, something different happens. s in changeIt() doesn't change when you get back to main():
public class TestClass {
static String str = "Hello World";
public static void changeIt( String s ) {
s = "Good bye world";
}
public static void main( String[] args ) {
changeIt( str );
System.out.println( str );
}
}
I'm guessing -- and I'd like confirmation -- that when you say s = "something" it's the same or equivalent to saying String s = new String("something"). Is this why s doesn't change? Is it assigned a whole new object locally which gets thrown away once you exit changeIt()?

that when you say s = "something" it's the same or equivalent to saying String s = new String("something")
Yes, pretty much. (though the JVM might do optimizations so that the same string literal used several times refers to the same String object).
Is this why s doesn't change? Is it assigned a whole new object locally which gets thrown away once you exit changeIt()
Yes. As you say, everything is passed by value in Java, even references to object. So the variable s in changeIt( String s ) is a different value from str you use in main(), it's just a local variable within the changeIt method.
Setting that reference to reference another object does not affect the caller of changeIt.
Note that the String object s refer to is still the same String as str refers to when entering the changeIt() method before you assign a different object to s
There's another thing you need to be aware of, and that is that Strings are immutable. That means that no method you invoke on a string object will change that string. e.g. calling s.toLowerCase() within your changeIt() method will not affect the caller either. That's because the String.toLowerCase() does not alter the object, but rather returns a new String object.

When you write
s = "Good bye world";
you are changing the value of s to be a reference to the new string. You are not changing the value of the string referenced by s.

Yes, now 'S' points to brand new object whose scope is limited to that method. String may not be perfect example to understand pass-by-value concept. Instead of string, let us say pass some mutable object reference and make changes to that assign new object inside the method. You don't see them outside of the object.
public class MyMain {
private static void testMyMethod(MyMain mtest) {
mtest=new MyMain();
mtest.x=50;
System.out.println("Intest method"+mtest.x);
}
int x=10;
public static void main(String... args)
{
MyMain mtest = new MyMain();
testMyMethod(mtest);
System.out.println("In main method: "+mtest.x);
}
}
Read second answers in this SO discussion.

Related

Java Function Call. Are non trivial Objects copied by reference?

I started with java a couple of weeks ago. Before that i had multiple years working with c/c++ on embedded targets and with c# with UI Stuff on Win PCs.
I got this very simple example:
public class StreamProcessing {
public static void main(String[] args) {
Stream stream = new Stream(); //after this line: Stream string empty
StreamFiller.fillStream(stream); //after this line: Stream string not empty any more
StreamPrinter.printStream(stream);
}
}
I'd expect that whatever StreamFiller.fillStream() does, the argument is copied. However it looks like fillStream is modifying the actual stream object itself.
The Stream class basically contains a string
public class Stream {
private String content = "";
int index = 0;
public char readChar() {
if (index < content.length()) {
return content.charAt(index++);
} else {
return 0;
}
}
public void writeString(String str) {
content += str;
}
}
The Streamfiller should modify it's stream copy but not the original reference
public class StreamFiller {
public static void fillStream( Stream stream ) {
stream.writeString( "This is a" );
stream.writeString( " stream." );
}
}
Please correct me if I'm wrong, but since the actual text of the string class is allocated on the heap, both the StreamProcessing () Stream object and the (supposed copied) local object of fillStream() point to the same address on the heap (yeah i now it's not an actual memory address like in c/c++ but some unique object identifier)
So is my assumption correct? Non trivial objects (aka objects allocated on the heap) are passed by reference?
thx for your help :)
The Java language does not let you make heap / stack distinction in your code the way C and C++ do.
Instead, it divides all data types in to two groups:
Primitive types:
These are simple built in numerical types such as int, double or boolean (not a numerical type in Java).
Note that String is not such a type!
Object types:
If it is a class, it is an object type. This goes for built in types such as String and for user defined types such as your Stream class.
For these types, all you ever see is a reference, whether you are looking at a local variable, class member, instance member, or function parameter.
Lets look at a simple example:
public class A {
public int a;
public static void main(String [] args) {
A var1 = new A();
A var2 = var1;
var1.a = 42;
System.out.println("var2.a = " + var2.a);
}
}
If you compile and run this example it will print 42.
In C++ the line A var2 = var1; would have invoked a copy constructor and created a new object but in Java there is no such thing. If you want a copy, you need to invoke clone method explicitly.
What is held in var1 and copied to var2 is just a reference.
So both vars "point" to the same object.
And again - it does not matter if the class is trivial or not. Even if a class is completely empty, you will still only be given and work with a reference to any object of this class.
As for the primitive types mentioned earlier, Java has wrapper classes such as Integer and Boolean for them.
You might want to read about "boxing" and "unboxing".
One more thing to note is that some types are immutable - that is, they do not provide a way to change their data once created.
String in Java is an immutable type, but it is also a bit different from any other type.
It has special privileges.
While Java does not support operator overloading like C++ does, for String type the language does provide a special + operator that preforms string concatenation.
How ever, since String objects are immutable, any concatenation operation will create a brand new String object, even one like this:
String a = "Hello";
a = a + " world!";
This creates a new string "Hello world" and stores the reference to it in a, leaving the reference to old "Hello" string to be garbage collected at some future point.
Even though in Java everything is passed by value, there is a difference between how primitive data types (such as int, char and boolean) and how reference data types are passed to a method.
When passing the value of a primitive data type, this value can only be changed in the scope of the particular method. When passing the value of a reference data type, the reference will remain the same but the value will change globally (or in whatever scope the object was initialised).
See also this for more information: https://docs.oracle.com/javase/tutorial/java/javaOO/arguments.html

what is the correct and best way to pass argument by reference to a method?

This is my code :
import java.util.LinkedList;
public class Main {
public static void main(String[] args) {
String str = new String("this is a text");
System.out.println(str);
getThis(str);
System.out.println(str);
}
private static void getThis(String str) {
str = "text changed";
}
}
and the output is :
this a text
this a text
I want str change after the getThis method called.
I know I should pass str by reference, and I know that this can be done by declaring the str as static and out of the main method and then call it in the method like this Main.str. But is it the correct way and standard way to pass by reference?
Java is not pass by reference, it's always pass by value. And for references.
It's passing references as values to the caller. You can do it by returning the String value from getThis() method and assigned to the same variable
public class Main {
public static void main(String[] args) {
String str = new String("this is a text");
System.out.println(str);
str = getThis();
System.out.println(str);
}
private static String getThis() {
return "text changed";
}
}
As others have stated Java is always pass by value with the slight caveat that when you pass in objects (like String) you are passing the value of a Reference to an object on the heap.
In your example, assignment has no effect outside the method and since Strings are immutable you can't really do much. If you passed in a StringBuilder then you could mutate the state of the object on the heap.
More generally instead of passing in an Object x you can pass in a wrapper object that contains a set method. Java provides an AtomicReference which allows you to do this.
In java, "references to objects are passed by value". So, any
reference to a non-primitive object that you pass will be directly
used and changes will be reflected in the original object.
Also, as a side note, Strings are immutable, so, you
will get a new String if you try to change it (Strings cannot be changed), the original one will not be changed.

"Variable may not have been initialized"

I've got a method that creates a String and another method that changes Strings
void create(){
String s;
edit(s);
System.out.println(s);
}
void edit(String str){
str = "hallo";
}
My compiler says that it "may not have been initialized".
Can someone explain this?
Variable may not have been initialized
As you define the s inside a method you have to init s in it somewhere every variable in a program must have a value before its value is used.
Another thing not less important, your code won't never work as you expected cause
Strings in java are inmutable then you cannot edit your String, so you should change your method edit(Str s).
I Change your code to something like this but i think your edit method should do another thing rather than return "hallo".
void create(){
String s=null;
s =edit(); // passing a string to edit now have no sense
System.out.println(s);
}
// calling edit to this method have no sense anymore
String edit(){
return "hallo";
}
Read more about that java is passed by value in this famous question : Is Java "pass-by-reference"?
See this simple Example showing that java is passed by value. I cannot make an example with only Strings cause Strings are inmutable. So i create a wrapper class containing a String that is mutable to see differences.
public class Test{
static class A{
String s = "hello";
#Override
public String toString(){
return s;
}
}
public static void referenceChange(A a){
a = new A(); // here a is pointing to a new object just like your example
a.s = "bye-bye";
}
public static void modifyValue(A a){
a.s ="bye-bye";// here you are modifying your object cuase this object is modificable not like Strings that you can't modify any property
}
public static void main(String args[]){
A a = new A();
referenceChange(a);
System.out.println(a);//prints hello, so here you realize that a doesn't change cause pass by value!!
modifyValue(a);
System.out.println(a); // prints bye-bye
}
}
You declare local variable s in method create, so that you need to initialized it before you use it. Remember that java does not have default value for local variable.
Init String s = "" or whatever value than your code will run normally.
try to initialize the string "s" to a null value, since you have declared a variable "s" but it has not been initialized. Hence it can't pass the reference of that variable while used as parameter.
String s = null;
Hope this helps
Give your variable S a value or as Jeroen Vanneve said "Change it to String s = null;"

How passing an Object as argument differs from passing an Array as argument?

I have come across two scenarios.
One in which an array is passed as argument to a method and if it is updated in the called method, it is reflecting in the calling method as well.
But in the second scenario, a String Object is passed as argument. The object is updated in the called method, but it doesn't reflect in the calling method.
I want to understand what is the difference between two, even though in both cases, value (of reference) is passed as argument. Please see below snippets.
Scenario 1:
class Test {
public static void main(String[] args){
int a[] = {3,4,5};
changeValue(a);
System.out.println("Value at Index 1 is "+a[1]);
}
public static void changeValue(int b[]){
b[1] = 9;
}
}
Output:
Value at Index 1 is 9
Here, reference (Memory Address) related to array a is passed to changeValue. Hence, b is just pointing to same address as a does.
Hence, whether I say b[1] or a[1], it is referring to same memory address.
Scenario 2:
public class Test {
public static void main(String[] args){
String value = "abc";
changeValue(value);
System.out.println(value);
}
public static void changeValue(String a){
a = "xyz";
}
}
Output:
abc
If I apply the same logic here, String Object VALUE's reference (Memory Address) is being passed to changeValue, which is recieved by a.
Hence, now a should be referring to the same memory location as VALUE does. Therefore, when a="xyz" is executed, it should replace "abc" with "xyz".
Can someone please point out where my understanding goes wrong? Thanks in advance!!
Java passes all its arguments by value. This means that a copy of the pointer to the String is made, and then passed to the method. The method then makes the pointer point at another object, but the original pointer still points to the same String.
This is not the same thing:
in the first example, you pass an array reference as an argument, therefore you correctly expect it to be changed by manipulating the reference directly;
in the second example however, you pass an object reference, sure -- but you change the reference itself in the method. Changes to a are not reflected when the method returns.
Consider any object:
public void changeObj(Object o)
{
o = new Whatever();
}
a new object is created, but it won't change o in the caller. The same happens here.
You're doing different things; with the string you set the parameter value, with the array you set something belonging to the reference.
For an equivalent array example you'd need to try setting the array reference to a new array:
public static void changeValue(int[] b) {
b = new int[] { 42, 60 };
}
The original array won't be changed.
The difference here is simple, and it is not actually about immutability of strings, as some other answers (now edited or deleted) might have originally implied. In one version (with the string), you have reassigned the reference, and in other version (with the array), you haven't.
array[0] = foo; // sets an element, no reassignment to variable
array = new int[] { 1,2,3 }; // assigns new array
obj = "hey"; // assigns new value
When you reassign the variable, you are not going to observe that change outside of the method. When you change elements of an array without reassigning the array variable, you will observe those changes. When you call a setter on an object without reassigning the actual variable of the object, you will observe those changes. When you overwrite the variable (new array, assigning new value, creating new object, etc.) those changes will go unobserved.
Arguments are passed (or copied) by value. The variable inside the method has the same value as the variable on the outside at the beginning. The variables are not linked, and they are not aliases for one another. They just happen to contain the same value. Once you reassign the value to one of them, that is no longer true! The variable on the outside is not affected by the variable on the inside, or even another local variable. Consider
Foo foo = new Foo();
Foo other = foo;
foo.setBar(1);
int bar = other.getBar(); // gets 1
foo = new Foo();
foo.setBar(42);
int bar2 = other.getBar(); // still gets 1
foo and other only referenced the same object for a time. Once foo was assigned a new object, the variables no longer had anything in common. The same is true for your reassignments to the parameter variable inside your method.
Thank you all for answers and updates..
I understood the difference between scenario 1 and 2 as below..
In scenario 1, the array reference is passed. The called method just updates one of the elements pointed by the reference.
While in scenario 2, the reference is passed, but when the called method assigns "xyz" to the reference variable (pointer), it actually creates a new String Object and its reference is assgined to a local reference variable 'a' (Pointer now points a different objct).
The code in called method is as good as
a = new String("xyz");
Hence, the object in called method and calling method are absolutely different and indepenedent and have no relation with each other.
The same could have happened with scenario 1, if instead of doing
b[1] = 9;
I would have used
b = new int[] {8,9,10};
I understood, Mutability fundamentals would have come in action, if I might have done like below..
String a="abc";
a="xyz";
In this case, object "abc" was being pointed by 'a'. When 'a' is assigned the duty to point to a new object "xyz", a new object "xyz" is created, which is not replacing the existing object "abc". i.e. "abc" is still existing but has no reference variable to keep itself accessible anymore. This non-replacement property is because of Immutability of String.

Could I change the reference inside one method with this reference as argument in Java?

private static void changeString(String s) {
s = new String("new string");
}
public static void main(String[] args) {
String s = new String("old string");
changeString(s);
System.out.println(s); // expect "new string"
}
How could I make the output of "new string" happen with s as the only argument to method changeString?
thanks.
In Java arguments are passed by value, object arguments pass a reference to the object, this means that you can change the reference of the argument, but that does not change the object you passed the reference to. You have two possibilities, return the new object (preferred) or pass reference to a container that can receive the new reference (collection, array, etc.) For example:
private static String changeStringAndReturn(String s) {
return new String("new string");
}
private static void changeStringInArray(String[] s) {
if (null != s && 0 < s.length) {
s[0] = new String("new string");
}
}
References in Java are passed by value, so even if you modify the reference inside the function, changes won't be reflected back to the calling function because what you modify inside the function is just a copy of the original reference not the original reference itself.
But you can return the new string from your changeString method instead of trying to modify the reference there(inside the function) itself.
Only if you make the function
private static void changeString(String[] s) {
s[0] = new String("new string");
}
String are immutable, and Java has no concept of a 'pointer-to-a-reference' as a first class datatype. If you don't like the above, you can make a little class containing a single String field.
You can, of course, return the new string from your changeString method instead of trying to change it in place.
Alternately, you can create an object that wraps or contains a string, and pass that in. The ChangeString method would change the string that was internal to your object, and the main method would still be holding a reference to that object.
Otherwise, you can't do this. String is immutable, and java always passes objects as a value that is a pointer to a particular object. Change where you're pointing, and you aren't referencing the same object anymore.
Strings are immutable in Java and parameters are passed by value so you can't change them (there is not equivalent to ref in C#). You can pass in a StringBuilder and change it's contents just as easily.
A: You can't, in Java object references are pass by value.
If you really need to, you can create a wrapper like this and use it the way you expected:
private static void changeString( _<String> str) {
str.s("new string");
}
public static void main(String[] args) {
_<String> s = new _<String>("old string");
changeString(s);
System.out.println(s); // prints "new string"
}
I know this is old, but, just for posterity, most of this is mostly wrong.
In Java, non-primitives are passed by reference. Primitives (boxed and unboxed) are passed by value.
To directly answer OP, you're getting a reference to the actual String you passed. However, as #bmarguilies correctly pointed out - Strings are immutable in Java. Any assignment to a String creates a copy, then assigns the newly-assembled copy to the reference.
This is not possible in Java as everything in Java is passed by value. In this case where the argument is an object reference it is the value of the reference that is passed into the method, not the reference itself.
Java does not allow out parameters like C#, so you will not be able to achieve this as such.
Its ugly but you could change the String to global static:
private static String s;
private static void changeString(String t) {
if(s.equals(t))
s = new String("new string");
}
public static void main(String[] args) {
s = new String("old string");
changeString(s);
System.out.println(s); // expect "new string"
}

Categories