Java只有值传递,没有引用传递!
结论:Java中的参数传递,只有值传递,没有引用传递!
以下均为错误理解:
- 值传递和引用传递,区别在于传递的内容。如果是个值,就是值传递;如果是个引用,就是引用传递
- Java是引用传递
- 传递参数如果是普通类型,就是值传递,如果是对象,就是引用传递
1.基本概念
1.1.基本类型与引用类型
int num = 20;
String str = "hello";
- num是基本类型,值直接保存在变量中
- str是引用类型,变量中保存的是实际对象的地址,称这种变量为“引用”,引用指向实际对象,实际对象内容保存在堆中
1.2.赋值运算符“=”
基于上述定义变量:
num = 20;
str = "java";
- 基本类型num,赋值运算符直接改变变量值,原本值被覆盖掉
- 引用类型str,赋值运算符改变引用中保存的地址,原本的地址被覆盖(但原本对象不会被改变)
上述“hello”字符串对象未被改变(未被任何引用指向的对象会被GC回收掉)
2.值传递与引用传递
当调用一个有参函数时,会将实际参数传递给形参。在程序语言中,传递过程中传递有两种情况,即值传递和引用传递
- 值传递:在调用函数时将实际参数复制一份传递到函数中,这样在函数对形参进行修改,将不会影响到原参
- 引用传递:在调用函数时将实参的地址直接传递到函数中,在函数中对参数进行修改,会影响到原参
2.1.传递基本类型,不修改原值
public class Test {
public static void main(String[] args) {
Test test = new Test();
int i = 10;
test.pass(10);
System.out.println("main:"+i); // 10
}
public void pass(int j) {
// 修改参数j的值
j = 20;
System.out.println("pass:" + j); // 20
}
}
在pass方法中,修改了参数j的值。从输入结果可知,pass方法内部对j的值修改并未改变实际参数i的值
2.2.传递引用类型,会修改原对象值
public class Test {
public static void main(String[] args) {
Test test = new Test();
User user = new User("Mr.Q", "man");
// 修改属性值
test.pass(user);
System.out.println("main:" + user);
}
public void pass(User user) {
// 修改name的值
user.setName("Tom");
System.out.println("pass:" + user);
}
}
class User {
private String name;
private String sex;
public User(String name, String sex) {
this.name = name;
this.sex = sex;
}
public void setName(String name) {
this.name = name;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
可见,实参的值被改变了!所以很多人得出结论:“Java方法中,传递普通类型时就是值传递,传递对象类型时就是引用传递。”但是这种表述是错误的!
在参数传递过程中,实际参数的地址0x666被拷贝给形参,这个过程就是值传递(引用地址),只是传递的值的内容是对象的引用
那为什么改了user中的属性值,会对原本的user产生影响呢?
Java中对象的传递,是通过复制的方式把引用关系传递。如果没有改引用关系,而是找到引用的地址,将里面的内容改了,会对调用方产生影响的,因为大家指向的是同一个共享对象(即)!
当在pass方法中,重新new一个对象,并改变其值,这样就不会对原参数产生影响了
public void pass(User user) {
// 重新创建对象
user = new User();
// 修改name的值
user.setName("Tom");
System.out.println("pass:" + user);
}
这样,堆中就有两个对象地址。此处可知,肯定不是引用传递,如果是引用传递的话,根据上面所讲“=”赋值,在user = new User()时,实参的引用也应该指向0x999,但实际上并没有
2.3.传递引用类型,不修改原对象值
public class Test {
public static void main(String[] args) {
Test test = new Test();
String name = "Mr.Q";
// 修改属性值
test.pass(name);
System.out.println("main:" + name); // Mr.Q
}
public void pass(String name) {
// 修改name的值
name = "Tom";
System.out.println("pass:" + name); //Tom
}
}
String是引用类型,new String("Mr.Q")
在堆上创建了对象,name指向了“Mr.Q”的引用。按照引用传递的说法,原参name的值应该会被修改为“Tom”,但实际并没有。
原因是:传递的地址值发生了改变!
String类型对于常量字符串的创建,判断对象在堆中不存在的话,就会创建一个新的,如果创建新对象,那么引用地址都会变
public static void main(String[] args) {
String a = "hello";
String b = a;
b = "你好";
System.out.println("a:" + a + "、b:" + b); // a:hello、b:你好
}
- String a = “hello”:在String池中创建一个常量“hello”,给a分配一个栈内存,存储常量“hello”的地址
- String b = a:给b分配一个栈内存,存储常量“hello”的地址,相当于把a自己存储的地址,复制给了b
- b = “你好”:在String池中检查是否有“你好”的常量,没有的话,就在堆内存中创建“你好”常量,并将b地址指向“你好”;有的话,就将b的地址直接指向“你好”的地址
2.4.传递对象,证明Java中没有引用传递
public class Test {
public static void main(String[] args) {
Student s1 = new Student("张");
Student s2 = new Student("李");
// 交换
Test.swap(s1,s2);
System.out.println("s1:"+s1.getName());
System.out.println("s2:"+s2.getName());
}
public static void swap(Student x, Student y) {
// 中间变量
Student temp = x;
x = y;
y =temp;
System.out.println("x:"+x.getName());
System.out.println("y:"+y.getName());
}
}
class Student {
private String name;
public Student(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
swap方法并未改变变量s1、s2中的对象引用,swap方法的参数x和y被初始化为两个对象引用的拷贝,该方法交换的是这两个拷贝,所以并未改变原值
3.Java中的值传递
值传递和引用传递最大的区别就是:直接传递的还是传递的是副本
值传递和引用传递区别并不是传递的内容,而是实参到底有没被复制一份给形参。在Java中,还是值传递,只是对于对象参数,值的内容是对象的引用地址!
- 传递的值在栈中(基本数据类型),直接拷贝一份值传递,改变的形参不会对实参产生影响
- 传递的值在栈中存放的是地址(引用类型),先根据栈中地址找到堆中的值,然后将地址拷贝一份(拷贝的地址也是一个值),此时形参和实参指向堆中的同一个地址,形参的修改导致堆中的对象修改,从而影响到实参的改变
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!