Wrayの知识库 Wrayの知识库
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
  • Java章节编写规范
  • Java基础

    • Java概述
    • Java语法
    • 面向对象编程
    • Java数组
    • String字符串
    • 异常处理
    • 基本类型与包装类
    • Object类与常用方法
    • 泛型
    • 枚举与注解
    • 反射
    • 值传递与引用
      • 1. 什么是值传递
      • 2. 基本类型参数
      • 3. 引用类型参数
      • 4. 重新赋值引用参数
      • 5. swap 经典面试题
        • 5.1 交换基本类型
        • 5.2 交换对象引用
      • 6. 修改对象字段为什么生效
      • 7. 数组参数
      • 8. String 参数
      • 9. final 参数
      • 10. 如何让方法影响外部结果
        • 10.1 返回新值
        • 10.2 修改可变对象
        • 10.3 使用包装结果对象
        • 10.4 使用 Atomic 类
      • 11. 方法调用中的对象生命周期
      • 12. 常见误区
        • 12.1 “对象参数是引用传递”
        • 12.2 “方法里修改对象,说明不是值传递”
        • 12.3 “final 对象不可变”
        • 12.4 “String 传进去改不了是因为 String 特殊传值”
      • 13. 判断题训练
      • Tips 快问快答
    • Lambda表达式与函数式接口
    • Stream API
    • Optional与空值处理
    • 日期时间API
    • Java现代语法特性
  • Java集合

  • Java并发

  • Java IO

  • JVM

目录

值传递与引用

Java 方法参数传递只有一种方式:值传递。

这句话非常重要,也非常容易被误解。很多人看到方法里能修改对象字段,就以为 Java 有引用传递。实际上 Java 传递的是“变量值的副本”。对于基本类型,这个值就是具体数值;对于引用类型,这个值是对象引用的副本。

# 1. 什么是值传递

值传递指调用方法时,把实参变量中保存的值复制一份给形参。

public void change(int value) {
    value = 20;
}

int number = 10;
change(number);
System.out.println(number); // 10

过程:

调用前:
number = 10

调用 change(number):
把 number 的值 10 复制给形参 value

方法内:
value = 20

方法结束:
number 仍然是 10

形参 value 是一个新的局部变量,修改它不会影响外部变量 number。

# 2. 基本类型参数

public static void add(int x) {
    x++;
}

public static void main(String[] args) {
    int a = 1;
    add(a);
    System.out.println(a); // 1
}

栈帧关系:

main 栈帧
┌────────┐
│ a = 1  │
└────────┘
    │ 复制值 1
    ▼
add 栈帧
┌────────┐
│ x = 1  │ -> x++ 后 x = 2
└────────┘

add 返回后,main.a 仍然是 1

# 3. 引用类型参数

引用类型变量保存的是对象引用。传参时,复制的是引用值。

public static void rename(User user) {
    user.setName("Jerry");
}

User u = new User("Tom");
rename(u);
System.out.println(u.getName()); // Jerry

过程:

调用前:
u ─────────────▶ User{name="Tom"}

调用 rename(u):
把 u 中保存的引用值复制给形参 user

方法内:
u ─────────────▶ User{name="Tom"}
user ──────────┘

执行 user.setName("Jerry"):
u ─────────────▶ User{name="Jerry"}
user ──────────┘

外部对象变了,不是因为引用本身被传递过去了,而是因为两个引用副本指向同一个对象。

# 4. 重新赋值引用参数

如果在方法中让形参指向新对象,不会影响外部引用。

public static void reset(User user) {
    user = new User("Alice");
}

User u = new User("Tom");
reset(u);
System.out.println(u.getName()); // Tom

过程:

调用前:
u ─────────────▶ User{name="Tom"}

进入方法:
u ─────────────▶ User{name="Tom"}
user ──────────┘

方法内重新赋值:
u ─────────────▶ User{name="Tom"}

user ─────────▶ User{name="Alice"}

方法结束:
user 消失,u 仍指向原对象

这说明 Java 不是引用传递。如果是引用传递,user = new User("Alice") 会让外部 u 也指向新对象。

# 5. swap 经典面试题

# 5.1 交换基本类型

public static void swap(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
}

int x = 1;
int y = 2;
swap(x, y);

System.out.println(x); // 1
System.out.println(y); // 2

方法里交换的是副本。

main: x = 1, y = 2
        │    │
        ▼    ▼
swap: a = 1, b = 2

swap 内交换后:
swap: a = 2, b = 1

main 不变:
main: x = 1, y = 2

# 5.2 交换对象引用

public static void swap(User a, User b) {
    User temp = a;
    a = b;
    b = temp;
}

User u1 = new User("A");
User u2 = new User("B");
swap(u1, u2);

System.out.println(u1.getName()); // A
System.out.println(u2.getName()); // B

过程:

调用前:
u1 ─────▶ User("A")
u2 ─────▶ User("B")

进入方法:
u1 ─────▶ User("A") ◀──── a
u2 ─────▶ User("B") ◀──── b

swap 内交换 a 和 b:
u1 ─────▶ User("A") ◀──── b
u2 ─────▶ User("B") ◀──── a

方法结束:
a、b 消失,u1、u2 不变

方法无法交换外部两个引用变量本身。

# 6. 修改对象字段为什么生效

public static void change(User user) {
    user.setName("Changed");
}

这里修改的是对象内部状态:

外部引用 u
   │
   ├──────────────┐
   ▼              ▼
同一个 User 对象  形参引用 user

两个引用副本指向同一个对象,因此通过任意一个引用修改对象字段,其他引用看到的对象状态也会变化。

# 7. 数组参数

数组也是对象。

public static void update(int[] arr) {
    arr[0] = 100;
}

int[] numbers = {1, 2, 3};
update(numbers);
System.out.println(numbers[0]); // 100

数组元素被修改,是因为形参和实参指向同一个数组对象。

但重新赋值数组形参无效:

public static void reset(int[] arr) {
    arr = new int[] {9, 9, 9};
}

int[] numbers = {1, 2, 3};
reset(numbers);
System.out.println(Arrays.toString(numbers)); // [1, 2, 3]

# 8. String 参数

String 是引用类型,但不可变。

public static void append(String text) {
    text = text + "!";
}

String s = "hello";
append(s);
System.out.println(s); // hello

过程:

s ───────▶ "hello"
text ────┘

text = text + "!"

s ───────▶ "hello"
text ───▶ "hello!"

方法中只是让形参 text 指向新字符串,不会影响外部变量 s。

如果需要返回新值,应显式返回:

public static String append(String text) {
    return text + "!";
}

s = append(s);

# 9. final 参数

final 修饰参数,表示形参变量不能重新赋值。

public void update(final User user) {
    user.setName("Tom"); // 可以
    // user = new User("Jerry"); // 编译失败
}

final 限制的是参数变量本身,不限制对象内部状态。

final user ─────▶ User 对象
  │
  └─ 不能让 user 改指向

User 对象内部字段是否能改,取决于对象自身是否可变

# 10. 如何让方法影响外部结果

# 10.1 返回新值

public static int add(int value) {
    return value + 1;
}

number = add(number);

# 10.2 修改可变对象

public static void addName(List<String> names) {
    names.add("Tom");
}

# 10.3 使用包装结果对象

public class Result {
    private int value;
}

# 10.4 使用 Atomic 类

public static void increment(AtomicInteger value) {
    value.incrementAndGet();
}

但不要为了绕过值传递而滥用可变容器。最清晰的方式通常是返回结果。

# 11. 方法调用中的对象生命周期

public User create() {
    User user = new User("Tom");
    return user;
}

user 局部变量会随着方法栈帧销毁,但对象不一定销毁。

create 栈帧
┌──────────────┐
│ user 引用     │
└──────┬───────┘
       ▼
堆内存 User 对象

return user 后:
调用方拿到引用
局部变量 user 消失
堆中对象继续存活

只要对象仍然可达,就不会被 GC 回收。

# 12. 常见误区

# 12.1 “对象参数是引用传递”

不准确。对象参数传递的是引用值的副本。

# 12.2 “方法里修改对象,说明不是值传递”

不准确。修改对象内容和修改外部变量引用是两回事。

# 12.3 “final 对象不可变”

不准确。final 引用不能改指向,对象是否可变取决于类设计。

# 12.4 “String 传进去改不了是因为 String 特殊传值”

不准确。String 也是引用值传递,只是它不可变,拼接会创建新对象。

# 13. 判断题训练

代码行为 外部变量是否变化 原因
方法内 x = 2,x 是 int 参数 不变 改的是副本
方法内 user.setName("A") 对象状态变化 同指向一个对象
方法内 user = new User() 外部引用不变 改的是形参副本
方法内 arr[0] = 1 数组元素变化 同指向一个数组
方法内 arr = new int[10] 外部数组不变 改的是形参副本
方法内 str = str + "x" 外部字符串引用不变 新字符串赋给形参

# Tips 快问快答

Q:Java 到底是值传递还是引用传递? A:只有值传递。基本类型传数值副本,引用类型传引用值副本。

Q:为什么方法里能修改对象字段? A:形参引用副本和外部引用指向同一个对象,修改的是对象内部状态。

Q:为什么方法里 user = new User() 不影响外部变量? A:这只是让形参副本指向新对象,外部引用变量没有被修改。

Q:Java 能写一个通用 swap 方法交换两个变量吗? A:不能直接交换外部两个局部变量,因为方法拿到的只是值副本。

Q:数组作为参数为什么能被修改? A:数组是对象,传入的是数组引用值副本,两个引用指向同一个数组对象。

Q:String 为什么传进方法后改不了? A:String 不可变,拼接会生成新对象,并只改变方法内形参的指向。

Q:final 参数有什么用? A:防止方法内部重新给参数变量赋值,有助于表达该引用不应改指向。

Q:想让方法改变外部值,推荐怎么做? A:优先返回新值;需要共享状态时再修改可变对象。

Q:对象作为返回值时,方法结束对象会消失吗? A:不会。局部变量消失,但返回的对象只要仍可达,就会继续存活。

Q:面试中如何一句话解释? A:Java 永远是值传递;对象参数传的是引用这个值的副本,所以能改对象内容,不能改外部引用变量本身。

上次更新: 2026/06/24, 16:28:28
反射
Lambda表达式与函数式接口

← 反射 Lambda表达式与函数式接口→

Copyright © 2023-2026 Wray | 鄂ICP备2024050235号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式