Bootstrap

Java中的按值传递和引用传递


在 Java 中,按值传递和按引用传递是两种常见的参数传递方式。为了深入理解这两种传递方式,我们需要从 Java 如何处理方法参数传递来入手。其实,Java 中所有的参数传递都是 按值传递,但是需要注意的是,对于对象参数,Java 按值传递的是引用的副本。

一、按值传递(Pass by Value)

在 Java 中,所有方法参数的传递方式实际上是 按值传递。这意味着当我们将一个变量传递给方法时,方法接收的是该变量的副本(值),而不是变量本身。

1.1 对于基本数据类型(如 int, double, boolean 等)

  • 传递的值是基本数据类型的副本。
  • 修改方法内部副本的值不会影响原始变量。
示例:基本数据类型按值传递
public class ValuePassing {
    public static void main(String[] args) {
        int a = 5;
        System.out.println("Before method call: a = " + a);  // 输出: a = 5
        modifyValue(a);
        System.out.println("After method call: a = " + a);   // 输出: a = 5
    }

    public static void modifyValue(int num) {
        num = 10;  // 修改的是 num 的副本,不会影响原始 a
    }
}

当我们将 a 传递给 modifyValue 方法时,方法接收的是 a 的副本。修改 num(副本)不会影响原始变量 a,所以 a 的值保持不变。

1.2 对于引用数据类型(如对象)

  • 对于对象类型,传递的是对象引用的副本。换句话说,传递的是指向对象内存地址的引用副本,而不是对象本身。
  • 修改对象的内容(通过引用副本)会影响原始对象,但如果改变引用的指向,则不会影响原始引用。
示例:引用数据类型按值传递
public class ReferencePassing {
    public static void main(String[] args) {
        Person p = new Person("John", 30);
        System.out.println("Before method call: " + p.getName());  // 输出: John
        modifyName(p);
        System.out.println("After method call: " + p.getName());   // 输出: Mike
    }

    public static void modifyName(Person person) {
        person.setName("Mike");  // 修改对象的属性,影响原始对象
    }
}

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

二、按引用传递(Pass by Reference)

  • 按引用传递是指方法接受的是对象的引用本身,因此方法可以直接修改原始对象的值。值得注意的是,Java 并不支持按引用传递
  • 在 Java 中,对于对象类型,实际上还是 按值传递,传递的是对象引用的副本,而不是引用本身。因此,我们不能在方法内改变引用的指向(例如指向另一个对象),但可以改变引用指向对象的内容。

三、示例

public class Main {
    public static void main(String[] args) {
        int a = 3;
        int b = 4;
        String zhang = "zhangsan";
        String li = "lisi";
        
        Peoplek i = new Peoplek();
        i.name = "i";  
        Peoplek j = new Peoplek();
        j.name = "j"; 
        
        test1(a, b);  
        System.out.println(a + " " + b); 

        test1(zhang, li);  
        System.out.println(zhang + " " + li);  
        
        test1(i, j);  
        System.out.println(i.name + " " + j.name); 
    }

    // 重载的 test1 方法,处理基本类型的传递(按值传递)
    private static void test1(int a, int b) {
        int c = a;
        a = b;
        b = c;
    }

    // 重载的 test1 方法,处理字符串的传递(按值传递)
    private static void test1(String zhang, String li) {
        String a = zhang;
        zhang = li;
        li = a;
    }

    // 重载的 test1 方法,处理 Peoplek 对象的传递(传递引用)
    private static void test1(Peoplek i, Peoplek j) {
        String a = i.getName();
        i.name = j.getName();
        j.name = a;
    }

    static class Peoplek {
        String name;
        
        public String getName() {
            return name;
        }
    }
}

  • ab 的值在 test1(a, b) 中没有改变,因为是按值传递。

  • zhangli 的值在 test1(zhang, li) 中没有改变,因为是按值传递(字符串是不可变的)。

  • i.namej.name 的值被互换,因为是传递的对象引用,修改的是对象的内容。

输出:

在这里插入图片描述

四、总结

  • 按值传递:方法接收到的是传入参数的副本,无论是基本类型的副本还是引用类型的副本。对于基本类型,副本的修改不会影响原始值;对于引用类型,副本的修改会影响原始对象的状态,但无法改变原始引用指向的对象。
  • 按引用传递(实际上 Java 不支持): 方法接收的是传入对象的引用本身,可以直接修改原始对象的内容,但不能改变引用本身。
    在这里插入图片描述
;