各种变量的存储方式:
基本数据类型:
int age = 20;
变量名age和值20都存储在jvm栈中
引用数据类型:
字符串见 https://blog.csdn.net/oooo2316/article/details/103298996
懒得看的话:字符串常量存储在堆的常量池中,字符串对象存储在堆的非常量池中
对象:
对象本身存储在堆中,包含了对象的成员变量和成员函数。
对象的地址x0123作为引用,赋值给对象变量obj。
因此,可以将x0123看作obj的值,这样一来,obj的变量名和值都是存储在jvm栈中了,只不过它的值是对象的地址。
程序运行时调用main方法,于是将mian方法的各种东西压入栈桢。
现在,假设main中新建了基本类型变量a和对象类型变量b,对象b中包含一个基本类型变量 i
。
int a = 5;
B b = new B();
b.setI(10);
还有一个可以将传入的 a
进行平方的方法 square(int a)
和将 b.i
进行平方的方法 square(B b)
。
void squareTest(int a){
a = a*a;
}
void squareTest(B obj){
obj.setI(obj.getI() * obj.getI());
}
然后调用square()方法,把a和b传入,
调用square()时,jvm再将square()方法压入栈帧,并从局部变量表中找出a和b的值复制一份过去。
而a是基本数据类型,b是引用数据类型
这样就导致,a传过去的是变量a的真实值,而b传过去的是b在jvm栈中的值,也就是b引用的对象的地址。
细心的小伙伴可能已经发现了:
在square()方法中,a的值被复制过来了,方法中操作的是a的克隆,对克隆a的操作是不会影响原来的a的;
而b复制过来的是地址,相当于C++中的址传递,对 b
进行操作时,发现它是一个地址,然后就顺着地址,找到了藏在堆中的源对象,并更改了 b.i
的值,可以看出,main函数和square()函数操作的是同一个地址,所以在square()中的更改在main函数中也可以看到。
int a = 5;
B b = new B();
b.setI(10);
//传入值
test.squareTest(a);
System.out.println(a); //5
test.squareTest(b.getI());
System.out.println(b.getI()); //10
//传入对象
test.squareTest(b);
System.out.println(b.getI()); //100
运行结果:
如果我们把 square(B b)方法改动一下:
void squareTest(B obj){
obj = new B();
obj.setI(obj.getI() * obj.getI());
}
执行 square() 方法之后main方法中的 b.i
的值会不会变呢?
运行结果:
这是因为,在更改之后的 square() 方法中,更改了变量指向的引用,从堆中新建了一个B对象,并把它的地址赋给了 square() 方法中的obj变量,但obj变量只是main函数中b变量的拷贝,所以b变量仍然指向原来的地址。
就像你又一个情敌,后来执行 obj = new B()
之后情敌喜欢别的人了,而你还是喜欢原来的。
当然, square() 执行完之后情敌和他喜欢的人都挂了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d6bGZH7s-1574949250918)(Java参数传递——值传递还是址传递?.img/image-20191128214336035.png)]
obj = new B()
之后情敌喜欢别的人了,而你还是喜欢原来的。
当然, square() 执行完之后情敌和他喜欢的人都挂了。