I'm currently studying towards my Java associate certificate and I have the official textbook for study. In the text book is the following example code.
public class Island {
Island i;
public static void main(String[] args) {
Island i2 = new Island();
Island i3 = new Island();
Island i4 = new Island();
i2.i = i3;
i3.i = i4;
i4.i = i2;
I understand here that I have initialised 3 Island objects with reference variables i2, i3 and i4 which each point to their own object. i2 is then redirected to point towards i3 and i3 towards i4 etc. What I don't understand is the need for the "i2.i" dot operator, what exactly is it doing here ? is i2 = i3 not as equally valid ?
If anyone has any good resources on where I can read quite in depth into all of the applications of the dot operator in java that would also be helpful, thanks.
You're using an instance variable of the same type as the class itself (see line Island i). This means, that the class Island holds an attribute i of the same type Island. Every island has therefore a link to another island. Your assignment of i2.i = i3; defines the instance variable of i2 to be i3. In other words: i2 has a link to i3. You can get i3 if you have i2.
If you used the assignment i2 = i3, the value of i2 would be overridden by the reference of i3. This means that i2 is not used anymore and the object behind i3 would also be the same object behind i2 (same object, 2 different variable names).
It's important to be careful with the details here.
You have indeed created three objects, but the objects are distinct from the variables.
Let's simplfy: Consider Island x = new Island();, and Island y; You have two variables, x and y, but only one object. The object doesn't have a name, but it is bound to the variable x, so when you say x, you get that object. And when you say y, you get nothing (y is null).
The dot accesses the object denoted by by the expression that precedes it. So x.i accesses the i member variable of the object that is bound to x, and similarly, y.i is attempting to access a member-variable of no object at all, which causes an exception to be thrown.
So now it is clear that you can say x.i = x; to set the member variable Island.i of the object bound to x to the value that happens to be the same object. Or you could set it to something else, like x.i = new Island(); or x.i = y;.
The dot doesn't have to be preceded by a variable, any expression will do. For example, you could say (new Island()).i = x; to create a new object (which again doesn't have a name; objects never have names) and bind the i member of that object to the object bound to x. Since this object is never bound to any variable, it is immediately eligible for collection.
The point of your code example is that all objects are bound to variables that exist beyond the scope of the i1, i2 and i3 (namely to the member variables of the three objects), and thus they form a reference cycle. An interesting question on the topic of garbage collection is whether the three objects are eligible for collection.
Related
Can someone help me understand this piece of code?
int[] A = new int[2];
Object x = A; // All references are Objects
A[0] = 0; // Static type of A is array...
x[1] = 1; // But static type of x is not an array: ERROR
According to the Oracle Java SE site:
In the Java programming language, arrays are objects (§4.3.1), are dynamically created, and may be assigned to variables of type Object (§4.3.2). All methods of class Object may be invoked on an array.
I understand that arrays can be assigned to Object type variables, however, my confusion perhaps lies in the concept of reference. When we say Object x = A, we are saying that x has a reference to the array A. However, my confusion lies in x[1] = 1. Why is it an error such that the x is considered an Object even though it is referencing an array? Do I think of [] as a kind of method that is only accessible in the "array" class?
While I would not unnecessarily throw away type information, you can use Array#setInt(Object, int, int) to access an int[] through an Object. That is,
Array.setInt(x, 1, 1); // x[1] = 1;
would work.
arrays are objects - This means that array for any type (reference or primitive) are the child class for Object class.
By using parent reference instance of child class can be accessed. Therefore the statement Object x = A; is syntactically correct.
By using Child reference child related methods/implementations can be accessed. Therefore the statement A[0] = 0; is syntactically correct.
By using parent reference child related methods/implementations cannot be accessed. Hence the statement x[1] = 1; is syntactically incorrect.
When we assign objects of the same type to one another, the new object stores the address of the object that's being assigned to it. For instance, if I have my own class named CLA then the following code will produce 11 as the output:
public class CLA{
int val;
public static void main(String...args){
CLA ob1 = new CLA();
ob1.val = 10;
CLA ob2 = ob1;
ob1.val++;
System.out.printf("%d", ob2.val);
}
}
because ob2 would refer to ob1 and consequently show ob1's val.
However, the same doesn't happen for Wrapper classes. When I assign an Integer object to another, the operation simply behaves like we're dealing with values and not objects. As opposed to the above code, the output of the below code is 10:
Integer x = 10; //auto-boxed as new Integer(10)
Integer y = x;
x++;
System.out.printf("%d", y.intValue());
Why does this happen?
Do wrapper class objects get unboxed while being assigned to another object, so as to pass the value instead of the address??
When you do x++, it is the same as x = x + 1 so it is actually a new instance of Integer assigned to x but y still points to the old instance which value is 10.
Keep in mind that wrapper instances are immutable.
For an Integer x, the expression x++ can be understood like x = new Integer(x.intValue() + 1);. Its not exactly the same, but helps for understanding.
So, it doesn't modify the Integer object with the value 10 that x pointed to before the x++, it creates a new Integer object with the value 11 independent of the original 10 Integer, and assigns this 11 Integer to x.
But there's nothing in the x++ expression that would make y point to a different Integer instance. So y still points to the original 10.
That's the difference to the CLA example where you don't introduce a new instance with ob1.val++, but modify the single instance. If the Integer class had a public field value, then x.value++ would show the behaviour you expected - but the value field is (for good reason) private - not accessible to the outside.
Given:
interface Animal { void makeNoise(); }
class Horse implements Animal {
Long weight = 1200L;
public void makeNoise() { System.out.println("whinny"); }
}
public class Icelandic extends Horse {
public void makeNoise() { System.out.println("vinny"); }
public static void main(String[] args) {
Icelandic i1 = new Icelandic();
Icelandic i2 = new Icelandic();
Icelandic i3 = new Icelandic();
i3 = i1; i1 = i2; i2 = null; i3 = i1; //<-- line 14
}
}
When line 14 is reached, how many objects are eligible for the garbage collector?
A. 0
B. 1
C. 2
D. 3
E. 4
F. 6
answer is E. why?
The question, and especially insisting on that particular “correct” answer of 4, doesn’t make much sense, as it draws several assumptions about how a JVM works which are naïve in the best case or simply wrong.
The first thing it does wrong is to assume to know, how many objects are ever created.
The main method creates three instances of Icelandic having an inherited field weight initialized with the value of 1200L. The value 1200L is converted to a Long instance via auto-boxing using the method Long.valueOf which is allowed, but not required to cache frequently requested values. So we can’t predict the number of Long instances created here, it might be one or three, but even two is possible if the caching did not work for all invocations, for whatever reasons.
Then the code is playing around with the local variables holding the three instances and is supposed to hold only one of these instances “after” line 14 but there is no associated code after that line for which this state could have a relevance. The only thing which happens after that line is returning from the main method so after line 14 all local variables are out-of-scope.
Whether there is a point of execution which can be associated with “after line 14” but still being in method main where exactly one instance of Icelandic is in scope depends on whether line debug information have been included in the class file and how the compiler did map byte code instructions to these line numbers.
If the return byte code instruction inserted by the compiler is not associated with line 15, all instances created in the main method might be garbage collected after line 14 but keep in mind that the Long instance might still be referenced from a global cache and hence not be eligible to garbage collection. We simply can’t predict.
There is another aspect of JVM execution: optimization. JVMs are allowed to perform “escape analysis” and elide object creations if they don’t have any impact on the programs semantics. In your program, all objects are unused from a JVMs point of view. So another legal execution scenario is that this main method never creates any object and then there are no object eligible for garbage collection at all.
So each object holds a Long object, so when each Icelandic object is marked for garbage collection, so is the Long value it pointed to. Therefore after:
Icelandic i1 = new Icelandic();
Icelandic i2 = new Icelandic();
Icelandic i3 = new Icelandic();
i3 = i1; i1 = i2; i2 = null; i3 = i1;
the original i3 object is lost, and the original i1 value is lost. i1 and i3 both point to the original i2 keeping it alive (and i2 points to null, keeping nothing alive). This leaves 2 objects getting marked for garbage collection (the original i1 and original i3) as well as the Long value they pointed to, giving you 4 items marked for garbage collection.
I would go with Icelandic 2 objects eligible (object 1 and 3).
Since each Icelandic holds a Long object, it gives us 4.
After Line14, Only original i2 Object is alive, no referemce is point to original i1 and i3, so they are eligible for the garbage collector, and this object has a base type, a long field, lose it to, so 2*2 = 4;
The important stuff is here
Icelandic i1 = new Icelandic();
Icelandic i2 = new Icelandic();
Icelandic i3 = new Icelandic();
i3 = i1; i1 = i2; i2 = null; i3 = i1;
So i3 loses its reference right away so the original object is ready for collection.
i1 and i3 reference i2 so i1s original object is ready for collection.
Then i2 loses its reference so all 3 are null and garbage collected.
I'd personally go with 3. That's a pretty thought provoking question.
public class NotActuallyImmutable {
private final int x;
public NotActuallyImmutable(int x) {
this.x = x;// line 1
}
public int getX() {
return x;
}
}
public class Mutable extends NotActuallyImmutable {
private int x = 123;
public Mutable(int x) {
super(x);
}
public int getX() {
return x++;
}
}
now in my main class
NotActuallyImmutable n = new Mutable(42); // line2
int x = n.getX();
System.out.println("x is"+x);
I am expecting the output as 42 but it return the output as 123. I am expecting 42 because at line 2 I am making object of class Mutable and then at line 1 I am setting value as 42. so when i do n.getX() I should get the this latest value not the default 123. I know Ii am missing something but not able to figure out the logic behind it?
The problem is that the field x in Mutable and the field x in class NotActuallyImmutable are not the same. The x that is returned by getX() is the one in Mutable (because the getX() that is invoked is Mutable.getX(), not NotActuallyImmutable.getX()).
Note that if you removed the instance field from Mutable, then you would have a compiler error because NotActuallyImmutable.x is private to NotActuallyImmutable and not accessible to any code in Mutable.
If you made NotActuallyImmutable.x a protected field, then Mutable.x would shadow it and you would still have the same behavior. If you removed Mutable.x in this case, you would still have a compiler error because you were trying to increment a final field.
If you remove Mutable.getX(), then the x that would be returned by getX() would be NotActuallyImmutable.x, despite there being another field of the same name in Mutable.
The private int x in Mutable and the private int x in NotActuallyImmutable are completely different fields that just have the same name.
This isn't a problem for the compiler because you can't access a private field from another class. So as far as the compiler is concerned, when you define Mutable, the x in NotActuallyImmutable is invisible and might as well not exist.
It is of course confusing for the programmer. If you rename one of the fields to y (and the getter method to getY) the behaviour seems much more intuitive.
NotActuallyImmutable n = new Mutable(42); // line2
This means you have an object of type NotActuallyImmutable but the instance of created object is Mutable.
so in this code your dealing with Mutable object which will return 123. as the number you passed is saved in NotActuallyImmutable not in Mutable,
n has two different x values which are visible in different contexts, the parent class's private member variable and the child class's private member variable.
NotActuallyImmutable n = new Mutable(42); // line2
Creates a new Mutable. Executes parent(x) which sets the parent class's x to 42.
int x = n.getX();
n is a Mutable instance so this calls Mutable's getX() which returns Mutable's value for x (123) rather than the parent's.
I agree with Nice explanations given in above answers. But to to just brief the final understanding. As i am doing new Mutable(42).getX(), jvm first will look in Mutable object to get the value of X not inside NotActuallyImmutable. If i remove getX() method from Mutable , i get the expected(as per my expectation) value i.e 42.
This example gets messy becoz variable name i.e X is same in parent and child class but good for understanding concept
This is the code I have, please look at it before you read the question
package ict201jansem2012;
public class Qn3b {
public static void main(String[] args) {
int a = 1;
int b[] = {4,5};
String s = "Good luck!";
method1(b[1]);
System.out.println("(1) b[0] = "+b[0]+"; b[1] = "+b[1]);
method2(b);
System.out.println("(2) b[0] = "+b[0]+"; b[1] = "+b[1]);
method3(a);
System.out.println("(3) a = " + a );
method4(s);
System.out.println("(4) s = " + s );
}
public static void method1(int b) {
b = 7;
}
public static void method2(int[] b) {
b[0] = 3;
}
public static void method3(int a) {
a = 3;
}
public static void method4(String s) {
s = "UniSIM";
}
}
Output:
(1) b[0] = 4; b[1] = 5
(2) b[0] = 3; b[1] = 5
(3) a = 1
(4) s = Good luck!
So my question is ,
This is intresting for me to know as learning programmer. The int b array 0 index value has changed, but not the other variables like the String s and int a. Before i ran this program I roughly thought in my mind that the variable will change their values as the methods are called ,this is because the method is being called and the main method vairable such as a,s and b array are passed and then they are being modified.
So in a nutshell why is that the b array 0 index is changed while the other variables are not changed?
Because you said you were a beginner programmer, I'll do a little writeup to explain (or try to explain) exactly what is happening.
It is because you are passing an argument to your method1 - method4 methods
These arguments, themselves, are references to other objects.
When you use the assignment operator, an equals sign, you overwrite that reference for the value in the current scope - where variables can be 'seen'.
In your code:
In the case of method1 you are creating a new reference, the variable can only be seen within that scope. That is, when you then go b = << expr >> you are assigning the variable b within method1's scope the value, not b in the main scope. The same is true of your method3 and method4 methods, you are assigning a new value to the respective variables within that scope, as you are creating new references rather than altering the original objects.
But, method2's code behaves differently, this is because you are mutating the object inside that code. You are altering the object directly - rather than creating a new reference inside that scope.
Consider the code below
int[] array = new int[] {1, 2};
public void test()
{
method1(array);
System.out.println(array[0] + ", " + array[1]);
method2(array);
System.out.println(array[0] + ", " + array[1]);
}
// because we are working with objects, the identifier, can be different to the arrays identifier
// in this case, I've chosen to use 'a' instead of 'array' to show this
public void method1(int[] a)
{
// this mutates the array object
a[0] = 2;
}
public void method2(int[] array)
{
// this overwrites the method2.array but not the global array
array = new int[] { 1, 2, 3 };
}
We create a new array, with identifer 'array' in the global scope. (In Java, this would be the classes own scope)
In method1, we take an argument, which is the same object being passed as the global array object, so when we mutate it, both objects will change. So, the first print statement will be
"2, 2"
Where array[0] has been altered
N.B. Because we dealing with objects, the 'name' of the variable doesn't matter - it will still be a reference to the same object
However, in method2, we take an argument, like in method1, but this time we use the assignment operator to assign that variable to a new value in the scope that it's currently in - so the global array isn't altered, so we still print out
"2, 2"
For a beginner programmer, I would personally write a few test programs where you get to fully understand how variables and scopes work.
But just know, everytime you create a new block of code, a new scope is created, local variables to that scope can only be seen in that scope and ones below it.
For instance:
public void test()
{
int a = 5;
method1(a);
System.out.println(a); // prints 5
}
public void method1(int a)
{
// a is only viewable in the method1 scope
// and any scopes below it, that is, any scopes created within method1
// and since we use an assignment operator, we assign method1.a a value, not global 'a' a value
a = 123;
if (true)
{
// this is a new scope, variables created in this block cannot be seen outside it
// but can see variables from above it
System.out.println(a); // prints 123
}
}
Here, we create a new scope inside method1 inside the if statement, which can see a above it. However, because method1 and test's scopes are both independent, when we use the assignment operator, we assign the value of a to the local scope. So a is different in both test and method1
I hope you understand better now.
I'm not very good at conveying things, but if it even helped a little bit in understanding scopes I did well, plus, it was fun.
Java is pass-by-value, but most values (everything that's not a primitive, in this case int[] and String) are references, which means they act like pass-by-reference.
Here's a nice writeup: http://javadude.com/articles/passbyvalue.htm
arrays are special type of objects and memory will be allocated on HEAP. When you pass array as parameter to method it will be pass as reference-value (copy of the reference).
This means initial b and this new reference points to same object. Unless new reference points to another object, changes on this reference will reflect on same object. That is why you are seeing value reflected on original array.
All of the values were passed TO the inner methods, but the inner methods returned nothing. However, method2 modified the internal value of the array that was passed to it, so that inner value appeared modified on return.
Note that method2 is the only one where you did not assign to the variable (parameter) itself, but rather assigned to an element of the object whose reference was passed in.
There is a critical difference between modifying the reference (pointer) to an object, and modifying the object itself.