博主最先接触的是C和C++,后面自学了Java用到了现在.说来惭愧,没有特地去深究一些基础问题,也是迷迷糊糊的按着C++那一套来,最近开始看<<Thinking in Java>>, 羞于很多基础内容都没有弄明白, 故有此文.
让我们先记住结论:
1. Java中只有值传递 !只是值的形式不同, 基本数据类型传的值是内容(名副其实的传值), 对象传的值是对象的地址(传值传的值是一个地址).
2. 操纵方法体中的参数, 操纵的是另一个有自己内存空间的变量, 而不是我们传入的变量, 但是这两个变量可以指向同一个对象实体.
3.String虽然不是基本数据类型, 但其在函数参数传递过程中的表现与基本数据类型一致.
造成基本类型变量与对象变量在参数传递过程中, 表现的差异的原因在于在Java中两者容纳的值不同:
对基本类型(int, float, boolean 等)而言, 变量是非句柄变量, 即变量的值容纳的是变量的内容(名副其实的值);
对对象变量而言, 变量是句柄变量, 即变量的值容纳的是操纵对象的句柄(指向对象实体的地址, 所以虽然是值传递, 却表现出了传址的特性);
下面让我们来看具体的例子:
基本类型的参数传递:(String的表现与基本类型一致)
import java.util.*;
public class Test{
//基本类型的参数传递: 基本类型变量中容纳的就是变量的具体内容,名副其实的传值
public static void main(String[] args){
int ori = 123;
System.out.println("ori before execute doubleNum is: " + ori);
doubleNum(ori);
System.out.println("ori after execute doubleNum is: " + ori);
}
public static void doubleNum(int para){
para *= 2;
}
}
输出: ori的值没有被改变
在参数传递的过程中, 将ori变量所在内存中存放的值(这里由于是基本类型, 所以存放的值就是变量的具体内容, 也就是 123), 传递给了函数doubleNum 的参数 para 的内存空间, para 对应的内存空间存放的值也变为123, 接下来的doubleNum的操作均是对 参数 para 自己的内存空间的值( para 的具体内容), ori 的值不会受到函数 doubleNum 的影响.
对象类型的参数传递:
import java.util.*;
class ObjectValues{
public int objValue;
public ObjectValues(int a_){
objValue = a_;
}
}
public class ValuePass{
//对象变量的传值, 传递的值是一个对象的句柄(地址, 不过仍然是传值, 只是值的内容不同), 所以在执行函数之后ob变量的objValue被改变了
public static void myFunMyObject(ObjectValues o){
o.objValue = 123456;
}
public static void main(String[] args){
ObjectValues ob = new ObjectValues(233);
System.out.println("执行函数前, 对象ob的 objValue 值为: " + ob.objValue);
myFunMyObject(ob);
System.out.println("执行函数后, 对象ob的 objValue 值为: " + ob.objValue);
}
}
输出: 对象ob的objValue被函数改变了
执行 ObjectValues ob = new ObjectValues(233); 语句的时候, 在堆空间内创建了ob对象的实例, 栈空间创建了指向对象实例的句柄. 在参数传递的时候, 句柄ob存放的内容是指向对象实例的地址, 该地址被传递到了参数 o 的空间(依然是值传递, 但是值的内容是一个指向对象实体的地址), 接下来的操作同样只针对参数o的内存空间, 但是由于o 的内存中保存的是指向ob实体的地址(两个内存空间指向了同一个对象实体), 所以我们最终改变了对象ob的objValue.
赋值也是这么一个道理.
另外, 需要特别注意的是, String虽然不属于基本类型, 但是String在参数传递过程中的表现与基本类型一致!!
比较普遍的解释是, String在值传递的时候, 传递的值确实是传的地址, 但是源码中, new 了一个新的String, 接下来的操作都是对这一个new出来的String实体, 因此没有改变原本的String实体, 结果就是String的表现和基本类型一致, 方法外部的String作为参数传递进方法时, 不会被方法改变.