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
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
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.
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 an array of type string which I want to re-use inside a method.
I need to pass it to a function which returns a subset of the elements of array passed. To capture the returned array from function, should I declare a new string array or can I safely re-use same array that was holding the unfiltered array to hold filtered array ??
Also clarify that, when I pass a variable like array to a function does it creates a new space for the array in the heap each time I pass it as parameter to further functions or just it uses the same space in the heap & just passes the reference? I guess in case of array it passes just the reference but in case of simple variables it allocates new space on stack, right?
EDIT: Thanks you all for the great answers and explanations!!
Please also clarify whether that if I am reusing the array, would I be required to set the null at the position when I end the subset of array ? (Actually the subset of my array is not directly calculated from the array but some other calulations, so I would be required to manually set the null otherwise the older elements of the list would be visible, right??)
when I pass a variable like array to a function does it creates a new space for the array in the heap each time I pass it as parameter to further functions or just it uses the same space in the heap & just passes the reference
Java is entirely pass by value, but with arrays and object types, the value being passed is a reference (pointer) to the actual object, which is not duplicated. So when you pass an array into a function, only the reference to the array is copied; the array itself isn't. That's why if a function modifies the contents of an array, the code calling the function sees the modifications.
Example:
// A method that changes the first entry in an array
void changeArray(String[] theArray) {
theArray[0] = "Updated";
}
// A method that uses the above
void someMethod() {
String[] foo = new String[3];
foo[0] = "Original 0";
foo[1] = "Original 1";
foo[2] = "Original 2";
this.changeArray(foo); // `changeArray` receives a *copy* of the reference
// held in `foo`; both references point to the same
// array
System.out.println(foo[0]); // Prints "Updated"
}
Passing a reference into a method is exactly like assigning it to another reference variable:
String[] foo = new String[3]; // Create a new aaray, assign reference to `foo` variable
String[] bar = foo; // Copy the reference into `bar` as well
foo[0] = "Hi there"; // Use the reference in `foo` to assign to the first element
System.out.println(bar[0]); // Use the reference in `bar`, prints "Hi there"
Essentially, references are values (numbers) that tell the JVM where the data for an object is. So even when we copy a reference (because Java is entirely pass-by-value, the value of the reference is passed to the function), it still points to the same memory as the original.
Since the reference is passed into the function by value, the function can't change the calling code's reference:
// A method that assigns a new reference; the calling code sees no change
void changeReference(String[] theArray) {
theArray = new String[1]; // Now we're not using the caller's object anymore
theArray[0] = "I was changed";
}
// A method that uses the above
void someMethod() {
String[] foo = new String[3];
foo[0] = "Original 0";
foo[1] = "Original 1";
foo[2] = "Original 2";
this.changeReference(foo);
System.out.println(foo[0]); // Prints "Original 0", our reference wasn't changed
}
You'll note that this is exactly how primitives are treated:
void whatever(int a) {
a = 5;
}
void someMethod() {
int foo;
foo = 3;
whatever(foo);
System.out.println(foo); // Prints 3, of course, not 5
}
...and in fact, object references (as opposed to objects) are primitives.
...should I declare a new string array or can I safely re-use same array that was holding the unfiltered array to hold filtered array
You can safely re-use it if that's appropriate within the scope of your function. It may be more appropriate to declare a new variable (e.g., filteredThingy), but that's a matter of style and will depend on the situation.
You probably can by it is very bad style and is very error prone and is very unsafe for future modifications.
Here is the example.
public void foo(String[] args) {
// do something with array.
args = new String[] {"a", "b"}; // reuse of the array.
// more code.
for (Sring s : args) {
// what will this loop get"?
// The answer is: a, b
// but will you remember this fact in a month if "more code" above is 50 lines long?
}
}
Passing objects is essentially passing references. E.g., your array will only exist in one place in memory when passing it between functions. Here's a runnable example illustrating that.
If you like you can reuse the same array in your routine. Note that changes made to that array will be visible to all objects sharing the same reference to that array. If that's not desirable then you must declare a new array.
String parentArray = {"S","B","R"};
// no need to use new operator to hold subSetArray.
String subSetArray = returnSubSet(parent);
public String[] returnSubSet(String[] _parent)
{
return Arrays.copyOfRange(_parent,1,2);
}
, when I pass a variable like array to
a function does it creates a new space
for the array in the heap each time I
pass it as parameter to further
functions or just it uses the same
space in the heap & just passes the
reference?
Answer
1. Object in Java are Pass by reference by value
2. when you pass an array to a function , a _parent variable will be created in stack and it points to the same Array Object in heap.
UPDATES: Primitive example
int global_scope_variable=10;
setValue(global_scope_variable);
public void setValue(int val)
{
// val is visible only within this method.
System.out.println(val); // output is 10
val=20;
System.out.println(val); // output is 20
System.out.println(global_scope_variable); // output is 10
}
I wish to confirm which scenario will cause a Garbage Collection on the object myObj:
Scenario 1
ArrayList arList = new ArrayList();
while(someCondition)
{
myObj = new MyObect(); // a custom object
arList.add(myObj);
}
Scenario 2
ArrayList arList = new ArrayList();
while(someCondition)
{
myObj = new MyObect(); // a custom object
arList.add(myObj);
myObj=null;
}
Does explicitly setting an object as null improves garbage collection behavior or will be it same when I reset the object using the new constructor ?
You don't specify the scope of myObj and its important. If its a local variable it almost certainly doesn't matter. If its an instance variable then that could be a long-lived and unnecessary reference in which case setting to null will be useful.
Update: given the updated information that myObj is local to the method, it will be of zero value to set it to null at the end of each iteration of the loop. Consider this example:
public void process(String text) {
String[] lines = text.split("\n");
List<MyObject> list = new ArrayList<MyObject>();
Object myObj;
for (String line : lines) {
myObj = new MyObject(line);
list.add(myObj);
// 1. set myObj = null here
}
list = null; // 2
// 3. do some other stuff
}
public class MyObject {
private final String line;
public MyObject(String line) {
this.line = line;
}
}
Now in this example, let's say that at step 3, it took a long time. Say 10 minutes. During that 10 minutes myObj is pointing to the last line processed. Doesn't sound like a problem? Well it could be. The way substrings work in Java is that they reference the original string. So if you do:
String s = ... // 100 megabytes
String s2 = s.substring(100, 101);
you're actually keeping the entire 100MB in memory because s2 references s.
So in the function I have above, myObj references a line which references the entire file. Changing step 1 to myObj = null; would actually help that because this reference is preventing the object being garbage collected.
Note: step 2 is important here because if you didn't nullify the list all the references would exist anyway.
You just need to think about how references work. An object won't be garbage collected while a reference to it exists. This means clearing long-lived references and keeping variables scoped as tightly as possible. The correct solution for the above is:
for (String line : lines) {
Object myObj = new MyObject(line);
...
}
and then myObj is scoped inside the loop so as soon as the loop ends or another iteration begins it has gone out of scope, which is much better.
Setting it to null will have no effect, since the object is still reachable via arList.
That is, your MyObect instances will live at least as long as arList.
EDIT: Based on your comment, it does sound like myObj is longer-lived. In that case, set it to null after the end of your loop.
I think that this is the root of your misunderstanding.
hmm.. but I don't wish to keep 2 copies of myObj , one in arList and one in the original variable. How can I flush myObj once I add it to arLsit ?
You do NOT "keep two copies of myObj". In your examples, there is only ever one "copy" of each MyObject instance created by the loop. The sequence is:
You create a MyObject instance, assigning its reference to myObj.
You add the reference to the instance to the ArrayList that arList refers to.
You assign null to the reference in myObj.
Note that adding the reference to the list does NOT create a copy of the MyObject instance. It simply means that that you have the reference in two places instead of
one. And when you assign the null you once again have the reference in just one place.
The other thing to note is that assigning null to something will never CAUSE the garbage collector to run. All it does is to (explicitly) remove a potential copy of a reference from consideration the next time the garbage collector is run.
Finally, if we assume that the scoping is as follows, then the line C will have no discernible effect ... unless either line A or line B triggers a garbage collection.
{
MyObject myObj;
ArrayList arList = new ArrayList();
while (someCondition) { // A
myObj = new MyObect(); // B
arList.add(myObj);
myObj = null; // C
}
}
Because it is in a while, myObj is always overwritten (the reference). So in Scenario 1 only one object (the last added in arList) will not be null.
It would be better if you declare it in the while statement:
while(someCondition)
{
MyObect myObj = new MyObect(); // a custom object
arList.add(myObj);
}