Questions are based on the following code :
class Car implements Cloneable
{
//Relevant constructor
String name;
int wheels;
Car another;
public Object clone()
{
/*DEEP
Car n = new Car(this.name,this.wheels);
n.another = new Car(this.another.name,this.another.wheels);
return n;
*/
return super.clone() ;
}
}
main(String[] args)
{
Car c1 = new Car("merc",4);
c1.another = new Car("baby merc",55);
Car c2;
c2 = (Car)c1.clone();
//PART 1
//HERE I TRY TO CHANGE object c2's another's name to "Han"
System.out.println(c1.another.hashCode() == c2.another.hashCode());
/*POINT 1*/ c2.another.name = "Han";
System.out.println(c1.another.hashCode() == c2.another.hashCode());
//PART 2
String s = new String("gG");
System.out.println(s.hashCode());
s ="JAJA";
System.out.println(s.hashCode());
}
In part 1 I change the object c1's member another's member name
to be different.
In part 2 I create a string , check its hash code , then change its value and then again check its has code.
Output :
true
true
3264
2269358
I don't understand that when I change c2.another.name then
c1.another.name also points to that same exact old location that the previous string "baby merc" had occupied. Shouldn't the operation at POINT1 cause a new reference location to be returned thus resulting in c2.another pointing to a new location while c1.another points to the old?
And why does the same not happen when I make a string point to a new
string? A new area in heap is allocated and the reference variable
holds the reference of the new location. In case of c1 and c2
the reference location doesn't change even after modification! Why?
c2 = (Car)c1.clone(); // c1.another == c2.another (yes, the references are equal, so I am using ==)
I don't understand that when I change c2.another.name then c1.another.name also points to that same exact old location that the previous string "baby merc" had occupied. Shouldn't the operation at POINT1 cause a new reference location to be returned thus resulting in c2.another pointing to a new location while c1.another points to the old?
Both c1 and c2 share the same another object / reference. They are not concerned about what changes inside it. The second line returns true because you are changing something inside another, assume that you have a box of coins and 2 people holding it. If you remove 2 coins from it, the remaining coins will be the same for both people holding it.
And why does the same not happen when I make a string point to a new string? A new area in heap is allocated and the reference variable holds the reference of the new location. In case of c1 and c2 the reference location doesn't change even after modification! Why?
You are creating 2 different objects and making the same reference point to it (one after another), so you have 2 different hash codes. This has got nothing to do with cloning.
c1.another is the same object as c2.another, therefore they will always have exactly the same hash code.
Moreover, the standard hash code is based on the objects identity. Changing the fields of the object does not change its hash code.
Whenever a clone() method is called on an object that contains members of any class type, then only reference variables to those members are copied.
That's why when the following statement is executed:
c2 = (Car)c1.clone();
A new object is created in the heap for c2, it contains a reference to the object another. C1 also contains reference to another. Now both c1 and c2 has reference variables that point to another. Therefore both c1 and c2 can modify the state of another.
In the case of String, a new area is allocated in the heap,
whenever you use a 'new' keyword to create a String object
When you create a string literal, and that string does not exist in the string pool so far.
Related
This question already has answers here:
Is Java "pass-by-reference" or "pass-by-value"?
(93 answers)
Closed 7 years ago.
I have a list of Cell object that represent a board game inside the board class.
Cell boardGame[][] = new Cell[8][8];
I needed a temporary cell to try the player move on him and compare it to the other cells, so I though that I could use a java pass-by-value to do it.
test(board.boardGame);
board.printBoard();
private static void test(Cell[][] boardGame) {
Cell c = new Cell((new Soldier(ChessPiece.QUEEN, Color.WHITE)), 7, 4);
boardGame[7][7] = c;
}
I read some post about java here, but apparently I still didn't catch it 100%.
I expected to see only one white queen on the board, but I saw two.
I know that if you pass a reference you can change its values, but I though that if I would pass the array itself its members won't be modified unless I would execute a return.
Please help me to understand this subject better.
Thanks
Edit:
I think I don't understand when it called attributes and where it doesn't.
I though the different it if you are call "new" or not.
When its part of another object it called attribute right? but every object can be created as part of another object. I can create a new string in dog class and then create the dog class in the animal class and then create it in another class. So only the top class is in the stack?
For exemple:
public class Board { //fake class
int num= 0;
public void test(int i){
i = 1;
}
}
and on another class:
public class Main {
static void outsideTest(Board board){
board.num = 1;
}
public static void main(String[] args) {
Board board = new Board();
System.out.println(board.num);
board.test(board.num);
System.out.println(board.num);
outsideTest(board);
System.out.println(board.num);
}
}
Now I didn't understand why on test() method the num didn't change and on outsideTest() the num change, num as been created in the heap because its part of the board object, so its need to be changed on both cases no?
The best and least confusing way to remember it is as follows: Java passes everything by value, that includes the references. :)
When you have a variable:
Object a = new Object();
you don't actually have an object stored in a. What you have is a reference to an object somewhere in memory.
Likewise when you call a method on an object:
String b = a.toString();
you don't do that. What you call is a method that uses the data of the referenced object as its context.
So when you pass an object as an argument
System.out.println(b);
You don't pass the whole object. You pass the reference and that reference is passed by value.
edit:
If the reference were not passed by value, but by reference, you could do something like this, which fortunately you can't.
public void swap(Object a, Object b){
Object swap = a; a = b ; b = swap;
}
String a = "a";
String b = "b";
swap(a,b);
// would print "b a" if references were
// passed by reference but instead prints
// "a b" as they're passed by value.
System.out.println(a + " " b);
The reference to the object is passed by value, which means that
boardGame = new Cell[8][8];
does not do any harm, but changing anything you get from boardGame does.
Java is essentially always "pass-by-value".
Caveat: It passes the value stored in the memory for the variable.
For primitive data types, the memory is allocated in stack space, whereas for objects reference memory is allocated in stack space but the object itself is created in heap. Similar can be stated for arrays too though they are not exactly objects in a strong sense.
Diagrams below would make it clearer.
Your Cell object 2D array should seem something like anObjectArrayVar (not exactly as the ref in the diagram pointing to the objects should now be pointing to the rows and we would need another level of allocation in heap in between ref and objects for each row (a set of cells refering to the objects).
So, when you pass boardGame, the value stored in the stack is passed that stores the reference to the array of objects (just like the value stored in the anObjectArrayVar). If say the list of refs is stored in location numbered 50 then anObjectArrayVar would have stored that and it passes that value to the test method when we call it. In such a scenario the test method wont be able to goto memory location anObjectArrayVar and change its value (to say 100) as it has only a copy of the value but it could easily change what it refers to(directly or indirectly) like the values in ref or the next level (and add new objects as in your case adding a new cell with queen) or the objects pointed to by them and those changes would reflect through out the program!
I would also like to draw your attention to the fact that the code
boardGame[7][7] = c;
would replace the current cell (as well as the soldier currently in it) which would create major issues if there was originally a soldier in that place at that point in the game. The game state would actually change.
As a suggestion (given the limited knowledge about your design) I would say at least save the cell in some other value in test method before replacing it.
Cell old = boardGame[7][7];
//now do all your operations
boardGame[7][7] = old;//just before returning from the function
I have read it everywhere that Java stores Object references in array. Even i demonstrated it myself.
But then i changed the state of the object, means i changed the values of attributes and saved in array, and i can retrieve multiple state of same object. If Array saves only references then how do references holds the states.
For example:
class Test{
String id;
}
Test[] testArr = new Test[2];
test = new Test();
test.id = "ABC"
testArr.add(test)
test.id = "XYZ"
testArr.add(test)
now in case above if we would storing only references then second assignment would have overwritten id value of test object and both entry in array would have same value of id, but this is not the case and we can retrieve the id values ABC and XYZ. I am confused!
Take a look at this code:
class Test {
String id = "A";
}
public class Main {
public static void main(String[] args) {
ArrayList<Test> list = new ArrayList<Test>();
// Adding some Test Object to the list.
Test foo = new Test();
list.add(foo);
System.out.println("Value of foo id: " + foo.id);
// Retrieving the Object from the list & changing the value
Test bar = list.get(0);
bar.id = "B";
System.out.println("Value of foo id: " + foo.id);
}
}
output:
Value of foo id: A
Value of foo id: B
As you can see the arraylist only holds references. If you retrieve the Object from the list and change something in it, the original Object will be changed too
To answer your question. Yes. All variables are simply references to the location of an object, not just an ArrayList. Writing MyObject obj = new MyObject(); creates a new object however the variable obj is just a reference to the location of that object in the Heap (aka memory).
An ArrayList (without looking at its actual implementation) simply stores each reference to the location of an object as an index of the ArrayList rather than a unique variable.
In a bit more detail: You need to understand what each part of creating an object does. Try to imagine it in this way:
Each object is located in a memory address that takes up the number of bytes all its fields use. Lets imagine we have an object called MyObject that takes up 100 bytes. When we create an Object using the new keyword (ie. new MyObject()) it is stored in a memory location in the heap (which is an area of memory set aside for your program to use as dynamic memory allocation). Let us say that when this object is created it takes up memory space 1000 (up to 1100 because it uses 100 bytes of memory). So:
|MyObject| <-- this is a visualization of memory space
1000
The when we write MyObject obj it sets aside memory in the stack (which is used for static memory allocation) and this will hold the location of the object. So it may hold the reference to the location of the object in its own location which we will pretend is labeled 4
|______| <- empty memory location because it hasn't been assigned yet.
4
When we put the 2 instruction together and write MyObject obj = new MyObject() it puts the address of the object into the memory location of the variable so we end up with:
|MyObject| <-- location of actual object
1000
|1000| <-- location of variable which a reference to the location of the object
4
I was trying something, and I came across this interesting scenario. I just wanted to understand what is the major difference in these two snippets.
Initially, I took two sets, initialized one and assigned the reference to other. When I cleared set A, I noticed that Set B size changed to zero as well
Set<String> A = new HashSet<String>();
A.add("hello");
A.add("HEY");
A.add("hey");
Set<String > B = A;
System.out.println("initial sizes :" + A.size() + " " + B.size());
A.clear();
System.out.println("final sizes :" + A.size() + " " + B.size());
The output of this was something like this :
initial sizes :3 3
final sizes :0 0
Now, I tried to depict the same behavior for objects as follows:
Object a1 = new Object();
Object b1 = a1;
System.out.println("initial sizes :" + b1.toString() + " " + a1.toString());
a1 = null;
System.out.println("initial sizes :" + b1.toString() + " " + a1);
The output for this was something like :
initial sizes :java.lang.Object#54182d86 java.lang.Object#54182d86
initial sizes :java.lang.Object#54182d86 null
What is the exact difference here, I was expecting to get a NullPointerException when i tried to print b1.toString()
For primitives such as int, double, float etc, a copy of the value is made and that is passed by value:-
int x = 10;
public void foo(int k){
}
foo(x)
Here k will get a copy of the value stored in x so k will now have a value of 10. However, x and y are in two different memory locations. Changing the value of x will not change the value of k.
For object references a copy of the reference is made and that is passed by value (a reference is nothing more than the address of some memory). So in essence both references will now point to the same object (that is, the same memory location).
Myobject m = new Myobject();
public void bar (Myobject j){
}
bar(m)
A copy of the value of the reference m will be made and assigned to j. Both m and j will now point to the same object.
The difference here, is that a1 and b1 are not the objects themselves but references to those objects. So, if you modify the object referenced by a1 the object referenced by b1 (which is the same object) will change too. If however you tell a1 to point to another instance (or null in this case) it will no longer reference the same object so changes to that won't effect b1.
To go into a little more detail: Java is pass by value. However when you try to pass an object (rather than a primitive value) you're actually passing the value of the reference (also sometimes called the handle). That's why it can sometimes be a bit confusing when trying to determine whether Java is pass by handle or pass by reference.
Check this image. A2/A3 are REFERENCES to bojects. In first case these are references to Set (a1 on image). When one reference modifies object second reference sees the same change
On the other hand if you just set reference = null then you "erase one arrow" from the picture. The reference stops pointing to object, but the other reference still points to it.
You just noticed that java is completly pass by value, in which even references to other objects are passed as value.
If you null an object you are actually not nulling objects, which methods would be executed on the same object. You are only nulling the value of the reference to the object.
Check this example, you are just nulling the value of the reference on list 1, while you are still able to execute the methods on the original list.
ArrayList<Integer> list1 = new ArrayList<Integer>(0);
list1.add(1);
list1.add(2);
ArrayList<Integer> list2 = list1;
list1.clear();
list1 = null;
System.out.println(list2.size());
System.out.println(list1.size()); // will cause an nullpointerexception
The calls of methods on list1 does also effect list2, but nulling the object list1 wont affect the list 1
My answer is an additional element to #I.K. answer. Primitives (copy of value) and Non-Primitives (copy of reference values) - that's the key understanding.
Look at this code where I have put the comments (hosted on IDEONE):
import java.io.*;
/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
public static void main (String[] args) throws java.lang.Exception
{
String a1 = "blablabla";
String b1 = a1;
System.out.println(b1);
System.out.println(a1);
System.out.println(b1.hashCode());
System.out.println(a1.hashCode());
a1 = "wow";
System.out.println(b1.hashCode());
System.out.println(a1.hashCode());
System.out.println(b1);
System.out.println(a1);
Set<String> a = new HashSet<String>();
Set<String> b = a;
a.add("hey");
a.add("HellO");
System.out.println(b.size());
System.out.println(b.hashCode()); // e.g. 69712814
System.out.println(a.hashCode()); // same - 69712814 (It's important to know)
a.clear();
System.out.println(a.size()); // same hashcode i.e. gets affected by the change.
System.out.println(b.size());
You can see that they are effectively hashed using the same code.
String class is a bit special in Java as you probably know that they use the String pool for "Intern"ing the values. If you run the code above you can see that as soon as you do a1 = "wow"; it will create a new value "wow" in the pool and therefore, the hashCode() changes.
Assume we have a class:
class Account {
String name;
int ID;
}
Then
a1 = new Account();
a2 = new Account();
Will create 2 variables that point to 2 memory locations storing 2 instances of class Account.
My question is how Java can know how big these instances are to allocate their memory (Because with String type, we can assign any string to that. For example, a1.name = "Solomon I", a2.name = "Alan". This will lead to different size of each instance)
Memory location is a 'continuous' string of bytes. Therefore, if I have a1 = new Account() then a2 = new Account() => a1's memory location is fixed ('used memory | a1 | a2') so what will happen if I make a1.name a very long string? Will a1's memory location extend to a2's memory location?
Thank you for reading this, please let me know if I have any misconception.
name is a String reference (not the actual string). It will "point" to a String object when you assign it.
Therefore, as part of your object, java only needs to "allocate" space for the String reference, plus an int, which are constant in size.
The object just holds the reference to other object(member variables). So its size will always be fixed. So changing the contents of the referred object will not effect the size of the object referring to it.
So you need not worry about the String size and your 'Account' class object will not be effected even if you change the String, as only String reference is stored by the 'Account' class object.
Hope this has helped you.
I have the following code
class sample
{
public void update(List l)
{
l = null;
}
public static void main (String[] args)
{
List m = new ArrayList();
m.add("suresh");
m.add("sankar");
new sample().update(m);
System.out.println(m);
}
}
The answer will be {["suresh,"sankar"]}. The m is a pointer to the arraylist object, it contains an memory address value (for ex consider 0xf34 ). when we pass m to update method ,the local variable l will be set to 0xf34 that points to arraylist object in memory .when we set null to this variable l , the memory address replaces the arraylist with null ,hence the variable m should also refer null.am i right.please help.
No, the compiler has it right. :)
The parameter l contains a reference to the ArrayList object assigned to m. l gets updated to null, and indeed any later use of lwithin the update() method would see it as null. But l is a separate variable that has scope only within that method -- it's not linked to m in any way (other than the fact that they originally contained references to the same object).
The key to understanding this is to remember that all objects are accessed indirectly via a reference. When an object is passed as an argument the method, the "value" actually being passed is a reference, and not the object itself.
When you null out the l parameter in the update method, you are setting that specific reference to null - the original reference m remains unchanged, and the object referred to by both references is also unchanged.
If you know C/C++, then this can be paraphrased as:
void update(List* l)
{
l = NULL; // set the pointer to null - the object (*list) is unmodified
}
void main()
{
List* m = ...;
update(m);
printf(m->values());
}
The pointer m is copied by value. The object pointed to (the list *m) is not altered in any way. The value of the pointer is copied from m to l. When l is set to NULL, that is a local change that only affects the value of the l pointer.
Here's an example where pass by reference involves a non-local change,
class NonLocalChange
{
public void change(int[] i) {
i[0] = 2;
i = null;
}
public static void main(String[] s) {
int[] m = new int[1];
m[1] = 3;
change(m);
System.out.println(i[0]);
}
}
The result printed is 2. This is because the change method changes the object referenced by l, and not just the reference itself.
Note that this doesn't throw a NullPointerException, even though l is assigned to null. As before, it's a reference, and so it's a local assignment to the value of that reference.
Your update method just sets the local l reference to null without changing anything about the passed-in object.
just imagine that you have a address (X for example) in your heap.
if you set m to refer to X as well as l to refer to X. we have two variable referring to same address, if you change one of them to null, the other one will remain as old value.
Lets say, new ArrayList() returns address 2000 in which the new ArrayList object is stored.
List m = new ArrayList():
lets say, m # 9999 = 2000, here let the number following '#' indicates the address at which 'm' is stored and the number following the '=' represents the value of 'm' (that would be an address as well since it is a reference type). So now 'm' at address 9999 holds 2000 which is the address of the newly created ArrayList object.
update(m):
Since Java is call-by-value always, it calls the update() method copying the value stored in m which is 2000. So now the parameter 'l' in update() definition holds the value 2000 (which is also the address of the previouly created ArrayList object). So we can say,
l # 8888 = 2000 ('l' at address 8888 holds the value 2000)
l = null:
So now, l # 8888 = null
System.out.println(m):
What is the value of m now? it is still 2000. The update() method didn't change the value of m. The method has just changed its local variable 'l' value. so 'm' still refers the previously created ArrayList object and is being printed.
No, you're not right. Java, as many other high level languages, employs call by sharing.
You could think of it like that: the reference to the actual argument is passed by value. Which means that within the called function, the reference initially points to the same value, thus any changes to the value itself are visible outside the scope of the called function, but any changes to the local reference are not. It is as if a copy of the address was passed.
Some additional reading for those interested.
objects and primitives
java+pass+by+value search