什么是值传递,什么是引用传递呢?在深入了解这个问题之前,我们先通过一段代码来看看:
public class Main {
public static void main(String[] args) {
int c = 2; //c 叫做实参
User user = new User(); //user叫做实参
user.setName("nick");
user.setAge(2);
pass(user, c);
System.out.println("c的值是:" + c );
System.out.println("name的值是:" + user.getName());
}
public static void pass(User user, int c) {
c = 3;
user.setName("pass");
}
}
class User {
String name;
int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "name = " + name + " --- age = " + age;
}
}
代码运行结果:
c的值是:2
name的值是:pass
你会发现,为什么c的值保持不变,而d的值却变了呢?这就是值传递和引用传递区别导致的。在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数,这个过程叫值传递。在调用函数时将实际参数的地址直接传递到函数中的形参,那么在函数中对参数所进行的修改,将影响到实际参数,这个过程叫做引用传递。
那为什么值传递后,原来的值不变,而引用传递却变了呢?我们再来复习下基本类型和引用类型的值是如何存储的,int c = 66由于是基本类型,值将会直接存储在栈内存中,而user由于是基本类型,实际值存储在堆内存中,user引用存储在栈内存中,并且指向实际值地址。
main() 方法中的 user 引用,与 pass() 方法中的 user 引用,指向的是同一个堆内存中的 user对象,红色虚线是在 main() 方法中初次给 name 属性赋的值"main"。实线部分,是在 pass() 方法中给 name 属性赋的值"pass"。因为在堆内存中只有一个 User 类实体,因此 main() 方法与 pass() 方法中的 user 指向的都是同一个 User 类 0x000031。因此,无论在 main() 方法还是 pass() 方法中,改变其 user 的属性值后,打印 User 类的属性值肯定是一样的,他们用的是一个实体类。而可以看到基本类型d在栈内存中,分别有两个,一个是main方法的d=66,一个是pass方法的d=0,在pass方法赋值时,是赋值给了d=0这个值类型,所以原先的d=66这个值类型依然保持不变。
对以上内容理解清楚后,我们再来看一个例子:
public class StringBase {
public static void main(String[] args) {
int c = 2; //c 叫做实参
String b = "hello"; // b 叫做实参
StringBase stringBase = new StringBase();
stringBase.change(c, b); // 此处 c 与 b 叫做实参
System.out.println("c的值是:" + c );
System.out.println("b的值是:" + b);
}
public void change(int a, String b) { // a 与 b 叫做形参
a = 3;
b = "no";
}
}
运行结果为:
c的值是:2
b的值是: hello
这个时候是不是有点懵了,按照我的理解,String是对象类型,应该属于引用类型了,内部b赋值应该会影响外边才对啊!怎么b的值没有变呢?
String类型在值传递和引用传递问题中比较特殊,为什么说特殊呢,因为对于一些常量字符串的创建,只要判断对象在堆中不存在,便会创建一个新的,如果是创建新对象,那么引用地址都会变。我们可以通过一个简单的例子来解释下:
String a = "hello"; //a 相当于实参
String b= a; //a1 就相当于形参
b = "你好";
System.out.println("a是:" + a + " --- b是:" + b);
运行结果为:
a是:hello --- b是:你好
String a = "hello"; 在 String 池中检查并创建一个常量:"hello",给 a 分配一个栈内存,在此存储常量 hello 的地址。
String b= a; 给 b 分配一个栈内存,在此存储常量 hello 的地址。相当于 a 把自己持有的地址,复制给了 b。
b = "你好"; 在 String 池中检查是否有 "你好" 的常量。如果有,将 b 的地址指向 "你好" 的地址。如果 String 池中没有 "你好" 常量,在堆内存中创建 "你好" 常量,并将 b 地址指向 "你好"。
通过这个例子,我们再来回顾下最开始的这个试题,如果我们将在pass方法中的赋值改成new对象复制,结果又会是什么样呢?
public class Main {
public static void main(String[] args) {
int c = 2; //c 叫做实参
String d = "hello"; //d 叫做实参
User user = new User(); //user叫做实参
user.setName("main");
user.setAge(2);
System.out.println("c的值是:" + c );
System.out.println("name的值是:" + user.getName());
}
public static void pass(User user, int c) {
c = 3;
User user1 = new User(); //user叫做实参
user1.setName("main");
user1.setAge(2 );
user = user1;
}
}