Java 是值传递还是引用传递?

#Java#面试八股

Java 是值传递还是引用传递?

结论:Java 只有值传递(pass-by-value)。方法拿到的是实参的副本:基本类型复制数值本身;引用类型复制的是"引用的值"(可以理解为对象地址)。所以方法内可以修改对象的内容,但让形参重新指向另一个对象,调用方不受影响。常说的"Java 是引用传递"是对现象的误称——传的不是引用变量本身,而是引用的副本。

1. 内存里发生了什么

调用前:                        进入方法后:
  main 栈帧                       method 栈帧
  n = 10                          n' = 10              ← 数值副本
  u ──→ [User{name}]              u' ──→ [User{name}]  ← 同一个堆对象
  arr ──→ [int[]{1}]              arr' ──→ [int[]{1}]  ← 同一个数组

u' = new User(...)  只让副本指向新对象,main 里的 u 仍指向原对象

判断口诀:看方法有没有重新赋值形参。改字段/元素 → 影响调用方;重新赋值引用 → 不影响。

2. 代码:三种字段的实测

class User {
    String name;
    User(String name) { this.name = name; }
}

public class PassByValueDemo {
    static void modify(int n, User u, int[] arr) {
        n = 99;                    // 副本,无效
        u.name = "changed";        // 改同一对象的字段,有效
        u = new User("new");       // 只改副本引用,无效
        arr[0] = 99;               // 改同一数组元素,有效
    }

    /** 经典失败案例:交换两个引用 */
    static void swap(User a, User b) {
        User tmp = a;
        a = b;
        b = tmp;                   // 交换的是副本,调用方毫无感知
    }

    public static void main(String[] args) {
        int n = 1;
        User u = new User("A");
        int[] arr = {1};

        modify(n, u, arr);
        System.out.println(n);         // 1
        System.out.println(u.name);    // changed
        System.out.println(arr[0]);    // 99

        User x = new User("X"), y = new User("Y");
        swap(x, y);
        System.out.println(x.name + y.name);  // XY,没有交换
    }
}

3. 现象与本质对照

操作是否影响调用方本质
修改基本类型形参否数值副本
修改对象的字段是引用副本指向同一对象
形参重新 new 一个对象否只改了副本引用
修改数组元素 / 集合内容是引用副本指向同一容器
交换两个引用否交换的是副本
传入 null 后在方法内赋值否副本有了新地址,调用方仍是 null
传入 String 后"修改"否String 不可变,方法内只会产生新对象

4. 为什么 String 看起来像"值传递"

static void change(String s) {
    s = s + "!";
}

String s = "hi";
change(s);
System.out.println(s);   // hi,不是 hi!

String 是不可变(immutable)的,s + "!" 创建了新对象并让形参指向它,原对象没变。同理,Integer、LocalDate 等不可变类型都表现一致。

5. 常见追问

追问回答要点
那为什么说"传引用"?传的是引用的副本,共享对象但独立变量,属于值传递的一种
C++ 的 & 引用和 Java 一样吗?不一样,C++ 引用是别名,能真正交换;Java 只能改对象内容
想让方法"交换"两个变量怎么办?返回封装对象/数组,或用 AtomicReference、Map.Entry 等容器
为什么方法内改集合会生效?集合是对象,形参与实参指向同一个堆对象
基本类型包装类传参有坑吗?有,Integer 不可变,方法内 i++ 只改副本,调用方不变
传大对象要注意什么?值是引用的副本,开销小;但方法内修改会影响调用方,注意副作用与并发

小结

Java 只有值传递:基本类型传值、引用类型传"引用的值"。记住判断标准——改内容会同步,换引用不同步。理解这一点,才能解释为什么 swap 无效、为什么 String 参数"改不动"、为什么集合参数会被方法改掉。

笔记加载中…