如果你学的第一门程序语言是 java 可能对这个传递方式没有那么敏感, 如果学了 c 或 c++, 然后再学 java, 那么可能对这个问题会感到困惑.
1. 值传递与引用传递的概念
在将传递方式之前先理解一下形参与实参.
形式参数: 是在定义函数名和函数体的时候使用的参数, 目的是用来接收调用该函数时传入的参数.
实际参数: 在调用有参函数时, 主调函数和被调函数之间有数据传递关系. 在主调函数中调用一个函数时, 函数名后面括号中的参数称为 "实际参数".
可以这么理解: 形参是实参的抽象, 实参是调用时的参数, 形参是定义函数的参数
值传递: 方法调用时, 实际参数把它的值传递给对应的形式参数, 函数接收的是原始值的一个 copy, 此时内存中存在两个相等的基本类型, 即实际参数和形式参数, 后面方法中的操作都是对形参这个值的修改, 不影响实际参数的值.
引用传递: 是指在调用函数时将实际参数的地址直接传递到函数中, 那么在函数中对参数所进行的修改, 将影响到实际参数.
划重点: 值传递和引用传递的主要区别
值传递 | 引用传递 |
创建副本,在函数体中不能改变原来的值 | 不创建副本,在在函数体中不能改变原来的值 |
创建副本的含义
创建副本也就是说, 把要调用的实参先拷贝一份出来, 然后用拷贝的那一份传进函数体内. 不创建副本时, 不会发生 copy 这个步骤.
举个值传递的栗子:
- public class Test01 {
- public static void main(String[] args) {
- int a=1,b=2;
- swap(a, b);
- System.out.println("a="+a);
- System.out.println("b="+b);
- }
- public static void swap(int a,int b) {
- int temp=a;
- a=b;
- b=temp;
- }
- }
结果:
1 a=1 2 b=2
上面栗子中, 在函数中让实现 a,b 交换, 但调用函数后, 输出的结果仍然是 a,b 原来的值, 说明函数体中的操作并不能影响 a,b 在函数体外的值.
引用传递的栗子就不测试了, 有兴趣的话可以用 c++ 来测试, 参数为定义为别名或指针时, 在 c++ 中是引用传递.
误区: 传递的参数如果是普通类型, 那就是值传递, 如果是对象, 那就是引用传递. 这是错误的!!!!!
2.java 中的值传递
在 java 中, 无论参数是基本类型, 还是引用数据类型, 都是值传递方式. 下面来举个引用数据类型的参数, 基本数据类型的栗子上面已经有了.
- public class Test01 {
- public static void swap(Student st1,Student st2) {
- Student temp=st1;
- st1=st2;
- st2=temp;
- }
- public static void main(String[] args) {
- Student st1=new Student("张三",20);
- Student st2=new Student("李四",20);
- swap(st1, st2);
- System.out.println("st1:"+st1);
- System.out.println("st2:"+st2);
- }
结果:
st1:Student [name = 张三, age=20]
st2:Student [name = 李四, age=20]
例子中, st1 和 st2 的所指向的对象并没有发生改变.
这时候, 你可能会问, 既然 java 是值传递, 那么实参会发生拷贝, 那拷贝的是什么东西呢? 答案是: 拷贝的是对象在堆中的地址. 来个栗子来验证一下:
- public class Test01 {
- public static void print(Student stu1,Student stu2) {
- Student temp=stu1;
- stu1=stu2;
- stu2=temp;
- System.out.println("在函数体中交换后打印 stu1:"+stu1);
- System.out.println("在函数体中交换后打印 stu2:"+stu2);
- }
- public static void main(String[] args) {
- Student stu1=new Student("stu1",20);
- Student stu2=new Student("stu2",30);
- print(stu1, stu2);
- }
- }
结果:
在函数体中交换后打印 stu1: Student [name=stu2, age=30]
在函数体中交换后打印 stu2: Student [name=stu1, age=20]
从结果中可以看出, 在函数体中 stu1 和 stu2 所指向的对象确实是发生了改变, 这是因为在值传递的过程中拷贝了他们在堆中的地址.
来看看他们在内存中的怎么样的:
这时候你可能会问, 既然 java 的值传递是拷贝地址, 那我能不能改变地址所只想的内容? 答案是: 当然可以
- public static void changeInf(Student stu) {
- stu.setName("我改名字了");
- }
- public static void main(String[] args) {
- Student stu=new Student("张三",18);
- changeInf(stu);
- System.out.println(stu);
- }
结果:
1 Student [name = 我改名字了, age=18]
结论: java 中只有值传递, 这可能是因为 java 没有指针和别名引用的原因吧.
来源: https://www.cnblogs.com/ironHead-cjj/p/11366888.html