新手上路,记录自己的理解。
在C++中,函数传值有两种方式,即值传递和引用传递。而在Java中,只有值传递,即便传递的对象为引用类型(如String)。所谓的值传递是指在调用函数时将实参复制一份传递到函数中,这样函数中操作的是实参的副本,不会对实参造成修改;所谓的引用传递是指在调用函数时将实参的地址传递到函数中,这样函数中操作的是实参对象,函数内部的修改会影响到实参。
由此可见,值传递与引用传递的本质区别是:值传递会产生一个副本,而引用传递不会。因此,值传递才不会改变原实参的值,而引用传递会。
下面以三个例子(基本数据类型、字符串、类)进行阐述:
//基本数据类型
public static void main(String[] args)
{
int a = 1;
System.out.println("调用函数前a的值为:" + a);
System.out.println("a的内存地址为:" + System.identityHashCode(a));
fun(a);
System.out.println("调用函数后a的值为:" + a);
}
public static void fun(int b)
{
b = 2;
System.out.println("b的内存地址为:" + System.identityHashCode(b));
}
调用函数前a的值为:1
a的内存地址为:1989780873
b的内存地址为:1480010240
调用函数后a的值为:1
/引用类型:字符串
public static void main(String[] args)
{
String a = "河图";
System.out.println("调用函数前a的值为:" + a);
System.out.println("a的内存地址为:" + System.identityHashCode(a));
fun(a);
System.out.println("调用函数后a的值为:" + a);
}
public static void fun(String b)
{
b = "洛书";
System.out.println("b的内存地址为:" + System.identityHashCode(b));
}
调用函数前a的值为:河图
a的内存地址为:1078694789
b的内存地址为:81628611
调用函数后a的值为:河图
public class Practice
{
//引用类型:类和对象
public static void main(String[] args)
{
Person a = new Person();
a.age = 18;
System.out.println("调用函数前a的值为:" + a.age);
System.out.println("a的内存地址为:" + System.identityHashCode(a));
System.out.println("a属性的内存地址为:" + System.identityHashCode(a.age));
fun(a);
System.out.println("调用函数后a的值为:" + a.age);
}
public static void fun(Person b)
{
b.age = 0;
System.out.println("b的内存地址为:" + System.identityHashCode(b));
System.out.println("b属性的内存地址为:" + System.identityHashCode(b.age));
}
}
class Person
{
int age;
}
调用函数前a的值为:18
a的内存地址为:1480010240
a属性的内存地址为:81628611
b的内存地址为:1480010240
b属性的内存地址为:1828972342
调用函数后a的值为:0
对基本数据类型而言,其值存在栈中。参数传递时,形参从栈上复制实参的值,但本身已是两个对象,因此不改变实参值。
对引用类型(字符串)而言,其引用存在栈中,其值存在堆中。形参从栈上复制其引用,因此可以访问到堆上的值。而函数中b变更了引用,此时b指向堆中另一片内存,无法访问实参引用所指向堆中的值,但不影响实参值。
对引用类型(类)而言,其引用存在栈中,其值存在堆中。形参从栈上复制其引用,因此可以访问到堆上的值。函数中b变更了属性的值,但并未变更其引用,因此b仍然可以访问到实参引用所指向堆中的值。而栈中的值不可更改,堆中的值可以更改,因此最终属性age的值被更改。
我们可以稍微更改下代码:
public class Practice
{
//引用类型:类和对象
public static void main(String[] args)
{
Person a = new Person();
a.age = 18;
System.out.println("调用函数前a的值为:" + a.age);
System.out.println("a的内存地址为:" + System.identityHashCode(a));
System.out.println("a属性的内存地址为:" + System.identityHashCode(a.age));
fun(a);
System.out.println("调用函数后a的值为:" + a.age);
}
public static void fun(Person b)
{
b = new Person();//在这里更改b自身的引用
b.age = 0;
System.out.println("b的内存地址为:" + System.identityHashCode(b));
System.out.println("b属性的内存地址为:" + System.identityHashCode(b.age));
}
}
class Person
{
int age;
}
调用函数前a的值为:18
a的内存地址为:1480010240
a属性的内存地址为:81628611
b的内存地址为:1828972342
b属性的内存地址为:1452126962
调用函数后a的值为:18
显而易见,a与b的地址不同了,此时在b中改变属性不会再影响到a。
所以,Java中只有值传递。