这是一个有点争议,但又没什么争议的问题,如果问起来你大可以回答他是值传递。下面就让我们来了解一下关于Java中有关参数传递的问题
值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。(基本数据类型复制的是它本身的值,而引用数据类型复制的是它的地址。所以说如果将一个基本数据类型和一个引用数据类型的变量传进函数,基本数据类型的函数外的实际值是不会受影响的,而引用数据的地址是不会被修改的,但是如果是修改了引用数据类型对象的属性值,函数外的实际参数是会受影响的)
(这是百度词条上面的解释)
引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
(这是百度词条上面的解释)
当你问大多数程序员Java是传值还是传引用的时候,你可能会得到两种答案之一:(1)Java传递原始类型数据时使用的是传值方式;传递对象时则使用传引用方式;String类型的数据采用的是传值方式,因为字符串是不可变的。(2)Java传递所有参数都使用传值方式。
只有第二个答案是正确的。理解传值和传引用的区别的关键是要记住,当你向一个方法传递一个对象时,Java没有把对象放入堆栈,它只是拷贝对象的引用然后将这个引用的拷贝放入堆栈。也就是说,根据定义,Java使用的是传值方式。
public class train {
public static void test(int a,int b){
int jie = a;
a = b;
b = jie;
System.out.println("测试方法中的值: a是:"+a+",b是:"+b);
}
public static void main(String[] args) {
int a = 10;
int b = 11;
test(a,b);
System.out.println("主方法中的值: a是:"+a+",b是:"+b);
}
}
运行结果:
这就可以证明参数是基本数据类型的时候传递是值传递。
public class train {
public static void test(Test test){
test.setA(100);
test.setB(110);
System.out.println("测试中的地址:"+test);
System.out.println("测试中的各属性值:a:"+test.getA()+" , b:"+test.getB());
}
public static void main(String[] args) {
Test test = new Test();
test.setA(10);
test.setB(11);
System.out.println("测试前的地址:"+test);
System.out.println("测试前的各属性值:a:"+test.getA()+" , b:"+test.getB());
test(test);
System.out.println("测试后的地址:"+test);
System.out.println("测试后的各属性值:a:"+test.getA()+" , b:"+test.getB());
}
}
class Test{
private int a;
private int b;
public Test() {
}
public Test(int a, int b) {
this.a = a;
this.b = b;
}
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public int getB() {
return b;
}
public void setB(int b) {
this.b = b;
}
}
运行结果:
这里可以看出来引用传递实际上就是值传递,传递的值是地址(指针),它通过这个地址来改变对象的属性是有效的,会影响到函数外的这个对象的实际属性。
问:请给出下面端代码的运行结果
public class train {
public static void main(String[] args) {
String name = "Scott";
StringBuilder sb = new StringBuilder("wsq");
int age = 5;
User user = new User();
user.setName(name);
user.setAge(age);
user.setSb(sb);
System.out.println("改变前: user = " + user);
change(user, name, age,sb);
System.out.println("改变后:name = " + name);
System.out.println("改变后:age = " + age);
System.out.println("改变后:sb = " + sb);
System.out.println("改变后: user = " + user);
}
public static void change(User user, String name, int age,StringBuilder sb) {
name = "Tom";
age = 20;
sb.append("test");
user.setName(name);
user.setAge(age);
user.setSb(sb);
}
static class User {
private String name;
private int age;
private StringBuilder sb;
public StringBuilder getSb() {
return sb;
}
public void setSb(StringBuilder sb) {
this.sb = sb;
}
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 "User{" +
"name='" + name + '\'' +
", age=" + age +
", sb=" + sb +
'}';
}
}
}
我的结果是:
问:
age是一个基本类型变量,User、String、StringBuilder都是引用类型变量,但是在调用change()方法之后,实参name和age都没有改变,而sb和user对象改变了。那么可不可以说调用change()方法时,User对象是引用传递,age是值传递?可是String的传递方式又是什么呢?它的表现和age相同,但本身确是引用对象,这该如何解释呢?那StringBuilder呢?
答:
Java和C++不同,C++中有引用传递的函数调用方式,而Java中只有值传递。
针对于上面程序的应用场景,在调用change()方法的时候,user、name、age三个变量都是值传递。
其中,user对象是将引用拷贝了一份,引用是对象的地址,change()中对user的修改,并没有影响到这个地址,而是修改了对象属性。产生混淆的关键在于人们看到对象本身被函数修改了,就错误的认为这是引用传递。但我们区分值传递还是引用传递的关键在于实参是否被函数所修改,对于user对象来说地址才是实参!但如果你在change()方法中修改user引用的地址,即新创建一个新的user对象的话,就会看到main方法中的user并没有任何改变,也就反向印证了它实际上是值传递。
name变量自然也是将name引用拷贝一份传递给change()方法,根据值传递的定义,函数对这个副本的修改不会影响到实际参数,又因为String的final特性,name = "Tom"; 实际上就是修改了name的地址,因此,实际参数不会受函数修改的影响。
age其本身也是将数值拷贝一份传入change(),所以任何修改都不会影响到实参。所以我们说,在Java中只有值传递这一种参数传递方式。
(这里有关StringBuilder就留给你来思考了!)
上面这段代码当输出结果为何时,才可以认定是引用传递呢?