何为传值&引用
在搞懂传值和引用之间的区别前,我们不妨先明白他俩到底是啥
- 传值
call by value
按值传递,表示方法接收的是调用者提供的值 - 传引用
call by reference
按引用传递,表示方法接收的是调用者提供的变量地址
如果你有C或C++的基础,好像还挺简单,心想:这有啥不明白的
可是,Java中可没有 &
标识符来帮你区分传值和引用
所以,先别急,慢慢看下去
传谁的值,引谁的用
不论是传值还是引用,总得有个操作的目标吧,这样的目标在Java中分为两派:
【1】基本数据类型(int, char,boolean之类)
【2】引用数据类型(类,接口,数组之类)
因此,我们貌似可以将其排列组合分为4类
- 基本数据类型+传值
基本数据类型+引用- 引用数据类型+传值
- 引用数据类型+引用
然而,情况2是根本不存在的,在Java中,基本数据类型的调用都是通过拷贝的方式实现的,所谓拷贝就是把其值赋值一份。
基本数据类型+传值
这是我们最常见的情况,例如
这个例子中的value其实就是x的一份值的拷贝
【例1】自增至3倍
public static void main(String[] args) {
double x = 3.14;
triple(x);
System.out.println(x); //result: 3
}
public static void triple(double value) {
value = 3 * value;
}
对于基本数据类型,我们可以很轻松的判断其传入类型,毕竟它只有传值一种(手动滑稽🐱👓),然而,引用数据类型的区分就很麻烦了
引用数据类型的传值和引用
先看一个例子
public static void main(String[] args) {
String x = "abc";
String y = "def";
swap(x, y);
System.out.println("x: " + x + "\ty: " + y); //猜猜是多少
}
public static void swap(String a, String b) {
String temp = a;
a = b;
b = temp;
}
所以,最终输出的x,y值到底是多少呢
x=“abc”, y=“def”
swap不能改变x,y的状态
这里你可能会想,说来说去,这不还是传值吗?
我们不妨再看一个例子
【例2】增加员工薪资至三倍
public class Test {
public static void main(String[] args) {
Staff s = new Staff();
s.setSalary(10000);
tripleSalary(s);
System.out.println(s.getSalary()); //result: 30000
}
public static void tripleSalary(Staff s){
s.setSalary(3*s.getSalary());
}
}
class Staff{
private double salary;
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
这个例子中,Staff对象被传入tripleSalary方法中,我们先不管其是按值传递还是按引用传递,其结果是,s员工的薪资从10000被修改为了30000;
这时,你的心中会多出来一些想法:
你的内心:哦,我明白了,JAVA对基本数据类型采用的是传值,对引用数据类型采用的是引用
我可以先告诉你,这种想法是错误❌的,或者说不具有普遍性
举个栗子你就明白了,还是刚才的Staff类,
【例3】我们让两个员工交换信息
public static void main(String[] args) {
Staff x = new Staff();
x.setSalary(10000);
Staff y = new Staff();
y.setSalary(20000);
swapStaff(x,y);
System.out.println(x); //Staff{salary=10000.0}
System.out.println(y); //Staff{salary=20000.0}
}
public static void swapStaff(Staff a, Staff b) {
Staff temp = a;
a = b;
b = temp;
}
很遗憾,员工x, y并没有成功交换信息,我想看到这里的你已经抓狂了,不用担心,我们不妨重新捋一遍
【1】基本数据类型采用传值,这点毋庸置疑
【2】引用数据类型到底是传值还是引用???
关于这点,你可能是受C++影响太深,C++中,有着&
符号的加持,传值和引用都是比较绝对的概念,不会出现例2和例3中的矛盾
那么,事情的真相究竟是??
【Java Core】实现一个改变对象的参数的状态的方法是完全可以的,实际上也非常常见。理由很简单,方法得到的是对象引用的副本,原来的对象引用和这个副本都引用自同一个对象。
这是Java核心技术卷一书中的解释,加粗部分需要自己好好理解。
其实,说的就是Java对于对象的引用或者说对引用数据类型的引用是通过一个叫做引用副本的技术实现的,要想理解引用副本得先知道Java是如何存储数据的。
Staff x = new Staff();
x.setSalary(10000);
x这个变量中存储的就是一个地址,指向了一个Staff类的对象
在引用副本中,其实就是多了一个指向原来这个对象的变量名(副本)
因此,例3中只是在进行两个副本之间的交换,我们不妨打印出来看看
解决了例3,例2中的问题又该如何解释呢??
这里,我们要引入一个知识点:
- 对于没有提供修改自身的成员方法引用类型,不能通过引用改变
- 对于提供修改自身的成员方法引用类型,可以通过引用改变自身参数状态
对于String
类,它的对象是一个不可变对象(用final
修饰)
而对于我们编写的Staff
类,它提供了改变自身参数的方法setSalary
好了,关于Java中的传值和传引用我的理解都在这里了,个人为Java初学者,有不足的请在评论区多多指教。
最后,用JavaCore书中的话作为收尾
- 方法不能修改基本数据类型的参数
(即【例1】) - 方法可以改变对象参数的状态、
(即【例2】) - 方法不能让一个对象参数引用一个新的对象
(即【例3】)