Java是值传递还是引用传递?(保证你搞懂参数传递原理!)

Java是值传递还是引用传递

关于是值传递还是引用传递,我个人觉得没有必要纠结这个问题。当调用方法传参的时候,底层的逻辑是不变的,但是不同的人对于这两个词有不同的理解,我们只需要搞懂参数传递的原理即可。

1.基本数据类型和引用数据类型

想要弄懂今天的问题,首先需要知道基本数据类型和引用数据类型,直接上图:
Java是值传递还是引用传递?(保证你搞懂参数传递原理!)_第1张图片
基本数据类型直接在栈内存,存值
引用数据类型在栈内存存的是引用,引用指向堆内存里面的真正的值

2.执行方法传参到底发生了什么
2.1方法参数为基本数据类型
public class Test02 {
    public static void change(int num){
        num = 2;
    }
    public static void main(String[] args) {
        int a = 1;
        change(a);
        System.out.println(a);  //输出1
    }
}

一段非常简单的代码,我先不说它到底输出了什么,我画一下整个过程的内存图:
Java是值传递还是引用传递?(保证你搞懂参数传递原理!)_第2张图片
main方法压栈:a变量赋值为1,调用change方法
change方法压栈:num已经将a所存的内容复制一遍给了自己
所以很明显a和num是两块内存,change方法让num变成2,和a没有任何关系,所以上面代码的输出是1

2.2方法参数是引用数据类型
public class Test02 {
    public static void change(Cat cat){
        cat = new Cat(10);  //注意看这里!!!
    }
    public static void main(String[] args) {
        Cat myCat = new Cat(5);
        change(myCat);
        System.out.println(myCat.getAge());  //输出是5!!!
    }
}
class Cat{
    private int age;

    public Cat(int age) {
        this.age = age;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

和第一段代码思路相同,只是把change方法里面的int类型参数,改成了Cat类型,我画一下整个过程的内存图:
change方法中cat = new Cat(10)语句执行之前:
Java是值传递还是引用传递?(保证你搞懂参数传递原理!)_第3张图片
change方法中cat = new Cat(10)语句执行之后:(cat的内存地址不应该是0x123了,会发生改变,这里当时做图的时候疏忽了)
Java是值传递还是引用传递?(保证你搞懂参数传递原理!)_第4张图片
显然,change方法改变的是它内部的cat,没有影响到main方法里面的myCat,所以最后输出还是5

综上所述,无论参数是基本数据类型,还是引用数据类型。参数的来源者(a,myCat)和参数的接收者(num,cat)是两个不同的东西,占了两块不同的内存。参数来源者将自己存储的东西(可能是值,也可能是内存地址)复制一份传给了参数接收者。而方法(上面的change方法)内部操作的是参数接收者

OK,读到这里可能会有小伙伴提问了,照你这么说那就是肯定不会有影响了,那下面这段代码怎么回事呢?为什么myCat受到影响了呢?

public class Test02 {
    public static void change(Cat cat){
        cat.setAge(10);  //这里做了修改
    }
    public static void main(String[] args) {
        Cat myCat = new Cat(5);
        change(myCat);
        System.out.println(myCat.getAge());  //输出变成了10!!!
    }
}
class Cat{
    private int age;

    public Cat(int age) {
        this.age = age;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

好的,我们直接上内存图:
change方法中cat.setAge(10)语句执行之前:(change执行之前内存图和上面是一样的)
Java是值传递还是引用传递?(保证你搞懂参数传递原理!)_第5张图片
change方法中cat.setAge(10)语句执行之后:(这里不一样了)
Java是值传递还是引用传递?(保证你搞懂参数传递原理!)_第6张图片
大家要仔细体会cat = new Cat(10)和 cat.setAge(10)两句代码的差别,结合上面的内存图。
cat = new Cat(10) 是直接改变了cat的值,让cat指向了堆内存中新的对象,它并没有对原来指向的对象做任何修改,而myCat还是指向原来的对象,自然不会受到任何影响。

cat.setAge(10)是通过cat这把钥匙进入到了堆内存中对象的内部,通过setAge方法改变了原来对象的属性值,cat和myCat依然指向堆中的同一个对象,但此时对象的属性已经被cat改变了,于是myCat自然收到了影响。

OK我知道肯定有的小伙伴又要问了,那String也是引用数据类型呀,为什么改变String的值,不会有影响呢?好的我们看下面这段代码:

public class Test02 {
    public static void change(String str){
        str = "world";  //这里做了修改
    }
    public static void main(String[] args) {
        String myStr = "hello";
        change(myStr);
        System.out.println(myStr);  //输出的是hello!!!
    }
}

今天就给大家讲到底!!!
看内存图:
change方法执行str = "world"之前:(myStr和str指向了同一个hello)
Java是值传递还是引用传递?(保证你搞懂参数传递原理!)_第7张图片
change方法执行str = "world"之后:
Java是值传递还是引用传递?(保证你搞懂参数传递原理!)_第8张图片
大家需要搞明白str = "world"这句代码发生了什么。其实关于new String(“world”)和 = "world"的区别还是需要单独写一篇文章的,这里就先不详细说了,之后我会给大家分享一下。OK回归正题。

String类型的值是不可以修改的,这里说的修改,是针对内存来说的,一旦你指定了一个初始值str = “hello”,那堆中的这块内存就不可以发生改变了,不能再把这块内存里面的值改成"world",所以底层的操作是新开辟了一块"world"内存,然后让str指向新的对象。

所以change方法里面的代码 str = “world"其实是让str指向了新的对象,并不是对原来的对象进行了修改,而myStr依然指向原来的对象"hello”,所以输出的结果依然是"hello"。

综上所述,参数传递的本质就是将参数来源者的内容复制一份,丢给参数接收者。假如这个内容是引用,那么就相当于它们两者都拥有了打开堆中对象大门的钥匙,假如参数接收者把钥匙丢了换了一把新钥匙(比如new操作,创建了一个新对象,并让自己指向它),那么堆中原始对象根本没有受到任何影响,参数来源者访问原始对象的属性也不会有任何改变。但是假如参数接收者通过这把钥匙,打开了堆中对象的大门,并且对里面的东西做了修改(比如上面的cat.setAge操作),那么堆中的原始对象发生了改变,此时参数来源者再去访问堆中的对象属性,也就发生了改变。

OK到这里就结束了,笔芯~
Java是值传递还是引用传递?(保证你搞懂参数传递原理!)_第9张图片

你可能感兴趣的:(Java基础,java,编程语言,python,字符串,jvm)