结论:Java中都是值传递,没有“引用传递”
(1)基本数据类型传值,对形参的修改不会影响实参;
(2)引用类型传引用的值,形参和实参指向同一个内存地址(同一个对象),所以对参数的修改会影响到实际的对象;
(3)String, Integer, Double等immutable的类型特殊处理,可以理解为传值,最后的操作不会修改实参对象。
下面来进行详解:
首先回顾一下在程序设计语言中有关将参数传递给方法(或函数)的一些专业术语。按值调用(call by value)表示方法接收的是调用者提供的值,而按引用调用(call by reference)表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。 它用来描述各种程序设计语言(不只是Java)中方法参数传递方式。
Java程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容。
Example1
@Test
public void testInt() {
int num1 = 10;
int num2 = 20;
swapInt(num1, num2);
System.out.println("num1 = " + num1);
System.out.println("num2 = " + num2);
}
private void swapInt(int a, int b) {
int temp = a;
a = b;
b = temp;
System.out.println("a = " + a);
System.out.println("b = " + b);
}
结果:
a = 20
b = 10
num1 = 10
num2 = 20
解析:
在swap方法中,a、b的值进行交换,并不会影响到 num1、num2。因为,a、b中的值,只是从 num1、num2 的复制过来的。也就是说,a、b相当于num1、num2 的副本,副本的内容无论怎么修改,都不会影响到原件本身。另外即使是String, Integer, Double等immutable,也会是上面的情况,请看Example2
Example2
@Test
public void testString() {
String str1 = "hello";
String str2 = "world";
swapString(str1, str2);
System.out.println("str1 = " + str1);
System.out.println("str2 = " + str2);
}
private void swapString(String a, String b) {
String temp = a;
a = b;
b = temp;
System.out.println("a = " + a);
System.out.println("b = " + b);
}
结果:
a = world
b = hello
str1 = hello
str2 = world
通过上面的例子,我们已经知道了一个方法不能修改一个基本数据类型的参数,而对象引用作为参数不一样,请看 Example3
Example3
@Test
public void testArr() {
int[] arr = { 1, 2, 3, 4, 5 };
System.out.println("改变前arr[0] = " + arr[0]);
changeArr(arr);
System.out.println("改变后arr[0] = " + arr[0]);
}
private void changeArr(int[] array) {
// 将数组的第一个元素变为0
array[0] = 666;
}
结果:
改变前arr[0] = 1
改变后arr[0] = 666
解析:
array 被初始化 arr 的拷贝也就是一个对象的引用,也就是说 array 和 arr 指向的时同一个数组对象。 因此,外部对引用对象的改变会反映到所对应的对象上。
通过 example3 我们已经看到,实现一个改变对象参数状态的方法并不是一件难事。理由很简单,方法得到的是对象引用的拷贝,对象引用及其他的拷贝同时引用同一个对象。
很多程序设计语言(特别是,C++和Pascal)提供了两种参数传递的方式:值调用和引用调用。有些程序员认为Java程序设计语言对对象采用的是引用调用,实际上,这种理解是不对的。由于这种误解具有一定的普遍性,所以下面给出一个反例来详细地阐述一下这个问题。即使传递的是一个对象的引用也不会改变原有对象的值
Example4
@Test
public void testStudent() {
Student s1 = new Student("小张");
Student s2 = new Student("小李");
swapStudent(s1, s2);
System.out.println("s1.getName() = " + s1.getName());
System.out.println("s2.getName() = " + s2.getName());
}
private void swapStudent(Student x, Student y) {
Student temp = x;
x = y;
y = temp;
System.out.println("x.getName() = " + x.getName());
System.out.println("y.getName() = " + y.getName());
}
结果:
x.getName() = 小李
y.getName() = 小张
s1.getName() = 小张
s2.getName() = 小李
解析:
交换之前:
交换之后:
通过上面两张图可以很清晰的看出: 方法并没有改变存储在变量 s1 和 s2 中的对象引用。swap方法的参数x和y被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝。但是我们可以改变引用的对象的值,扩展如下
Example5
@Test
public void testStudent2() {
Student s1 = new Student("张三");
Student s2 = new Student("李四");
swapStudent2(s1, s2);
System.out.println("s1.getName() = " + s1.getName());
System.out.println("s2.getName() = " + s2.getName());
}
private void swapStudent2(Student x, Student y) {
x.setName("张三三");
y.setName("李四四");
System.out.println("x.getName() = " + x.getName());
System.out.println("y.getName() = " + y.getName());
}
结果:
x.getName() = 张三三
y.getName() = 李四四
s1.getName() = 张三三
s2.getName() = 李四四
上面的Example5我们发现,通过对x,y的改变可以改变原有的对象的值,主要是看我们是对形参的修改还是对形参指向的对象的修改,其中的区别能感觉到么 ,如果没有感觉到。
那就再看一遍Example4,Example5。
再次总结
Java程序设计语言对对象采用的不是引用调用,实际上,对象引用是按
值传递的。
下面再总结一下Java中方法参数的使用情况:
- 一个方法不能修改一个基本数据类型的参数(即数值型或布尔型》
- 一个方法可以改变一个对象参数的状态。
- 一个方法不能让对象参数引用一个新的对象。
看这里,看这里
文章总目录:博客导航
码字不易,尊重原创,转载请注明:https://blog.csdn.net/u_ascend/article/details/82876848
参考博文:https://github.com/Snailclimb/JavaGuide/issues/12
如果涉及到侵权,请联系我:[email protected],1-2个工作日处理。
作者自己维护的公众平台,感兴趣的来,需要什么可以私聊我,网盘中有大量学习资源,后续慢慢添加。
欢迎互喷