Java方法调用的一切中心都是栈(Stack).
每一个方法调用都产生一个独立的栈桢(Stack Frame)。在方法调用开始的时候,会把操作数压栈,return的时候,如果有返回值,则把返回值压入调用者程序的栈。方法结束后其栈桢被销毁。
所谓的传值就是这样实现的。
进入call()方法后,t和t2都只是本地变量,t的引用值由调用者压栈。对t执行setName()显然仍然执行对原对象的调用。而最后t=t2是通过一次压栈和一次弹出完成的。但是请记住,这个栈桢在这一句后被立即销毁,不会对调用者的栈桢产生影响。
其实,不需要花费力气在书籍中精心捉摸别人遣词造句,任何有心人都可以自己验证这个过程,这完全是最基本的jdk的功能:
对于如下程序:
Java代码
23: public static void call(Test t); {
24: Test t2 = new Test();;
25: t2.setName("cba");;
26: t.setName("abc");;
27: t = t2 ;
28: }
29:
30: public static void main(String[] arg); {
31: Test obj = new Test();;
32: call (obj); ;
33: System.out.println("obj"+obj.getName(););;
34: }
23: public static void call(Test t); {
24: Test t2 = new Test();;
25: t2.setName("cba");;
26: t.setName("abc");;
27: t = t2 ;
28: }
29:
30: public static void main(String[] arg); {
31: Test obj = new Test();;
32: call (obj); ;
33: System.out.println("obj"+obj.getName(););;
34: }
jdk1.4.2编译器产生的字节码如下:
Java代码
public static void call(Test);;
Code:
Stack=2, Locals=2, Args_size=1
0: new #3; //class Test
3: dup
4: invokespecial #4; //Method "<init>":();V
7: astore_1
8: aload_1
9: ldc #5; //String cba
11: invokevirtual #6; //Method setName:(Ljava/lang/String;);V
14: aload_0
15: ldc #7; //String abc
17: invokevirtual #6; //Method setName:(Ljava/lang/String;);V
20: aload_1
21: astore_0
22: return
LineNumberTable:
line 24: 0
line 25: 8
line 26: 14
line 27: 20
line 28: 22
LocalVariableTable:
Start Length Slot Name Signature
0 23 0 t LTest;
8 15 1 t2 LTest;
public static void main(java.lang.String[]);;
Code:
Stack=3, Locals=2, Args_size=1
0: new #3; //class Test
3: dup
4: invokespecial #4; //Method "<init>":();V
7: astore_1
8: aload_1
9: invokestatic #8; //Method call:(LTest;);V
12: getstatic #9; //Field java/lang/System.out:Ljava/io/PrintStream;
15: new #10; //class StringBuffer
18: dup
19: invokespecial #11; //Method java/lang/StringBuffer."<init>":();V
22: ldc #12; //String obj
24: invokevirtual #13; //Method java/lang/StringBuffer.append:(Ljava/lang/
String;);Ljava/lang/StringBuffer;
27: aload_1
28: invokevirtual #14; //Method getName:();Ljava/lang/String;
31: invokevirtual #13; //Method java/lang/StringBuffer.append:(Ljava/lang/
String;);Ljava/lang/StringBuffer;
34: invokevirtual #15; //Method java/lang/StringBuffer.toString:();Ljava/la
ng/String;
37: invokevirtual #16; //Method java/io/PrintStream.println:(Ljava/lang/St
ring;);V
40: return
LineNumberTable:
line 31: 0
line 32: 8
line 33: 12
line 34: 40
LocalVariableTable:
Start Length Slot Name Signature
0 41 0 arg [Ljava/lang/String;
8 33 1 obj LTest;
public static void call(Test);;
Code:
Stack=2, Locals=2, Args_size=1
0: new #3; //class Test
3: dup
4: invokespecial #4; //Method "<init>":();V
7: astore_1
8: aload_1
9: ldc #5; //String cba
11: invokevirtual #6; //Method setName:(Ljava/lang/String;);V
14: aload_0
15: ldc #7; //String abc
17: invokevirtual #6; //Method setName:(Ljava/lang/String;);V
20: aload_1
21: astore_0
22: return
LineNumberTable:
line 24: 0
line 25: 8
line 26: 14
line 27: 20
line 28: 22
LocalVariableTable:
Start Length Slot Name Signature
0 23 0 t LTest;
8 15 1 t2 LTest;
public static void main(java.lang.String[]);;
Code:
Stack=3, Locals=2, Args_size=1
0: new #3; //class Test
3: dup
4: invokespecial #4; //Method "<init>":();V
7: astore_1
8: aload_1
9: invokestatic #8; //Method call:(LTest;);V
12: getstatic #9; //Field java/lang/System.out:Ljava/io/PrintStream;
15: new #10; //class StringBuffer
18: dup
19: invokespecial #11; //Method java/lang/StringBuffer."<init>":();V
22: ldc #12; //String obj
24: invokevirtual #13; //Method java/lang/StringBuffer.append:(Ljava/lang/
String;);Ljava/lang/StringBuffer;
27: aload_1
28: invokevirtual #14; //Method getName:();Ljava/lang/String;
31: invokevirtual #13; //Method java/lang/StringBuffer.append:(Ljava/lang/
String;);Ljava/lang/StringBuffer;
34: invokevirtual #15; //Method java/lang/StringBuffer.toString:();Ljava/la
ng/String;
37: invokevirtual #16; //Method java/io/PrintStream.println:(Ljava/lang/St
ring;);V
40: return
LineNumberTable:
line 31: 0
line 32: 8
line 33: 12
line 34: 40
LocalVariableTable:
Start Length Slot Name Signature
0 41 0 arg [Ljava/lang/String;
8 33 1 obj LTest;
最后要批判一下,这种程序不应该在任何实际代码中出现。楼主看待Java,思维仍然停留在C的全局内存分配和“变量=指针”的思路上,所以拼命试图理解到底是传指针还是传引用。而实际上Java拜虚拟机所赐,他的内存已经可以严格隔离为常量池/静态方法区/堆栈区。在java中要牢记“变量=堆栈”。换句话说,被调用的方法唯一能够影响调用者变量(即堆栈)的机会,就是return 一个对象/值,其生成的字节码是ireturn或者areturn,包含有一次对调用者栈桢的压栈操作。
每一个方法调用都产生一个独立的栈桢(Stack Frame)。在方法调用开始的时候,会把操作数压栈,return的时候,如果有返回值,则把返回值压入调用者程序的栈。方法结束后其栈桢被销毁。
所谓的传值就是这样实现的。
进入call()方法后,t和t2都只是本地变量,t的引用值由调用者压栈。对t执行setName()显然仍然执行对原对象的调用。而最后t=t2是通过一次压栈和一次弹出完成的。但是请记住,这个栈桢在这一句后被立即销毁,不会对调用者的栈桢产生影响。
其实,不需要花费力气在书籍中精心捉摸别人遣词造句,任何有心人都可以自己验证这个过程,这完全是最基本的jdk的功能:
对于如下程序:
Java代码
23: public static void call(Test t); {
24: Test t2 = new Test();;
25: t2.setName("cba");;
26: t.setName("abc");;
27: t = t2 ;
28: }
29:
30: public static void main(String[] arg); {
31: Test obj = new Test();;
32: call (obj); ;
33: System.out.println("obj"+obj.getName(););;
34: }
23: public static void call(Test t); {
24: Test t2 = new Test();;
25: t2.setName("cba");;
26: t.setName("abc");;
27: t = t2 ;
28: }
29:
30: public static void main(String[] arg); {
31: Test obj = new Test();;
32: call (obj); ;
33: System.out.println("obj"+obj.getName(););;
34: }
jdk1.4.2编译器产生的字节码如下:
Java代码
public static void call(Test);;
Code:
Stack=2, Locals=2, Args_size=1
0: new #3; //class Test
3: dup
4: invokespecial #4; //Method "<init>":();V
7: astore_1
8: aload_1
9: ldc #5; //String cba
11: invokevirtual #6; //Method setName:(Ljava/lang/String;);V
14: aload_0
15: ldc #7; //String abc
17: invokevirtual #6; //Method setName:(Ljava/lang/String;);V
20: aload_1
21: astore_0
22: return
LineNumberTable:
line 24: 0
line 25: 8
line 26: 14
line 27: 20
line 28: 22
LocalVariableTable:
Start Length Slot Name Signature
0 23 0 t LTest;
8 15 1 t2 LTest;
public static void main(java.lang.String[]);;
Code:
Stack=3, Locals=2, Args_size=1
0: new #3; //class Test
3: dup
4: invokespecial #4; //Method "<init>":();V
7: astore_1
8: aload_1
9: invokestatic #8; //Method call:(LTest;);V
12: getstatic #9; //Field java/lang/System.out:Ljava/io/PrintStream;
15: new #10; //class StringBuffer
18: dup
19: invokespecial #11; //Method java/lang/StringBuffer."<init>":();V
22: ldc #12; //String obj
24: invokevirtual #13; //Method java/lang/StringBuffer.append:(Ljava/lang/
String;);Ljava/lang/StringBuffer;
27: aload_1
28: invokevirtual #14; //Method getName:();Ljava/lang/String;
31: invokevirtual #13; //Method java/lang/StringBuffer.append:(Ljava/lang/
String;);Ljava/lang/StringBuffer;
34: invokevirtual #15; //Method java/lang/StringBuffer.toString:();Ljava/la
ng/String;
37: invokevirtual #16; //Method java/io/PrintStream.println:(Ljava/lang/St
ring;);V
40: return
LineNumberTable:
line 31: 0
line 32: 8
line 33: 12
line 34: 40
LocalVariableTable:
Start Length Slot Name Signature
0 41 0 arg [Ljava/lang/String;
8 33 1 obj LTest;
public static void call(Test);;
Code:
Stack=2, Locals=2, Args_size=1
0: new #3; //class Test
3: dup
4: invokespecial #4; //Method "<init>":();V
7: astore_1
8: aload_1
9: ldc #5; //String cba
11: invokevirtual #6; //Method setName:(Ljava/lang/String;);V
14: aload_0
15: ldc #7; //String abc
17: invokevirtual #6; //Method setName:(Ljava/lang/String;);V
20: aload_1
21: astore_0
22: return
LineNumberTable:
line 24: 0
line 25: 8
line 26: 14
line 27: 20
line 28: 22
LocalVariableTable:
Start Length Slot Name Signature
0 23 0 t LTest;
8 15 1 t2 LTest;
public static void main(java.lang.String[]);;
Code:
Stack=3, Locals=2, Args_size=1
0: new #3; //class Test
3: dup
4: invokespecial #4; //Method "<init>":();V
7: astore_1
8: aload_1
9: invokestatic #8; //Method call:(LTest;);V
12: getstatic #9; //Field java/lang/System.out:Ljava/io/PrintStream;
15: new #10; //class StringBuffer
18: dup
19: invokespecial #11; //Method java/lang/StringBuffer."<init>":();V
22: ldc #12; //String obj
24: invokevirtual #13; //Method java/lang/StringBuffer.append:(Ljava/lang/
String;);Ljava/lang/StringBuffer;
27: aload_1
28: invokevirtual #14; //Method getName:();Ljava/lang/String;
31: invokevirtual #13; //Method java/lang/StringBuffer.append:(Ljava/lang/
String;);Ljava/lang/StringBuffer;
34: invokevirtual #15; //Method java/lang/StringBuffer.toString:();Ljava/la
ng/String;
37: invokevirtual #16; //Method java/io/PrintStream.println:(Ljava/lang/St
ring;);V
40: return
LineNumberTable:
line 31: 0
line 32: 8
line 33: 12
line 34: 40
LocalVariableTable:
Start Length Slot Name Signature
0 41 0 arg [Ljava/lang/String;
8 33 1 obj LTest;
最后要批判一下,这种程序不应该在任何实际代码中出现。楼主看待Java,思维仍然停留在C的全局内存分配和“变量=指针”的思路上,所以拼命试图理解到底是传指针还是传引用。而实际上Java拜虚拟机所赐,他的内存已经可以严格隔离为常量池/静态方法区/堆栈区。在java中要牢记“变量=堆栈”。换句话说,被调用的方法唯一能够影响调用者变量(即堆栈)的机会,就是return 一个对象/值,其生成的字节码是ireturn或者areturn,包含有一次对调用者栈桢的压栈操作。