目录
一、为什么Java中只有值传递
1、有此疑惑的原因
出现这个疑惑的最大原因是未能真正理解值传递和引用传递的含义;
2、首先搞清楚:什么是形参和实参
形参:指的是函数定义时使用的参数;
实参:指的是调用函数时,传递给函数的参数;
package com.zibo.java.february.first;
// 说明什么是形参和实参
public class MyParameter {
public static void main(String[] args) {
int num = 10;
doSth(num); // 这里的num是实参
}
private static void doSth(int num) {
num = 100; // 这里的num是形参
}
}
3、其次弄明白:什么是值传递和引用传递
值传递:指的是在调用函数时,将实参拷贝一份赋值给函数的形参,对形参进行操作;
引用传递:指的是在函数调用时,将实参传递给函数,直接对实参进行操作;
天大的误解:传递的是基本数据类型就是值传递,传递的是引用数据类型就是引用传递!
伪代码解释:
package com.zibo.java.february.first;
// 演示值传递和引用传递的逻辑
public class MyValue {
public static void main(String[] args) {
// 值传递:指的是在调用函数时,将实参拷贝一份赋值给函数的形参,对形参进行操作;
// java只有值传递,java就是这么个逻辑,实参和形参是相互独立的;
// 你传进来谁,我就将谁复制一份进行操作,不对原变量进行改变
// 基本数据类型
int x = 1; // 原始变量,原本的实参
// 此处省略函数调用
int y = x; // 新变量,形参,此时y的值就是1
y = 100; // 形参怎么改变,实参不会收到任何影响
// 引用数据类型
Student a = new Student(); // 原始变量,原本的实参,a的值是new Student()对象的引用地址
// 此处省略函数调用
Student b = a; // 新变量,形参,此时b的值并不是前面的new Student()的实际对象,而是引用地址
// 因为变量a本来存储的就是引用地址,赋值给a,所赋值的肯定也是引用地址,a和b指向同一个对象;
b.setName("小明"); // 此时,a和b同时指向的new Student()对象的name会发生改变
b = new Student(); // 此时a和b指向的就不是一个对象了,对b进行任何操作,a所指向的对象都不会改变
// 引用传递:指的是在函数调用时,将实参传递给函数,直接对实参进行操作;
// 在引用传递中大致逻辑是这样的,没有实参和形参的区分,你传进来谁,我就对谁进行操作
int x = 1; // 原始变量
// 此处省略函数调用
int y = x; // 这个y就完全等于x,对y的任何操作,就等于对x的任何操作;
// 对于引用数据类型,也是一样的,你传过来的变量就是函数所操作的变量;
/*
* 区别:
* 1、值传递:锁定变量的值,对变量的值进行操作,无论是基本数据类型的数值,还是引用数据类型的引用地址;
* 2、引用传递:锁定变量本身,对变量进行直接操作,相当于顺序执行的代码;
*/
}
}
4、3个Java中值传递的例子
基本数据类型:
代码:
package com.zibo.java.february.first;
public class MyBaseData {
public static void main(String[] args) {
int x = 10; // x是实参,x的值是10
System.out.println("函数执行之前x的值:" + x);
change(x);
System.out.println("函数执行之后x的值:" + x);
}
private static void change(int a) {
System.out.println("函数接收到的形参a的值:" + a);
a = 100;
System.out.println("形参被更改之后的a的值:" + a);
}
}
执行结果:
函数执行之前x的值:10
函数接收到的形参a的值:10
形参被更改之后的a的值:100
函数执行之后x的值:10
说明 :
这里,我们可以清晰地看出x的值在函数执行前后没有发生改变,函数对形参的任何操作,不影响实参;
解析:
引用数据类型:对象
代码:
package com.zibo.java.february.first;
public class MyObj {
public static void main(String[] args) {
Student student = new Student("訾博");
System.out.println("函数执行前的student:");
System.out.println(student);
change(student);
System.out.println("函数执行后的student:");
System.out.println(student);
}
private static void change(Student s) {
s.setName("大哥");
}
}
class Student{
private String name;
public Student() {
}
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}
执行结果:
函数执行前的student:
Student{name='訾博'}
函数执行后的student:
Student{name='大哥'}
说明:
有人问了,这就有意思了呀!你不是说值传递不改变实参吗,这不是改变了?!
这是最容易产生误解的地方了,值传递确实没有改变原始变量student,但是原始变量student存储的是new Student("訾博")对象的引用地址,当调用change()函数的时候相当于将引用地址赋值给了形参,也就意味着两个变量同时指向一个对象,change()函数对new Student("訾博")对象的改变同样会显现在实参变量上,见图解;
图解:
在不重写toString()函数的情况下输出变量student的值,我们可以看到两个相同的值:
函数执行前的student:
com.zibo.java.february.first.Student@4554617c
函数执行后的student:
com.zibo.java.february.first.Student@4554617c
引用数据类型:字符串
代码:
package com.zibo.java.february.first;
public class MyStr {
public static void main(String[] args) {
String name = "訾博";
System.out.println("函数执行前的name:");
System.out.println(name);
change(name);
System.out.println("函数执行后的name:");
System.out.println(name);
}
private static void change(String n) {
n = "大哥";
}
}
执行结果:
函数执行前的name:
訾博
函数执行后的name:
訾博
说明:
这咋没变?因为Java中字符串是一个常量,每次修改字符串并不是修改了原来的值,而是创建一个新的字符串并将原来的变量指向新的字符串引用;
图解:
5、总结
Java方法传参,都是对所传变量进行拷贝,对基本数据类型来讲,拷贝的是实际数值,对引用数据类型来讲拷贝的是引用地址;
Java中不存在函数对实参的操作,全部是对经过拷贝的形参的操作,也就是说Java中只存在值传递,不存在引用传递。
6、补充:深拷贝与浅拷贝
深拷贝指的是拷贝对象本身,浅拷贝指的是拷贝对象的引用地址,java的方法传参是一种浅拷贝。