我们为啥要讨论java传值还是传引用?因为不同的传递方式,导致的结果不一样
change(int s){s=100;}
void test(){
int i=10;
change(i);//i的值不会变
}
change2(Student student){
student.setScore(100);
}
void test2(){
Student stu=new Student();
stu.setScore(90);
change2(stu);//stu的score变成100
}
上面的结果大家想必都知道了,但是为啥?什么叫传值,什么叫传引用,还有些地方说java只会有传值,到底是什么意思,其实不用过于纠结,知道原理就好办了...
jvm中,每个线程对应一个栈结构,这个线程执行过程中,每调用一个方法,就会往这个栈中压入一个栈桢,方法里的参数、局部变量都存在栈桢里,一个叫局部变量表的地方
如果变量是基本类型的,比如int i=10;那在局部变量表中存的就是10这个值
如果变量是非基本类型的,比如Student stu=new Student();那在局部变量表中存的就是stu这个变量值,那stu的值是啥呢?就是一串地址,比如0x0001,这个地址指向了new Student()这个对象在堆中的起始地址(在java中,如果一个变量中存储的数值代表的是另外一块内存的起始地址,就称这个变量为引用,stu是new Student()对象的引用)
那么,重点来了,在方法调用时,我们传的到底是啥?其实就是局部变量表里存的东西,如果是基本类型,那就是它对应的值10,如果是非基本变量,那就是一串地址0x0001(上面说了,地址代表引用)。所以说基本类型传值,非基本类型传引用,原因就在于此
那传过去后,发生了什么呢,我再把上面的代码贴过来
change(int s){s=100;}
void test(){
int i=10;
change(i);
}
前面说了,每调用一个方法就往线程栈里压入一个栈桢,桢里面有局部变量表,参数就存在里面
test方法被调用时,压入一个栈桢A,A里面有个局部变量表,存了个10,调用change后,又压入了一个新栈桢B,B中的局部变量表里也存了个10,change方法里将参数重新赋值为100,就将局部变量表里的10替换成了100,当然了,这个局部变量表是B栈桢里的,和A已经没啥关系了,所以i依然是10
change2(Student student){
student.setScore(100);
}
void test2(){
Student stu=new Student();
stu.setScore(90);
change2(stu);
}
test2方法被调用时,压入一个栈桢A,A里面有个局部变量表,存了个0x0001,调用change2后,又压入了一个栈桢B,B中的局部变量表里也存了个0x0001,指向了new Student()对象在堆内存中的起始地址,调用setScore(100)操作时,jvm会通过0x0001找到new Student()对象,然后把它里面的socre属性设置为100,由于外面的stu也指向着new Student(),所以score=100,对它也是可见的
我们再改一改
change2(Student student){
student=new Student();
student.setScore(100);
}
change2方法新增了一行,student=new Student();其他不变,那对外面的stu还有影响吗?
答案是没有,student=new Student()操作干了这样几件事,在堆中创建一个新的Student对象,拿到它的起始地址,赋值给student变量,并存在change2方法的局部变量表中,对应的变化就是这样
student指向变成了新的Student对象,和stu已经没啥关系了,自然对它没有影响
从上面我们可以看出来,赋值操作的特殊性,赋值操作的作用对象是变量本身,而setScore()这种操作,作用对象是堆中的内容,两者的含义是完全不同的。赋值操作会将变量的值变成新值,不管是基本类型还是引用类型,一旦被重新赋值,那它就不是原来的它了,两者就不再有任何联系了。而变量调用setScore()这种操作,那它还是原来的它,只是内容变了下
所以再看到类似代码时,只要你看到的是赋值操作,甭管它是啥类型,和外面的变量就没啥关系了,方法里咋变都对外面没影响
写到这里我想到网上有一类用String类型当做参数的问题,大意是这样的:
public static void changeString(String s){//改变String型变量的函数
s="changeString";
}
调用代码:
String str="abc";
changeString(str);
完了之后当然str还是"abc",这是对的,但是好多解释竟然说是因为String是不变类,所以这里怎么修改str还是不会变
好吧,String类确实是final类型的,但这并不应该拿来解释这个现象,进入changeString方法后,做了一个赋值操作,s指向另一个对象"changeString"的地址去了,当然怎么变也不会影响到str了,就这么简单