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 + '\'' +
                '}';
    }
}

image.png
可见,实参的值被改变了!所以很多人得出结论:“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;
    }
}

image.png
swap方法并未改变变量s1、s2中的对象引用,swap方法的参数x和y被初始化为两个对象引用的拷贝,该方法交换的是这两个拷贝,所以并未改变原值

3.Java中的值传递

值传递和引用传递最大的区别就是:直接传递的还是传递的是副本

值传递和引用传递区别并不是传递的内容,而是实参到底有没被复制一份给形参。在Java中,还是值传递,只是对于对象参数,值的内容是对象的引用地址!

  • 传递的值在栈中(基本数据类型),直接拷贝一份值传递,改变的形参不会对实参产生影响
  • 传递的值在栈中存放的是地址(引用类型),先根据栈中地址找到堆中的值,然后将地址拷贝一份(拷贝的地址也是一个值),此时形参和实参指向堆中的同一个地址,形参的修改导致堆中的对象修改,从而影响到实参的改变
  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值