今天看到(Java核心计数)Java方法对参数按值传递的时候,居然怎么都想不通,故而自己写代码尝试了理解了一下:
定义一个Person类:
class Person
{
private double m_dAge;
public Person(double age)
{
m_dAge = age;
}
public void setAge(double age)
{
m_dAge = age;
}
}
Person只有一个年龄和一个修改年龄的方法。
在Java中,既然说对于基本数据类型和对象引用都采用值传递的方式进行,基本数据好理解,那就不多说了,主要看看对象。
增加一个交换对象的方法和增加年龄的方法:
private static void raiseAge(Person p)
{
p.setAge(100);
}
private static void swap(Person p1,Person p2)
{
Person temp = p1;
p1 = p2;
p2 = temp;
}
执行如下的增加年龄和交换两人的代码:
Person aaa = new Person(10);
Person bbb = new Person(20);
Person ccc = new Person(30);
raiseAge(aaa);//增加aaa的年龄到100
swap(bbb,ccc);//交换bbb和ccc
实际运行结果怎么样呢?
结果:1、aaa年龄增加到了100;2、bbb和ccc并没有交换。
按照正常思路来说,既然说了按值传递了,那aaa年龄增加了是怎么回事儿,aaa都变了而bbb和ccc没变又是怎么回事?
以下就说说个人观点(总觉得书上说得不明不白):
首先,需要明确一点,对于以上3个Penson变量,注意是变量而不是对象,变量经过=赋值一个new的对象后,说变量引用一个对象。重要的事情说三遍:变量不是对象、变量不是对象、变量不是对象。
其次,才开始说说按值传递的事儿。
对于aaa、bbb、ccc三个变量来说,其值为指向的引用对象,分别假设给三个对象一个具体的值(person1、person2、person3,三个假设值为内存对象)。
先说raiseAge,当我们将aaa传递给方法时,实际上是使得p也指向了aaa所指向的person1,也即这个时候person1有两个引用。那么在raiseAge方法中调用setAge,实际上就是改变了person1的内存,这个就无可争议了!
再说说swap方法,同样的道理,在方法执行前内存为person2(bbb)和person3(ccc),传参时p1指向了bbb、p2指向了ccc,这个时候person2(bbb、p1)和person3(ccc、p2)都有两个引用。当我们将p1和p2进行交换的时候,内存对象变为了person2(bbb、p2)和person3(ccc、p1),有没有发现,对比前后的内存,实际上我们将p1h和p2给交换掉了,可bbb和ccc还在原来的位置没变。再回到方法raiseAge,当方法执行完毕后变量p1和p2释放掉了,内存变为了person2(bbb)和person3(ccc),实际上原有的内存依旧没变。这就是为什么swap不成功了。
总结,也就是说,在参数传递的时候,按值传递,传递的是变量本身,区别于引用传递,引用传递的是对象的地址。而Java中的变量不等于对象,这是必须要明白的,变量只是一个对象的引用标识,无论这个变量怎么改变都不会改变对象本身。
或者说,还有另外一种理解,对于变量,从内存上看,变量应该是存放在栈区中的,而对象是存放在堆区中的,变量对对象的引用,实际就是在栈区中存放着堆区中的地址,两个不相关的东西怎么都不会相互影响的。