4

Java 值传递 or 引用传递? - Xi-iX

 2 years ago
source link: https://www.cnblogs.com/XiiX/p/16094857.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Java 方法传参 值传递 or 引用传递?

结论:Java采用的是值传递

先建立一些基础的概念

什么是值传递和引用传递?

  • 值传递(pass by value):是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数
  • 引用传递(pass by reference):是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数

Java的数据类型分为两类

  • 基本类型(int float等)
  • 引用类型(string, 数组等, 以及一切类对象)

如下图,展示了两者在内存中存储形式,基本类型存储的是值,而引用类型存储的是地址,该地址指向值所在的内存空间。引用类型有点类似于C语言中的指针。

image-20220402224542013

实践出真知,本文做了三个实验来论证为什么Java是值传递。

实验一:证明基本类型是值传递

public class test {

    public static void main(String[] args) {
        int a = 1000;
        changeInt(a);
        System.out.println("(main)a = " + a);
    }

    private static void changeInt(int a) {
        a = 3;
        System.out.println("(changeInt)a = " + a);
    }
}

// 运行代码会得到如下结果
// (changeInt)a = 3
// (main)a = 1000

在代码中,我们向changInt方法传入了变量a,并在方法内部中改变了a的值,但主程序中a的值并没有改变。因此基本类型是值传递。

实验二:引用类型是引用传递?

public class test {
    public static void main(String[] args) {
        Student std = new Student();
        std.name = "Nick";
        changeStd(std);
        System.out.println("(main)name = " + std.name);
    }

    private static void changeStd(Student std) {
        std.name = "Paul";
        System.out.println("(change)name = " + std.name);
    }
}

class Student {
    String name;
}

// 运行结果
// (change)name = Paul
// (main)name = Paul

在上段代码中,我们向changeStd方法传入了一个student类实例,并在方法内部中改变了学生类实例中的name字段。从运行的结果我们可以看出主程序中的学生实例的姓名也被改变。难道引用类型采用的是引用传递?当然不是,接下来继续看第三个实验。

实验三:引用类型是值传递?

public class test {
    public static void main(String[] args) {
        Student std = new Student();
        std.name = "Nick";
        changeStd(std);
        System.out.println("(main)name = " + std.name);
    }

    private static void changeStd(Student std) {
        std = new Student();
        std.name = "Paul";
        System.out.println("(change)name = " + std.name);
    }
}

class Student {
    String name;
}

// 运行结果
// (change)name = Paul
// (main)name = Nick

我们改变了方法内部的赋值,我们先重新给std创建了一个新的学生实例,并将名字修改。其结果与实验二相反,方法内部的赋值操作并未改变。难道引用类型又是值传递?

总结

要想理解三个实验的运行结果,其实原理并不复杂。

实验一:下图表示的是,两个变量的内存情况。只有可能是两个a有着不同的地址,方法内部的赋值才不会改变主程序的a值。如果两者是同一内存空间,那么方法内部的修改,必定会影响主程序的a值。

image-20220402231633717

实验二:下图表示的是方法内部还未赋值的时候,两个变量的内存情况。两个变量虽然有着不同的内存空间,但是存储都是Nick的地址,实际指向的是同一个地址空间。有了Nick的存储地址,当然可以方法内部去改变Nick的值。

image-20220402232001807
image-20220402232500029

实验三:与实验二相同,在还未创建新的实例时,两者指向的都是Nick。但是在给方法内部的std赋值之后,实际上改变了其存放的地址,将其指向了一个新的对象Paul。

image-20220402232738402

根据三个实验的结果,我们可以论证出Java采用的是值传递,只不过对于基本类型而言,传递的是一个具体的值,而对于引用类型而言传递的也是一个具体的值,只不过这个值是一个地址。而有这个地址,我们可以对地址指向的地址空间进行操作,所以会出现实验二的情况,但是如果我们对值本身进行改变赋值,两者是互不影响的。

看到一个例子说的很形象:

image-20220402233429207

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK