误区总是有人认为Java的赋值操作和传递参数操作都是passing by value和passing by reference。这很明显是错误的,在Java中只有 passing by value。众所周知,Java有两种数据类型结构:基本数据类型与引用数据类型。那么在赋值和传参自然也只会涉及到这两种。刨根究底,在内存中,基本数据类型的值是存在栈内存中的,而引用数据类型中,它的引用也是存在栈内存中的,但是这个引用指向的对象是存在堆内存中。存储方式不同,那么在赋值和参数传递中肯定会有一些不同。
赋值与参数传递
基本类型
对基本数据类型的赋值很简单,基本类型存储了实际的数值,而并非指向一个对象的引用,所以在为其赋值的时候,是直接将一个地方的内容复制到了另一个地方.例如:对基本类型使用a=b ,那么b的内容就复制给了a.若接着又修改了a,b的值是不会受影响的。
引用类型
但是在为对象赋值的时候,情况却发生了变化,对一个对象进行操作的时候,我们真正操作的是对对象的引用。所以倘若将“一个对象赋值给另一个对象”,实际上是将“引用”从一个地方复制到了另一个地方。倘若对对象使用c=d,那么c和d都会指向原本只有d指向的那个对象。
详解
例子1:
public class Solution {
private int a;
private int b;
public void setA(int a) {
this.a = a;
}
public void setB(int b) {
this.b = b;
}
}
Solution s1 = new Solution();
s1.setA(1);
s1.setB(2);
Solution s2 = s1;
s2.setA(-1);
设置断点,F11运行程序并打开Debug视图,在Variables一览我们可以看到s1的a,b的id与值和s2的a,b的id和值是完全一样的。
如果你无法理解为什么s2改变后,s1的值也会改变。那么就是引用类型和基本类型的理解不够清楚。
①:对于Solution s1 = new Solution();这条语句,这条语句执行的动作是创建一个对象,我们都很明白,但是它确包含了四个步骤:
②右边“new Solution”,表示以Solution类为模板,在堆空间中创建一个Solution类对象;
“③()”,这对括号表示,在对象创建后,会立马调用Solution类的构造函数,由于没有给参数,所以会调用默认的无参构造。
④左边的“Solution s1 ”创建了一个Solution类的引用变量,也就是说用来指向Solution对象的对象引用。这和C语言中的指针可以理解为一个意思。
这条语句包含了两个实体:一个是对象引用变量,一个是对象实体。
如上面说的s1和s2都指向了同一个对象。既然两个引用指向同一个对象,那么不管使用哪个引用操纵对象,对象的内容都发生改变,并且只有一份,通过s1和s2得到的内容自然也一样。
例子2:
package TempFile;
public class Solution {
//基本类型的参数传递
public static void fun1(int m){
m = 100;
}
//参数为对象,不改变引用的值
public static void fun2(StringBuffer s){
s.append("fun2");
}
//参数为对象,改变引用的值
public static void fun3(StringBuffer s){
s = new StringBuffer("fun3");
}
public static void main(String[] args) {
int i = 1;
Solution s1 = new Solution();
System.out.println(i);//i = 1
StringBuffer ss = new StringBuffer("main");
System.out.println(ss.toString());//main
fun2(ss);
System.out.println(ss.toString());//mainfun2
fun3(ss);
System.out.println(ss.toString());//mainfun2
}
}
可以看到如果变量的引用是同一个对象的话,那么如果一个引用是变量发生变化,其他的引用也会随之而发生变化。如果引用的不是同一个对象,例如fun3()这个函数,由于s=new StringBuffer(),也就是s,ss此时已经指向不同的对象,那么他两自然已经没有关系了。所以不论对s做什么,ss都不会受到影响。