Java:按引用传递还是按值传递?全面解析

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:关于“按引用”与“按值”的永恒争论



如果你曾经用 Java 编程,并将数组或对象传递给方法,然后惊讶地发现原始数据发生了变化——你并不孤单。这个问题从 Java 语言诞生之初就一直困扰着新手(甚至不仅仅是新手)。在 Stack Overflow 上,它获得了数百万次浏览,而评论区的争论多年来从未平息。



今天,我们将一劳永逸地弄清楚:Java 是“按引用传递”还是“按值传递”? 剧透:答案很明确,但魔鬼总是藏在细节中。我们不仅会给出干巴巴的定义,还会通过实际示例展示 Java 中参数传递的工作原理,以及为什么许多人会感到困惑。



Java 的创造者怎么说?



官方文档以及 Java 的创造者 James Gosling 本人没有留下任何争论的余地:Java 始终按值传递参数。没有任何例外。无论是基本类型还是对象。



但为什么代码会改变对象?为什么传递给方法的 List 或数组会被修改?让我们来一探究竟。



按值传递:这到底意味着什么?



首先,给出一个明确的定义。按值传递 意味着在调用方法时,会创建变量值的副本,并将这个副本传递给方法。原始变量(在调用代码中)保持不变。



按引用传递 是指将原始变量的引用传递给方法,方法可以直接更改其值(例如,替换对象)。Java 中没有这种情况。



现在,来看示例。



示例 1:基本类型(int, double, boolean)



对于基本类型,一切都很明显。让我们编写一个简单的代码:



public class Main {    public static void main(String[] args) {        int x = 10;        changePrimitive(x);        System.out.println("After method x = " + x); // 10    }

public static void changePrimitive(int value) { value = 20; // 修改副本 }}


输出:

After method x = 10


一切都很合理:我们传递了数字 10 的副本,在方法内部将副本改为 20,但原始值保持不变。这是纯粹的按值传递。



示例 2:对象(字符串、列表、自定义类)



现在是最有趣的部分。请看:



public class Main {    public static void main(String[] args) {        StringBuilder sb = new StringBuilder("Hello");        changeStringBuilder(sb);        System.out.println("After method: " + sb); // Hello World!    }

public static void changeStringBuilder(StringBuilder builder) { builder.append(" World!"); // 修改对象的内容 }}


输出:

After method: Hello World!


“啊哈!”你可能会说,“对象被修改了!所以这是按引用传递!”



不。 这是 90% 的新手都会掉入的陷阱。让我们分析一下实际发生了什么。



变量 sb 中存储了什么?



在 Java 中,对象类型的变量(例如 StringBuilder、List、String)并不存储对象本身。它存储的是指向内存中对象的引用(地址)。对象本身位于堆(heap)中的某处。



当你将 sb 传递给方法时,你传递的是这个引用的副本。也就是地址的副本。原始引用和副本都指向同一个对象。因此,当你通过引用副本修改对象的内容时,这些修改也会通过原始引用可见。



但是!如果你尝试在方法内部将一个新对象赋值给参数,原始引用不会改变。我们来证明这一点:



public class Main {    public static void main(String[] args) {        StringBuilder sb = new StringBuilder("Hello");        changeReference(sb);        S                

博客

书籍推荐