今天看到(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中的变量不等于对象,这是必须要明白的,变量只是一个对象的引用标识,无论这个变量怎么改变都不会改变对象本身。

或者说,还有另外一种理解,对于变量,从内存上看,变量应该是存放在栈区中的,而对象是存放在堆区中的,变量对对象的引用,实际就是在栈区中存放着堆区中的地址,两个不相关的东西怎么都不会相互影响的。