Java的参数传递是「按值传递」仍是「按引用传递」?

Java 编程语言中最大的困惑之一就是: java 是按值传递仍是按引用传递。我在面试中常常会问面试者这个问题,但仍是有不少面试者对这个问题的理解不是很正确。
在这里插入图片描述
有不少面试者是这样理解的:java

  • 若是传递类型为基础数据类型,则按值传递,
  • 若是传递类型为类,则按引用传递。

这样的理解正确吗?他们甚至还能够写出示例代码来验证他们的想法,让咱们来一块儿看一看大多数人是如何验证“基础类型按值传递,非基础类型按引用传递”这个想法的:程序员

基础类型数据做为参数传递面试

/**
 * 基础类型数据做为参数传递
 * @Author: danding
 * @Date: 2019/11/5
 */
public class TestParams {

    public static void main(String[] args){
        int x = 6;
        System.out.println("x的初始值为:" + x);
        add(x);
        System.out.println("x的最终值为:" + x);
    }

    public static void add(int x){
        x = x + 1;
        System.out.println("add 方法中的x值为:" + x);
    }

}

运行结果:算法

x的初始值为:6
add 方法中的x值为:7
x的最终值为:6

非基础类型做为参数传递
首先咱们定义一个类编程

/**
 * 定义一个女友的类
 * (简陋了点,只有年龄,但不影响咱们使用呀)
 * @Author: danding
 * @Date: 2019/11/5
 */
public class GrilFriend {
    private int age;

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

    public int getAge() {
        return age;
    }

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

而后咱们将建立实例并做为参数传递编程语言

/**
 * 基础类型数据做为参数传递
 * @Author: danding
 * @Date: 2019/11/5
 */
public class TestParams {

    public static void main(String[] args){
        GrilFriend gril = new GrilFriend(18);
        System.out.println("女友的初始年龄为:" + gril.getAge());
        add(gril);
        System.out.println("女友的最长年龄为:" + gril.getAge());
    }

    private static void add(GrilFriend friend){
        friend.setAge(friend.getAge()+1);
        System.out.println("女友在方法中的年龄为:" + friend.getAge());
    }

}

运行结果:this

女友的初始年龄为:18
女友在方法中的年龄为:19
女友的最长年龄为:19

非基础类型做为参数传递时,值的确被修改了。code

这个时候不少同窗经过以上两个示例验证,本身就得出了本身的结论:视频

若是传递类型为基础数据类型,则按值传递,不然为按引用传递。对象

在此说明,这个理解是错误的,错误的,错误的。下面咱们就来讲说 Java中的参数传递究竟是按值传递仍是按引用传递?

首先说下正确的答案:Java 的参数传递,不论是基本数据类型仍是引用类型的参数,都是按值传递,没有按引用传递!

首先,咱们应该了解按值传递或按引用传递的含义。

  • 按值传递:将方法参数值复制到另外一个变量,而后传递复制的对象,将其称为按值传递。
  • 按引用传递:将对实际参数的别名或引用传递给方法,将其称为按引用传递的缘由。

你个糟老头子坏得狠,我信你个鬼,你这个解释给我要给差评.....

且听老夫(哦,不,是小编)慢慢道来...

当一个对象被看成参数传递到一个方法后,在此方法内能够改变这个对象的属性,那么这里究竟是「按值传递」仍是「按引用传递」?

答:是按值传递。Java 语言的参数传递只有「按值传递」。当一个实例对象做为参数被传递到方法中时,参数的值就是该对象的引用的一个副本。指向同一个对象,对象的内容能够在被调用的方法内改变,但对象的引用(不是引用的副本) 是永远不会改变的。

基础类型参数传递

这个上面的示例已经验证了,为按值传递,这个你们应该不会有什么异议。

非基础类型参数传递

咱们重点来讲下对象类型做为参数传递

先来看一下传递的例子:

public class TestParams {

    public static void main(String[] args){
        Person p1 = new Person();
        System.out.println(p1);
        change(p1);
        System.out.println(p1);
    }

    private static void change(Person p2){
        p2 = new Person();
    }

}

class Person{

}

运行结果

Person@677327b6
Person@677327b6

能够看出两次打印person的地址值是同样的,即调用完change() 方法以后,person变量并无发生改变。

这个传递过程的示意图以下:

当执行到Person p1 = new Person();代码时,程序在堆内存中开辟了一块内存空间用来存储Person类的实例对象,同时在栈内存中开辟了一个存储单元用来存储该实例对象的引用,即上图中person指向的存储单元。

当执行到change(p1);代码时,person做为参数传递给change()方法,须要注意的是:person将本身存储单元的内容传递给了change()方法的p2变量!此后,在change()方法中对p2的一切操做都是针对p2所指向的存储单元,与person所指向的那个存储单元没有关系了!

这个时候该有同窗说了,那上面那个女友示例中,女友的年龄不是被在方法中修改了吗?若是传递的是副本那不该该修改不了女友的年龄吗?

若是咱们将女友中的代码放到内存示例图中走一遍,你应该就明白其中的道理了。
所谓引用副本,但其所指向的仍是真实的对象,因此修改的仍是真实对象上的属性。

我但愿上面的解释能消除全部疑问,只须要记住Java 的参数传递,不论是基本数据类型仍是引用类型的参数,都是按值传递,没有按引用传递!。当您将了解堆空间和栈内存以及存储不一样对象和引用的位置时,将会更加清楚,有关程序的详细说明,请阅读 Java Heap vs Stack


“不积跬步,无以致千里”,但愿将来的你能:有梦为马 随处可栖!加油,少年!

关注公众号:「Java 知己」,天天更新Java知识哦,期待你的到来!

  • 发送「Group」,与 10 万程序员一块儿进步。
  • 发送「面试」,领取BATJ面试资料、面试视频攻略。
  • 发送「玩转算法」,领取《玩转算法》系列视频教程。
  • 千万不要发送「1024」...
    每日福利