前言
今天上班后摸鱼知乎的时候,突然发现有人提了一个问题,下面贴图:
看到这个问题,我也反思了下,似乎我确实只是知道答案的,但是似乎并没有很有利的证据来证明我的观点,因此,想了想还是开个博客,从更深层次剖析来验证我的观点(ps: 观点有误望勿喷,可以一起学习交流)
一、Java到底是值传递还是引用传递?
我们先来看下这两者的简单的定义:
值传递(pass by value): 指在调用函数时将实际的参数复制一份到函数中,这样在函数中对参数进行修改,不会影响实际参数。
引用传递(pass by value):指调用函数时将实际的参数地址传递到函数中,那么函数对参数进行的修改将会影响实际的参数。
最后总结下两者的区别:
值传递
代码如下:
public static void main(String[] args) {
int num = 0 ;
String str = "hello";
fun(num);
fun(str);
System.out.println(num);
System.out.println(str);
}
/**
* @param str
*/
private static void fun(String str) {;
str = "world";
}
/**
* @param num
*/
private static void fun(int num) {
num = 10 ;
}
//最后输出:
//0
//hello
从上面可以看出:函数并未改变任何原始值,由此得出结论,Java是值传递啊!!!
引用传递
看了上面的证据,可能另外一派并不认同,马上扔出下面的证据,代码如下:
public static void main(String[] args) {
int num = 0 ;
String str = "hello";
User user = new User();
user.setName("oldtest");
user.setAge(20);
fun(num);
fun(str);
fun(user);
System.out.println(num);
System.out.println(str);
System.out.println("user name="+user.getName()+",user age="+user.getAge());
}
/**
* @param user
*/
private static void fun(User user) {
user.setName("newtest");
user.setAge(18);
}
/**
* @param str
*/
private static void fun(String str) {;
str = "world";
}
/**
* @param num
*/
private static void fun(int num) {
num = 10 ;
}
public class User{
private String name ;
private Integer age ;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
//最后输出:
//0
//hello
//user name=newtest,user age=18
上面的user对象可以看出被函数修改了,由此可以得出:Java是引用传递方式
基本类型值传递,引用类型引用传递
上面的两种证据综合起来看,引出第三种结论:当参数是基本类型时,是值传递,当是引用传递时是引用传递,但是String的情况无法自圆其说吧?
值传递(对象内容引用传递)
其派认为:Java传递方式实质还是值传递,代码如下:
public static void main(String[] args) {
int num = 0 ;
String str = "hello";
User user = new TaskServiceImpl().new User();
user.setName("oldtest");
user.setAge(20);
fun(num);
fun(str);
fun(user);
System.out.println(num);
System.out.println(str);
System.out.println("user name="+user.getName()+",user age="+user.getAge());
}
/**
* @param user
*/
private static void fun(User user) {
user = new User();
user.setName("newtest");
user.setAge(18);
}
/**
* @param str
*/
private static void fun(String str) {;
str = "world";
}
/**
* @param num
*/
private static void fun(int num) {
num = 10 ;
}
public class User{
private String name ;
private Integer age ;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
//输出结果:
//0
//hello
//user name=oldtest,user age=20
从上面的输出结果可以看出,假如对象是引用传递,那么user对象预期应该会被修改,但是实际User对象并未被修改,为何会出现这种情况呢?
实际上通过上面的概念我们很清楚,这里实际是把实参引用地址复制了一份给形参,所以上面的参数是值传递,把实参对象引用的地址当作值传递给了形参。
要区别是值传递还是引用传递,看的应该是实参是否被复制了一份给到形参,而不是看对象的内容是否修改;
那么怎么来解释String呢?其实你在函数中修改str的值时,str = “world”;此语句等价与new String(“world”); 随后改变的是形参的内容,并未影响到实际原始对象的内容。
二、图解
1.String图解
2.对象图解
总结
最后总结下,Java的参数传递方式实质还是值传递,不过对于对象来说,传递的是引用地址,形参持有的是实际参数的一份地址copy,通过此地址可以修改原始对象数据,但是对象内容的修改与是否引用传递是否无关,要区分是否引用传递,其根本要看是否形参是实参的一份拷贝。