Analyzing the invariance of Integer type parameter value to understand Java value parameter passing
Previously on java There are some doubts about the reference passing of value Integer and String The method is passed in to modify, but the final value is not modified. Now, after continuous learning, I have some new experience here. Now I summarize.
Code process
(1) Code first
private void add(Integer i) { i = i - 1; } private void reverse(String s) { s = "sey"; } public static void main(String[] args) { Integer i = 1; String s = "yes"; Test test = new Test(); test.add(i); test.reverse(s); // Print value System.out.println(String.format("i Value of:%d", i)); System.out.println(String.format("s Value of:%s", s)); }
(2) The printing results are as follows:
You can see that the value has not changed. Next, let me analyze why.
(3) Decompile as follows:
public class Test { public Test() { } private void add(Integer i) { i = Integer.valueOf(i.intValue() - 1); } private void reverse(String s) { s = "sey"; } public static void main(String args[]) { Integer i = Integer.valueOf(1); String s = "yes"; Test test = new Test(); test.add(i); test.reverse(s); System.out.println(String.format("i Value of:%d", new Object[] { i })); System.out.println(String.format("s Value of:%s", new Object[] { s })); } }
We can clearly see that due to the java syntax sugar, Integer i = 1; Essentially Integer.valueOf(1). Then go deep into the Integer source code:
Familiar friends will know that Integer has a cache of - 128-127. Within this interval, it will directly get the cache from IntegerCache and return. Beyond this interval, it will return a new object.
Principle analysis
This time I will discuss from the of virtual machine stack and heap:
First, the JVM model is as follows:
(1) Display specific bytecode
implement javap -c Test.class The command decomposes the method code and displays the specific bytecode of each method
public class Test { public Test(); Code: 0: aload_0 // Load variables at local variable table [0] (usually this object) 1: invokespecial #1 / / initialization method 4: return // Method end public static void main(java.lang.String[]); Code: 0: iconst_1 // Pushes the constant 1 onto the operand stack 1: invokestatic #3 / / Boxing (Integer.valueOf()), returns an object and pushes it to the top of the stack 4: astore_1 // The top element of the stack goes out of the stack and stores the reference in the local variable table [1] 5: ldc #5 / / String yes push the items in the constant pool onto the stack 7: astore_2 // The top element of the stack goes out of the stack and stores the reference in the local variable table [2] 8: new #6 / / create a Test object (allocate memory on the heap and return a reference), and push the reference to the top of the stack 11: dup // The stack top element is out of the stack, and the stack top element is copied 12: invokespecial #7 / / the top element of the stack is out of the stack, and the instantiation init() method is called 15: astore_3 // The top element of the stack is out of the stack and will refer to the location where the local variable table [3] exists 16: aload_3 // Load reference of local variable table [3] - > corresponding test 17: aload_1 // Load reference of local variable table [1] - > corresponding i 18: invokespecial #8 / / call the instantiation method test and add() 21: aload_3 // Loading references to local variable table [3] 22: aload_2 // Loading references to local variable table [2] 23: invokespecial #9 / / call the instantiation method test and reverse() 26: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream; 29: ldc #11 / / value of string I:% d 31: iconst_1 32: anewarray #12 // class java/lang/Object 35: dup 36: iconst_0 37: aload_1 38: aastore 39: invokestatic #13 // Method java/lang/String.format:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String; 42: invokevirtual #14 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 45: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream; 48: ldc #15 / / value of string:% s 50: iconst_1 51: anewarray #12 // class java/lang/Object 54: dup 55: iconst_0 56: aload_2 57: aastore 58: invokestatic #13 // Method java/lang/String.format:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String; 61: invokevirtual #14 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 64: return }
(2) We know that the Java virtual machine is thread private. When each method is executed, the Java virtual machine will synchronously create a stack frame to store the local variable table, operand stack, dynamic link, method exit and other information. The process from the call of each method to the completion of execution corresponds to the process of a stack frame from stack entry to stack exit in the virtual machine stack, This time, we will discuss the stack frames of the two methods of the virtual machine stack: main(), add().
(3) As shown in the figure, main corresponds to a stack frame. The local variable table element i in the stack frame points to i in the heap and s points to s in the heap.
When line 18 of bytecode is executed in the main() method: invokespecial #8 / / call the instantiation method test and add() method
Because Java is passed by reference, the reference address of i in the local variable table is passed to the i parameter in the add() method of the instantiated object test. At the same time, the add() method is called, and the stack frame of the add() method is pressed into the Java Virtual machine stack. At the same time, the stack frame has its own local variable table i, which points to the memory i in the heap.
Because i is a local variable at this time, it only exists in the stack frame of add when executing the code
i = i - 1;(Equivalent to execution i = Integer.valueOf(i - 1) );
As shown in the figure, in the local variable table in the stack frame of the add() method, i memory points to i1.
To sum up, we can see that the memory address of the local variable table i in the stack frame of the original main() method has not changed, so it will not change when printing. Only the memory address in the heap pointed to by i in the local variable table in the add() stack frame is changed, which will not affect the i in the local variable table of the main() method.
summary
The transfer method in Java is reference transfer. If you want to modify the value of a variable in a method, you can only modify the value of the original variable pointing to the memory in the heap, not by changing the reference address of the object in the method.
public class Test { Integer x = 1; Integer y = 2; /** * Exchange variable */ private void swap(Integer i1, Integer i2) { i1 = i1 ^ i2; i2 = i1 ^ i2; i1 = i1 ^ i2; } private void swap(Test test) { Integer x = test.x; Integer y = test.y; x = x ^ y; y = x ^ y; x = x ^ y; test.x = x; test.y = y; } public static void main(String[] args) { Integer i = 1; Integer j = 2; Test test = new Test(); test.swap(i, j); System.out.println(i); System.out.println(j); test.swap(test); System.out.println(test.x); System.out.println(test.y); } }
In this way, the values of variables can be successfully exchanged: the results are as follows