值传递与引用传递的理解一直不深,查看了很多的博客总结了自己的理解。如有错误,可以指出改正。
在了解引用传递之前,我们首先了解一下 什么是引用,其作用到底是什么?
1 引用是什么?
可以说str这个变量是实例对象地址的一个别名
2 基本类型和引用类型
Java中数据类型分为基本类型的引用类型两大类
基本类型: byte、short、int、long、float、double、boolean、char
引用类型: 类、接口、数组
基本类型的变量在声明时就会分配数据空间
而引用类型在声明时只是给变量分配了引用空间,并不分配数据空间
3 值传递和引用传递
- 方法调用时,实际参数把它的值传递给方法的形参,形参接收的只是原始值的一个副本,后续方法里对形参的修改不会影响原来的实参的值
- 在方法的执行过程中,形参和实参的内容相同,指向同一块内存地址,也就是说操作的其实都是源数据,所以方法的执行将会影响到实际对象。
注意: 在JAVA中所有的传递其实都是值传递,只不过区分于理解,加了引用传递而已。(其实在引用传递的时候,是把实参的地址传递给了形参而已)
4 实际理解
4.1 值传递
public static void main(String[] args) {
int num1 = 10;
int num2 = 20;
swap(num1, num2);
System.out.println("num1 = " + num1);
System.out.println("num2 = " + num2);
}
public static void swap(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的副本,副本的内容无论怎么修改,都不会影响到原件本身。
4.2 引用传递
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
change(arr);
System.out.println(arr[0]);
}
//将数组的第一个元素变为0
public static void change(int[] array) {
array[0] = 0;
}
输出结果:
0
解析:
假设arr指向的内存地址是0x11
实参arr黑色,形参array红色。
调用change()的时候,形参array接收的是arr地址值的副本,此时的array也指向了0x11。
在change方法中对0x11地址里面的值进行了修改,导致0x11地址中存储的值变为了0。
所以输出为arr[0]输出结果为0
在看一段代码,我们都知道String和基本类型的包装类都是不可变类。
- 什么是不可变类?
就是该类被final修饰,创建了这个类的实例之后,就不容许改变他的值了。
看一下这些的源码是否被final修饰。
在看如下的代码解析过程。
public static void main(String[] args) {
String str = "Hello";
change(str);
System.out.println(str);
}
public static void change(String s) {
s = "wolrd";
}
输出结果:
Hello
发现是不是和我们理解的,有点有出入?
在调用change方法的时候,str把他的地址副本拷贝了一份给了s。
此时的str和s都指向了一个字符串常量"hello"
那么为什么s = "wolrd"的操作 并没有影响到str呢?
String的API中有这么一句话:“their values cannot be changed after they are created”,
意思是:String的值在创建之后不能被更改,因为String是不可变类 被final修饰的。
API中还有一段:
String str = “abc”;
等效于:
char data[] = {‘a’, ‘b’, ‘c’};
String str = new String(data);
也就是说:对String对象str的任何修改 等同于 重新创建一个对象,并将新的地址值赋值给str。
实际上,可以转换成一种理解。
public static void main(String[] args) {
String str = "Hello";
change(str1);
System.out.println(str1);
}
public static void change(String s) {
char data[] = {'w', 'o', 'r','l','d'};
String str = new String(data);
s = str;
}
然后引用指向的地址是。
class Person {
String name;
public Person(String name) {
this.name = name;
}
}
public class Test {
public static void main(String[] args) {
Person p = new Person("张三");
change(p);
System.out.println(p.name);
}
public static void change(Person p1) {
Person person = new Person("李四");
p1 = person;
}
}
输出结果:
张三
那么为什么输出结果是张三呢?看图。
在调用change方法的时候 p和p1指向通一个内存地址。
在change方法里面执行如下内容之后,p1的内存地址改变了。
Person person = new Person(“李四”);
最后的总结内容是:
值传递的时候,将实参的值,copy一份给形参。
引用传递的时候,将实参的地址值,copy一份给形参。
也就是说,不管是值传递还是引用传递,形参拿到的仅仅是实参的副本,而不是实参本身。