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.
Related
This question already has an answer here:
Java method doesn't change parameter objects [duplicate]
(1 answer)
Closed 8 years ago.
Ok. I'm completely aware that all values in Java are passed by value. But this example does not behave as I expected:
public class Test {
private static void changeInteger(Integer x) {
x = 5;
}
public static void main(String[] args) {
Integer x = 0;
changeInteger(x);
System.out.println(x);
}
}
Since I'm passing wrapper class to the changeInteger Method, I'm passing its address, so, after executing function that should affect my x variable and set it to 5. But eclipse says that output is still 0. What did I understand wrong?
Consider this example:
class Wrapper {
int n;
public Wrapper(int k) { n = k; }
public String toString() { return ""+n;}
public static Wrapper valueOf(int k) { return new Wrapper(k); }
}
Now let us replace Integer in your code with the Wrapper class above:
private static void changeInteger(Wrapper x) {
x = Wapper.valueOf(5);
}
public static void main(String[] args) {
Wrapper x = Wrapper.valueOf(0);
changeInteger(x);
System.out.println(x);
}
Since you mentioned that you know about passing by value, I hope that it is clear why this code does what it does.
Now let's go back to your code. Under the hood, it is exactly the same code. The only difference is that you do not call Wrapper.valueOf: the compiler does it for you through autoboxing. Once you realize that this is what is going on, the issue should be clear to you.
ByteCode of changeInteger() to show that Integer.valueOf() is called :
private static void changeInteger(java.lang.Integer);
Code:
Stack=1, Locals=1, Args_size=1
0: iconst_5
1: invokestatic #16; //Method java/lang/Integer.valueOf:(I)Ljava/lang/In
teger;
.... // some other code
With x = 5; you assign a new value to x inside changeInteger(). You are not changing the value of the current Integer object.
The value of x outside the method is not affected.
You get puzzled by the autoboxing function of java. You cannot assign a primitive value to an objec. When you call x=5 it creates a new Integer object with 5 value and assignes its reference to x. But this affects only the parameter in the scope of changeIngeger, the original object with 0 and the reference of x in main scope are untouched.
Your problem is the fact that Java is pass-by-value not by-reference so the x in the method is not the same x of main. The fact that Integer is an immutable class, here doesn't change anything.
All Java parameters are passed by value. For all non-primitive types, the value contains a reference to the object that's passed.
For your piece of code, the Integer object is stored at location A in memory. Another location B in memory represents the main's variable x and stores a value A.
For the changeInteger call, a new location C is created and a value in B (which is A) gets copied to it. This is the local x of the changeInteger function.
With the assigning, you create a new variable stored at D and its location is assigned to C. Then you return from the method.
You can see that A and B isn't modified anywhere and thus the value remains unchanged.
Keeping as simple as it is, All Java Wrapper classes are Immutable. So you cannot seeing the change.
If you want to see the change, just return from method (not main) and assign it back.
I have been playing around with Java and cannot quite understand how java methods work with objects which are passed to them.
For example, In the code below i create some "Container" object instances which contains another object and a primitive. When i pass this "Container" object to methods, I can change the object that is held inside the container instance either directly modfying its value or using the new operator to construct a new object and replace its original. These changes are permanent as their values are that of the new objects when examined outside the method.
What is confusing me greatly is that although i can change a Containers inner object via methods, I cannot actully change the container itself. By this i mean that if i pass a container to a method and try to alter it via swapping or assignment from the new operator.
Below is the code i use to test the modification of an object instances attributes and then modification of the actual instance itself.
class InsideRef{
char myChar;
InsideRef(char newVal){
myChar = newVal;
}
}
class Container{
InsideRef myInRef = null;
int myPrimitive = 0;
Container(char innerChar, int innerPrim){
myInRef = new InsideRef(innerChar);
this.myPrimitive = innerPrim;
}
public void myDetails(){
System.out.format("Container.%s => myPrimitive -> %d || myInRef => %s -> %c.%n",
this.hashCode(),this.myPrimitive,this.myInRef.hashCode(),this.myInRef.myChar);
}
}
class AttribRefModder{
public static void ModObjRefVal(Container toEdit){
toEdit.myInRef.myChar = 'Z';
}
public static void ModNewObjReference(Container toEdit){
toEdit.myInRef = new InsideRef('Y');
}
}
class RefSwapper{
public static void RefSwap(Container A, Container B){
System.out.println("Swapping....");
System.out.print("OBJECT A -> ");
A.myDetails();
System.out.print("OBJECT B -> ");
B.myDetails();
Container temp = A;
A = B;
B = temp;
System.out.print("SWAPPED A -> ");
A.myDetails();
System.out.print("SWAPPED B -> ");
B.myDetails();
System.out.println("Exiting....");
}
public static void RefNew(Container A){
System.out.println("Assigning Reference New Object....");
A = new Container('V',999);
System.out.print("NEW C REF -> ");
A.myDetails();
System.out.println("Exiting....");
}
}
public class ReferenceModding{
public static void main(String[] args){
System.out.println("-----------MODDING INNER REFS----------");
Container C1 = new Container('A', 111);
System.out.print("ORIGINAL A -> ");
C1.myDetails();
AttribRefModder.ModObjRefVal(C1);
System.out.print("MODDED A.Ref -> ");
C1.myDetails();
AttribRefModder.ModNewObjReference(C1);
System.out.print("NEW A.Ref -> ");
C1.myDetails();
System.out.println("----------SWAPPING REFERENCES----------");
Container C2 = new Container('B',222);
RefSwapper.RefSwap(C1, C2);
System.out.print("OBJECT A -> ");
C1.myDetails();
System.out.print("OBJECT B -> ");
C2.myDetails();
System.out.println("----------ASSIGN NEW OBJECTS----------");
Container C3 = new Container('C',333);
System.out.print("OBJECT C -> ");
C3.myDetails();
RefSwapper.RefNew(C3);
System.out.print("OBJECT C -> ");
C3.myDetails();
}
}
I apologise if this is too much code i have posted. It's just i've been playing with Java all day and this object parameter business has really confused me. I cant work out why Java methods allows me to edit and assign a new object to the InsideRef refrences that are held inside a container class but do not allow me to perform the same operations on the actual container classes.
Thanks for any help you may be able to provide.
You have stated the characteristic of Java correctly.
Under the covers, you pass a reference -- a memory address is a good model -- of an object. So you can change anything referred to by that reference.
But you cannot change what the caller thinks of as "that object" -- the caller has a memory address containing the address passed to you, and you cannot change that. So regardless of what you do, you cannot change which object is referred to, only what is 'inside' that object.
I think the confusion comes from this part in RefSwap:
Container temp = A;
A = B;
B = temp;
In Java, this only affects the variables A and B within that method. It won't change what objects A and B point to when the method returns to the location where it was called from. So in main the objects C1 and C2 still refer to the same Container objects, these are not swapped around.
Parameter passing in java is always by value. This meaning that having a parameter in the left side of an assignment has no effect at all outside the method call; it just changes the value of the reference (pointer) you copied on the method call stack, so you have lost that value for the rest of the method.
For example, PL/SQL has a true parameter passing by reference, if you declare there
-- True pass-by-reference
procedure my_procedure(my_integer out integer) is
begin
my_integer := 6;
end;
You will see after the procedure call that the integer you passed there has changed its value. However, java does not support this parameter passing. Think of something like this in C:
// Just stepping on the values on the method call stack
void my_function(int* my_integer) {
my_integer = 0;
}
Does this change the value of the integer referenced by the pointer? No, it just crunches the pointer value. This is what you do with your
A = new Container('V',999);
I hope this has been of any help to you :)
When i pass this "Container" object to methods
The first thing you need to understand is it's not possible to "pass" an "object". The only types in Java are primitive types and reference types. That means every value is either a primitive or a reference. A "reference" is a pointer to an object. The type Container is a reference type -- it is the type of a pointer to an object, specifically, a pointer to instances of the class Container.
I have been through this question on legality of forward references but not clear as to what is meant by forward references in Java language . Can someone please explain with the help of an example ?
This is specifically a compilation error. And its all about ordering of class variable declarations. Let's use some code for illustrative purposes:
public class ForwardReference {
public ForwardReference() {
super();
}
public ForwardReference echoReference() {
return this;
}
public void testLegalForwardReference() {
// Illustration: Legal
this.x = 5;
}
private int x = 0;
// Illustration: Illegal
private ForwardReference b = a.reference();
private ForwardReference a = new ForwardReference();
}
As you can see, Java allows you to reference a class variable in a class method, even if the declaration of the variable comes after the method. This is an example of a (legal) forward reference, and support for this is built into the Java compiler.
What you cannot do though, is declare a class variable 'a' that depends on another class variable 'b' that has not been declared yet. Dependent class variable declarations must appear in reverse order of their dependency.
On a tangent, Most, if not all IDE's will warn you if your code contains illegal reference errors.
Illegal forward references are covered in section 8.3.2.3 of the JLS.
It's basically just the order that things are read by the compiler, if you have
int c = 3
int a = b;
int b = 5;
the compiler will read it from top to bottom, so it will se the first line, which declares a variable 'c', and assigns it to 3, and that is fine, then it will encounter the second line, which declares a variable 'a', and then tries to assign it to 'b'.
But now, the compiler has a problem: What is this 'b' thing? It has only yet learned about 'c', and just recently 'a', but it has no knowledge anything called 'b', since to the compiler, it has not yet been declared. So then, since the compiler can't handle all the confusion, it stops, and leaves you to figure what you have done to anger it.
So, the forward reference part would be a reference to something that does not yet exist. Forward in time perhaps..
In simple terms it means referencing (accessing a variable, calling a function) that is further down in the code file.
static int x=getY();
static int y=5;
static int getY() { return y; }
x's value is set to the result of getY()
getY() is called before y's value is set to 5
x's value is therefore 0 (default integer)
y's value is 5
public class AnyCode {
void print() {
System.out.println("Value of j - " + j); // legal
System.out.println("Value of i - " + i); // legal
}
// CASE - 1
int k = i; // illegal
int i;
// CASE - 2
int l = j; // legal
static int m = j; // illegal
static int j;
// CASE - 3
A aObj = bObj; // illegal
B bObj = new B();
public static void main(String[] args) {
/*
Note :- here anyCode act as a local variable and get space on stack
whereas the object it is referring to is present on heap. And you
cannot forward reference a local variable.
*/
anyCode.print(); // 'Cannot find symbol' error
AnyCode anyCode = new AnyCode();
}
}
class A {
}
class B {
}
*********Refer CASE - 1*********
Forward referencing instance variable is not allowed as compiler is not sure of the type of value we are forward referencing or it might even be possible that no such variable exist.
Consider an example :-
int a = b;
boolean b = false;
If forward referencing is allowed in above case then it might create a havoc.
int a = b; // What is b? is it a primitive variable or a value or a object reference
in the above example i have decided not to declare b and now if such assignment were allowed by java, then it will be a nightmare.
**********Refer CASE - 2*********
Static variables are loaded before instance variables and hence forward referencing static variables and assigning them to instance variable is perfectly fine
I have the following function.
func(ArrayList `<String>`[] name) { ........ }
The function fills the ArrayList[]. (I don't want to return the ArrayList[])
However, in the caller function the ArrayList[] obtained has all ArrayLists as null.
For eg.
name = new ArrayList[num];
func(name);
System.out.println(name[0]);
I get NullPointerException at line 3. Is this because of line 1, i.e. I am not parametrizing? If yes, is there another way this can be done? Because java does not allow creating a generic array of parametrized ArrayList.
That is obviously not your real code, but you're creating an array of ArrayLists, which probably isn't what you want. You can probably just do:
ArrayList<String> name = new ArrayList(num);
func(name);
System.out.println(name.get(0));
Note that when you create the ArrayList, you're only specifying the initial capacity, not the size (number of initial items). It will have an initial size of 0. Your func can just call add to add items.
Even better (no typing errors):
ArrayList<String> name = new ArrayList<String>();
I recommend not bothering with the initial capacity argument (num) - just leave it blank and it will work perfectly. But do bother with the generic type of String in the constructor, or the compiler will complain.
If you want to know how to use the ArrayList (for example, why to use the get() function), you should look at the documentation.
For arrays in Java when you create it all of the elements are either 0, false, or null depending in their type.
So:
final List<String>[] foo;
foo = new ArrayList<String>[10];
foo[0].add("hello"); // crash
that crashes because foo = new ArrayList<String>[10]; allocates enough room to hold 10 ArrayList<String> but it sets all of the values to null. So you need one additional step:
for(int i = 0; i < foo.length; i++)
{
foo[i] = new ArrayList<String>();
}
I haven't compiled the code, but pretty sure it is all correct. You would do that between step 1 and 2 of your program.
I am guessing a bit because your code isn't quite accurate (it would not generate a null pointer as written as near as I can tell).
EDIT:
You would do the new in the method and the for loop with the assignments could be done inside of the method. I prefer to allocate and initialize in the same place (less confusing) but you can split it up if you needed to.
The problem you are encountering is due to the fact that in Java, parameters to methods are passed by value. What this means, is that every parameter is effectively "copied" into the method, meaning that any assignments you make to the parameters are only visible within the method, and cannot be seen by the caller.
Going by your example, you're passing in a null reference to an array of List<String>'s. This reference is then "copied" into the func() method, and when func then assigns something to this variable, it is only the local variable that is being updated, and not the reference held by your calling code.
Here's some compilable code (based on your example) that demonstrates the problem:
public class Main {
public static void main(String[] args) {
List<String>[] array = null;
fill(array);
System.out.println("In main(): " + array[0].get(0));
}
public static void fill(List<String>[] array) {
array = (List<String>[])new List[10];
array[0] = new ArrayList<String>();
array[0].add("test");
System.out.println("In fill(): " + array[0].get(0));
}
}
The println in fill prints the correct value, because the array variable has been assigned to something within the fill method, however the println in the main method throws an NPE because only the "copy" of the array variable was changed by func, and not the "real" variable.
There are two ways to get around this: either instantiate the array within your calling code, or change the fill() method to return a reference to the array is has created.
Below is the first approach:
public class Main {
public static void main(String[] args) {
List<String>[] array = (List<String>[])new List[10];
fill(array);
System.out.println("In main(): " + array[0].get(0));
}
public static void fill(List<String>[] array) {
array[0] = new ArrayList<String>();
array[0].add("test");
System.out.println("In fill(): " + array[0].get(0));
}
}
You may be wondering why this works, because you're still assigning ArrayList's to the elements of the array, however these objects are visible outside of the calling method. The reason for this is that although the fill method is getting a "copy" of the reference to the array, the reference itself is still referencing the same array object. This means that you can modify the internal state of the array object, and any changes you make will be seen by the caller because it referencing that same object.
Below is the second approach:
public class Main {
public static void main(String[] args) {
List<String>[] array = fill();
System.out.println("In main(): " + array[0].get(0));
}
public static List<String>[] fill() {
List<String>[] array = (List<String>[])new List[10];
array[0] = new ArrayList<String>();
array[0].add("test");
System.out.println("In fill(): " + array[0].get(0));
return array;
}
}
(As an aside, you should generally try to avoid creating arrays of generic collections, a better idea would be to use a list to store the lists themselves. E.g:
List<List<String>> list = new ArrayList<List<String>>();
list.add(new ArrayList<String>());
list.get(0).add("test");
new ArrayList<?>[10] give me incompatible type. However, new ArrayList[10] works for me.
I have a problem with understanding the "pass-by-value" action of Java in the following example:
public class Numbers {
static int[] s_ccc = {7};
static int[] t_ccc = {7};
public static void calculate(int[] b, int[] c) {
System.out.println("s_ccc[0] = " + s_ccc[0]); // 7
System.out.println("t_ccc[0] = " + t_ccc[0]); // 7
b[0] = b[0] + 9;
System.out.println("\nb[0] = " + b[0]); // 16
c = b;
System.out.println("c[0] = " + c[0] + "\n"); // 16
}
public static void main(String[] args) {
calculate(s_ccc, t_ccc);
System.out.println("s_ccc[0] = " + s_ccc[0]); // 16
System.out.println("t_ccc[0] = " + t_ccc[0]); // 7
}
}
I know that because s_ccc is a reference variable, when I give it to the method calculate() and I make some changes to its elements in the method, the changes remain even after I leave the method. I think that the same should be with t_ccc. It is again
a reference variable, I give it to the method calculate(), and in the method I change the referece to t_ccc to be that of s_ccc. Now t_ccc should be a reference variable pointing to an array, which has one element of type int equals to 16. But when the method calculate() is left, it seems that t_ccc points its old object. Why is this happening? Shouldn't the change remain for it, too? It is a reference variable after all.
Regards
There's an extended discussion of how Java passes variables at an earlier question "Is Java pass by reference?". Java really passes object references by value.
In your code, the references for the arrays (which are objects) are passed into calculate(). These references are passed by value, which means that any changes to the values of b and c are visible only within the method (they are really just a copy of s_ccc and t_ccc). That's why t_ccc in main() was never affected.
To reinforce this concept, some programmers declare the method parameters as final variables:
public static void calculate(final int[] b, final int[] c)
Now, the compiler won't even allow you to change the values of b or c. Of course, the downside of this is that you can no longer manipulate them conveniently within your method.
Here's a simple way to understand it.
Java always passes copies of arguments. If the argument is a primitive type (e.g. an integer), then the called method gets a copy of the primitive value. If the argument is a reference type, then the called method gets a copy of the reference (not a copy of the thing referred to).
When your main method begins, each of s_ccc and t_ccc refers to a distinct array.
This is the situation, where parentheses indicate variables and square brackets indicate the actual array structures:
(s_ccc) ---> [7]
(t_ccc) ---> [7]
Assuming that you meant calculate(s_ccc, t_ccc), then at the beginning of the calculate method:
(s_ccc) ---> [7] <---(b)
(t_ccc) ---> [7] <---(c)
Locals b and c are copies of globals s_ccc and t_ccc respectively.
Still within calculcate, after b[0] = b[0] + 9 has completed:
(s_ccc) ---> [16] <---(b)
(t_ccc) ---> [7] <---(c)
The zero (only) position in the array referred to by b has been modified.
The assignment c = b within calculate produces this situation:
(s_ccc) ---> [16] <---(b)
^------(c)
(t_ccc) ---> [7]
The local reference variable c, now contains the same reference as b. That has no effect on the global reference variable t_ccc, which still refers to the same array as before.
When calculate exits, its local variables (on the right-hand side of the diagrams) disappear. The global variables (on the left-hand side) were not used in calculate, so they are unaffected. The final situation is:
(s_ccc) ---> [16]
(t_ccc) ---> [7]
Neither c_ccc nor t_ccc were changed; each still refers to the same array as before calculate. Calling calculate changed the content of the array referenced by s_ccc, using a copied reference to that array (in b).
Local variable c started as a copy of t_ccc and was manipulated within calculate, but that changed neither t_ccc itself nor it's referenced data.
The method receives variables by value. Those values can't be changed (as far as the caller of the method sees), but the values contained within them can (if it's an object, or as in this case, an array).
So when you change the value b[0] inside the array, the change can be seen outside the method. However the line
c = b;
will change the value of c inside the method, but that change will not be seen outside the method, since the value of c was passed by value.
The line calculate(s_ccc, s_ccc); indicates you're not actually doing anything to t_ccc.
However, should this line have read calculate(s_ccc, t_ccc); than the effect remains the same.
This is due to the fact that you're assigning a new value to c here: c = b;
When assigning a new value to a reference argument the reference is lost.
This is the difference between altering a by reference variable and assigning a new value.
On entry to calculate(), b points at s_ccc and c points at t_ccc. Altering b[0] will therefore change s_ccc, as you've seen.
However, the assignment
c = b;
only points c at the same object b is pointing at, namely s_ccc. It does not copy the contents of what b points to to what c points to. As a result, t_ccc is unchanged. If you added the following line to the end of calculate():
c[0] = c[0] + 5;
s_ccc[0] would be 21.