先上源码
public class TestRef {
public int id = 1;
public static void main(String[] args) {
int a = 10;
String b = "a";
TestRef c = new TestRef();
change(a);
change(b);
change(c);
changeRef(c);
}
public static void change(int a) {
a = 20;
}
public static void change(String b) {
b = "c";
}
public static void change(TestRef c) {
c = new TestRef();
}
public static void changeRef(TestRef c) {
c.id = 10;
}
}
再上字节码:
public class TestRef {
public int id;
public TestRef();
Code:
0: aload_0 //将this载入栈顶
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0 //再次将this载入栈顶,因为上面那行invokespecial已经消耗掉第一行的this去实例化对象了
5: iconst_1 //将常量值1推入栈顶
6: putfield #2 // Field id:I //设置偏移4那行推入的this的#2变量的值为偏移5那行所推入的值
9: return
public static void main(java.lang.String[]);
Code:
0: bipush 10
2: istore_1
3: ldc #3 // String a
5: astore_2
6: new #4 // class TestRef
9: dup
10: invokespecial #5 // Method "<init>":()V
13: astore_3
14: iload_1
15: invokestatic #6 // Method change:(I)V
18: aload_2
19: invokestatic #7 // Method change:(Ljava/lang/String;)V
22: aload_3
23: invokestatic #8 // Method change:(LTestRef;)V
26: aload_3
27: invokestatic #9 // Method changeRef:(LTestRef;)V
30: return
public static void change(int);
Code:
0: bipush 20
2: istore_0
3: return
public static void change(java.lang.String);
Code:
0: ldc #10 // String c
2: astore_0
3: return
public static void change(TestRef);
Code:
0: new #4 // class TestRef
3: dup
4: invokespecial #5 // Method "<init>":()V
7: astore_0
8: return
public static void changeRef(TestRef);
Code:
0: aload_0 //将参数载入栈
1: bipush 10 //推入一个10
3: putfield #2 // Field id:I //设置参数的#2实例变量的值为10
6: return
}
3个change(T)方法都是错误的使用例子,从字节码可以看到基本都是在操作方法自身的局部变量表,方法的参数都是一个对象引用(或者说指向对象内容的指针副本) 修改也只是将指针的值改变,而非指针指向的值
changeRef方法第一行就是 aload_0这个和构造函数第一行一样,都是将this载入栈顶
而this是一个指向对象的引用(指针)。
在main方法偏移26那行,aload_3 是将局部变量表slot 3 的的对象引用复制到栈顶,这样从旁证明了,java传递对象的时候传递的是对象的引用的副本 对象的引用是4个字节(操作数栈和局部变量表 都是一个单位等于4个字节 基本类型除了 double long之外其他都是一个单位)这也从旁佐证 方法调用传递的是对象引用副本