深入理解Java参数传递:图解“值传递”与“引用传递”的本质区别

61 阅读4分钟

大家好,我是一名CV工程师。

相信大家在面试过程中一定遇到过Java的“值传递”和“引用传递”到底有什么区别!或者说Java到底存不存在“引用传递”这一说法!估计面对这个问题大家都是模棱两可,那么接下来我就带大家分析下Java到底有没有“引用传递”!!!

首先我们要讲的关于Java参数传递的核心概念

  1. Java语言规范的官方定义:Java中只有值传递,没有引用传递。
  2. 对于对象类型,传递的是引用的拷贝(值),而非引用本身。-- 相当于在方法内创建了一个新的引用,但是新的引用和方法外引用指向了同一个对象。

当我们明确了这个核心概念之后,那么我们再来根据代码和内存模型图详细分析下引用类型的参数到底是怎么传递的,它与基础类型的参数的值传递到底有什么区别。

先用一个简单的示例看看引用类型的参数传递和基础类型的参数传递的运行效果。

public class PrimitivePassByValue {
    
    public static void main(String[] args) {
        int x = 10;
        int y = 20;
        
        System.out.println("交换前: x = " + x + ", y = " + y);
        
        // 尝试交换
        swap(x, y);
        
        System.out.println("交换后: x = " + x + ", y = " + y);
        // 输出:交换前: x = 10, y = 20
        //       交换后: x = 10, y = 20
        // 没有交换成功,因为是值传递
    }
    
    public static void swap(int a, int b) {
        int temp = a;
        a = b;
        b = temp;
        
        System.out.println("swap方法内: a = " + a + ", b = " + b);
        // 输出:swap方法内: a = 20, b = 10
    }
}
public class ObjectPassByValue {
    
    static class Person {
        String name;
        int age;
        
        Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }
    
    public static void main(String[] args) {
        Person person = new Person("张三", 25);
        
        System.out.println("调用前: " + person.name + ", " + person.age);
        
        // 传递的是person引用的拷贝
        modifyPerson(person);
        
        System.out.println("调用后: " + person.name + ", " + person.age);
        // 输出:调用前: 张三, 25
        //      调用后: 李四, 30
        
        // ❌ 误解:这证明了引用传递?
        // ✅ 真相:传递的是引用的拷贝,但通过这个拷贝可以修改原对象
    }
    
    public static void modifyPerson(Person p) {
        // p是person引用的拷贝,指向同一个对象
        p.name = "李四";  // 修改了原对象的内容
        p.age = 30;
        
        // 但p本身可以被重新赋值,不影响外部的person引用
        p = new Person("王五", 40);
        System.out.println("方法内新对象: " + p.name + ", " + p.age);
        // 输出:方法内新对象: 王五, 40
    }
}

看过这个示例之后相信大家对引用类型的参数传递肯定更加疑惑了,为了更改对象内容可以,但是重新赋值却达不到我们想要的效果。那么我用模型图来给大家做个清晰的分析。

public class ObjectPassByValue {
    
    static class Person {
        String name;
        int age;
        
        Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }
    
    public static void main(String[] args) {
        Person person = new Person("张三", 25);
        
        System.out.println("调用前: " + person.name + ", " + person.age);
        
        // 传递的是person引用的拷贝
        modifyPerson(person);
        
        System.out.println("调用后: " + person.name + ", " + person.age);
        // 输出:调用前: 张三, 25
        //      调用后: 李四, 30
        
        // ❌ 误解:这证明了引用传递?
        // ✅ 真相:传递的是引用的拷贝,但通过这个拷贝可以修改原对象
    }
    
    public static void modifyPerson(Person p) {
        // p是person引用的拷贝,指向同一个对象
        p.name = "李四";  // 修改了原对象的内容
        p.age = 30;
        
        // 但p本身可以被重新赋值,不影响外部的person引用
        p = new Person("王五", 40);
        System.out.println("方法内新对象: " + p.name + ", " + p.age);
        // 输出:方法内新对象: 王五, 40
    }
}

详细内存模型图

yuque_diagram.jpg

看了这个图之后大家是不是对Java的参数传递有了更深的理解。下面我也整理了一些关于这个知识点经常被问的面试题。

  1. 问:Java中是值传递还是引用传递?

    答:Java中只有值传递。对于基本类型,传递的是值的拷贝;对于对象类型,传递的是对象引用的拷贝。

  2. 问:为什么可以修改传入对象的内容?

    答:因为传递的是对象引用的拷贝,这个拷贝和原引用指向同一个对象,所以可以通过这个拷贝修改对象内容。

  3. 问:为什么不能修改传入的引用本身?

    答:因为传递的是引用的拷贝,在方法内重新赋值只是修改了这个拷贝的指向,不影响外部的原始引用。

  4. 问:String作为参数传递时有什么特殊?

    答:String是不可变对象,任何修改都会创建新对象。传递String参数时,如果重新赋值,只是修改了局部拷贝的指向。

  5. 问:数组作为参数时是值传递还是引用传递?

    答:数组是对象,所以传递的是数组引用的拷贝。可以修改数组元素,但不能让外部引用指向新的数组。

  6. 问:如何实现类似C++引用传递的效果?

    答:使用包装类、数组包装、Atomic类等,间接修改内容。

关注我!!!后续我会为大家带来更多的面试题以及知识讲解。