ps
最近在看java究竟是只有值传递还是值传递和引用传递都有这个问题的博客时,各有各的说法,有一篇博客的评论使我动了很大的好奇心
博客地址:http://www.cnblogs.com/binyue/p/3862276.html
大概内容
如下的一行代码,调用test2方法时,传递过来的对象是属于值传递还是引用传递
public class Test {
public static void main(String[] args) {
Test t = new Test();
MyObj obj = new MyObj();
t.test2(obj);// 这里传递的参数obj就是引用传递
// System.out.println(obj.b);
}
public void test2(MyObj obj) {
obj = new MyObj(); // 纠结在这一行
obj.b = 100;
}
}
class MyObj {
public int b = 99;
}
我的疑问
1.通过这个开始了解到底调用方法时传递的是值传递还是引用传递
2.调用方法时,如何确定实参给形参时,确实开辟了一份栈内存给形参
我的解决方式
我采用javap 看class指令,来确认传递的是值还是引用
javap -c Test.class
但是面临了一个问题,我对jvm不是很熟,只知道字节码的反编译命令,所以我采用了循序渐进的方式
1.先纯main方法时,编译,然后查看字节码
2.main方法输入 Test t = new Test(); 再编译,再查看
3.依次类推,一行行的代码进行编译,和查看
得到以下结果
字节码+查看字节码大全的解析
public class com.fanfan.test.Test {
///=========生成默认的无参数构造函数 start =======
public com.fanfan.test.Test(); ///
Code:
0: aload_0 ///aload_0 此指令告诉虚拟机,将局部变量this放入操作栈里。
1: invokespecial #8 // Method java/lang/Object."<init>": ///编译时方法绑定调用方法。 也就是绑定主方法main
()V
4: return ///void函数返回。
///=========生成默认的无参数构造函数 start =======
public static void main(java.lang.String[]);
Code:
/// =====start new test对象====
0: new #1 // class com/fanfan/test/Test ///new Test对象
3: dup ///复制栈顶一个字长的数据,将复制后的数据压栈。
4: invokespecial #16 // Method "<init>":()V ///运行时方法绑定调用方法 也就是 对象的无参数构造函数
7: astore_1 ///将栈顶引用类型值保存到局部变量1中。 也就是把new好的对象栈空间地址交给 t
/// =====end new test对象======
/// =====start通过 new test对象很容易知道 这一块new了 Object对象====
8: new #17 // class com/fanfan/test/MyObj
11: dup
12: invokespecial #19 // Method com/fanfan/test/MyObj."<in
it>":()V
15: astore_2
/// =====end 通过 new test对象很容易知道 这一块new了 Object对象======
16: aload_1 ///从局部变量1中装载引用类型值入栈。 把Test对象的实例t
17: aload_2 ///从局部变量2中装载引用类型值入栈。 把MyObj对象的实例obj
18: invokevirtual #20 // Method test2:(Lcom/fanfan/test/My ///调用方法test2
Obj;)V
21: return
public void test2(com.fanfan.test.MyObj);
Code:
//=======obj = new MyObj(); ==
0: new #17 // class com/fanfan/test/MyObj
3: dup
4: invokespecial #19 // Method com/fanfan/test/MyObj."<in
it>":()V
7: astore_1
//=======obj = new MyObj(); ==
8: aload_1
9: bipush 100
11: putfield #29 // Field com/fanfan/test/MyObj.b:I
14: return
}
上述字节码对应的代码
package com.fanfan.test;
public class Test {
public static void main(String[] args) {
Test t = new Test();
MyObj obj = new MyObj();
t.test2(obj);// 这里传递的参数obj就是引用传递
// System.out.println(obj.b);
}
public void test2(MyObj obj) {
obj = new MyObj(); // 纠结在这一行
obj.b = 100;
}
}
class MyObj {
public int b = 99;
}
结果
1.从字节码指令的16,17确实可以看出 ,传递的只是值传递,也就是类的实例
16: aload_1 ///从局部变量1中装载引用类型值入栈。 把Test对象的实例t
17: aload_2 ///从局部变量2中装载引用类型值入栈。 把MyObj对象的实例obj
2.对于第二问,目前还是不敢十分确定调用方法时,实参传递给形参时又开辟了一块内存地址,因为从字节码中看出,只是将局部变量的值重新压入栈内存中。
所以我斗胆认为,实参传递给形参时,并没有开辟一块存区域,只是调用test2方法占用的内存区域
参考博客
jvm入门
java字节码入门(上)
https://blog.csdn.net/xiandafu/article/details/51458791
字节码大全表
https://www.cnblogs.com/longjee/p/8675771.html
句柄
https://blog.csdn.net/lly983909814/article/details/72529773