Java中String通过引用传递问题详细解析

This is a classic question of Java. Many similar questions have been asked on stackoverflow, and there are a lot of incorrect/incomplete answers. The question is simple if you don’t think too much. But it could be very confusing, if you give more thought to it.

这是一个非常经典的问题,许多类似的问题在stackoverflow上被提问,有很多不正确或者不完整的回答。如果你不考虑那么多,直接认为string是immutable的,那问题就很简单,如果你想要了解更多细节,问题就变的很复杂。

我们看下面这个非常容易混淆,同时又很经典的代码

public static void main(String[] args) {
String x = new String("ab");
change(x);
System.out.println(x);
}
public static void change(String x) {
x = "cd";
}

我们可能以为会输出cd,实际上输出的是ab

这是一个很容易混淆的问题,我们来看一下一个貌似很合理的解释:

x stores the reference which points to the "ab" string in the heap. So when x is passed as a parameter to the change() method, it still points to the "ab" in the heap like the following: 变量x存储的是引用,这个引用指向堆上的string“ab”,如下图所示:

image.png

所以x传递给change方法的x参数,然后新建一个string“cd”,然后x有指向这个新建的cd变量

image.png

这样的解释看上去一点问题没有,但为什么输出的结果又不对呢?

真正的代码执行过程应该是这样的:

When the string "ab" is created, Java allocates the amount of memory required to store the string object. Then, the object is assigned to variable x, the variable is actually assigned a reference to the object. This reference is the address of the memory location where the object is stored. 当string变量‘ab’被创建出来的之后,java分配一块足够大小的内存去存储这个string对象,这个对象被分配给变量x,这个变量x实际上存储的是这个对象在内存中的地址。 The variable x contains a reference to the string object. x is not a reference itself! It is a variable that stores a reference(memory address). Java is pass-by-value ONLY. When x is passed to the change() method, a copy of value of x (a reference) is passed. The method change() creates another object "cd" and it has a different reference. It is the variable x that changes its reference(to "cd"), not the reference itself. 这个变量x包含一个这个string对象的一个引用。切记 ** x不是引用本身 **,x只是一个变量存储了一个引用(这个引用其实就是内存的地址)。 java只通过value传递当x被传递给change方法的时候。会将x的一份拷贝传递给change方法中的局部变量x,这是另外一个x,虽然这个x存储的引用也就是地址的值是一样的,待会就被改变了,change方法新建一个对象“cd”,是局部变量里的x指向这个新建cd,所以原本的x还是指向ab。

image.png

我们可以测试其他引用类型的传递,会发现他们实际上都是通过值传递的,会在方法里新建一个引用,当我们对这个引用指向一个新对象时就要注意了

import java.util.ArrayList;
import java.util.List;

public class StringTest {

    public static void main(String[] args) {
        String x = new String("ab");
        change(x);
        System.out.println(x);
        
        Integer y = new Integer(5);
        change(y);
        System.out.println(y);
        
        List<Integer> a = new ArrayList<Integer>();
        
        a.add(1);
        a.add(4);
        change(a);
        
        System.out.println(a);
    }
    
    public static void change(String x) {
        x = "cd";
        }
    
    public static void change(Integer y) {
        y = 4;
    }
    
    public static void change(List<Integer> a) {
        //a.add(5);
        a = new ArrayList<Integer>();
        a.add(2);
        
    }
}

输出结果

image.png

因为我们在方法中都是新建一个对象,所以局部变量的引用都改变了,无法改变原有的值,所以我们看到三个change方法都没有起到作用。

当我们向方法参数传递一个引用的时候要记住是传递的引用的值,而不是引用本身,当我们不让这个引用指向一个新对象的时候,不会出现问题,当我们在方法中将局部的引用赋给一个new出来的对象,那么我们要切记,这时候这个引用已经指向另一个对象了,它所操作的都不会反映在原有的对象上。

那么我们如何解决上面那个问题呢? 其实很简单,只要不在方法里新建一个对象就行了。保持方法中的那个局部变量的引用也在原有对象上操作

public static void main(String[] args) {
StringBuilder x = new StringBuilder("ab");
change(x);
System.out.println(x);
}

public static void change(StringBuilder x) {
x.delete(0, 2).append("cd");
}

我们总结一个关键的问题,Java中没有真正的按引用传递,所有变量都是按值value传递的,引用也是变量,只不过它的值是存的对象的地址。所以引用类型的变量在参数的传递过程中,也会新建一个局部变量,局部变量会得到和引用变量一样的值,也就是指向同一个对象。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏儿童编程

声音功能让儿童编程更有创造性

导读:Scratch中声音功能非常强大,除了常规的音效,你甚至可以模拟各种乐器的各个发音、设置节拍、休止……如果你愿意,甚至可以用它创作一个交响乐。我们可以引导...

13440
来自专栏儿童编程

儿童创造力教育与编程教育的碰撞——MIT雷斯尼克教授最新理论梗概

儿童编程教育已经在我国各一线二线城市疯狂出现,颇有“烂大街”的趋势。我们不禁要问很多很多问题:

21770
来自专栏儿童编程

天干地支五行八卦的对应关系

18790
来自专栏儿童编程

一张图理清《梅花易数》梗概

学《易经》的目的不一定是为了卜卦,但是了解卜卦绝对能够让你更好地了解易学。今天用一张思维导图对《梅花易数》的主要内容进行概括,希望能够给学友们提供帮助。

30840
来自专栏haifeiWu与他朋友们的专栏

复杂业务下向Mysql导入30万条数据代码优化的踩坑记录

从毕业到现在第一次接触到超过30万条数据导入MySQL的场景(有点low),就是在顺丰公司接入我司EMM产品时需要将AD中的员工数据导入MySQL中,因此楼主负...

26440
来自专栏华章科技

穿越十年后看互联网+:家电行业的金矿在哪里?

现在市场上炒得火热的智能家居未来出路在何方?做智能家居的创业者应该注意哪些机会?传统家电厂商又到底如何借助互联网进行转型?本文以智能空调为例,用故事的形式,提前...

8210
来自专栏儿童编程

我不是算命先生,却对占卜有了疑惑——如何论证“占卜前提”的正确与否

事出有因,我对《周易》感兴趣了很多年。只是觉得特别有趣,断断续续学习了一些皮毛。这几天又偶然接触到了《梅花易数》,觉得很是精彩,将五行八卦天干地支都串联了起来。...

13810
来自专栏儿童编程

《动物魔法学校》儿童学编程Scratch之“外观”部分

导读:本文通过一个案例《动物魔法学校》来学习Scratch语言的“外观”部分。之后通过一系列其他功能的综合运用对作品功能进行了扩展。

18640
来自专栏Ken的杂谈

【系统设置】CentOS 修改机器名

17230
来自专栏FSociety

SQL中GROUP BY用法示例

GROUP BY我们可以先从字面上来理解,GROUP表示分组,BY后面写字段名,就表示根据哪个字段进行分组,如果有用Excel比较多的话,GROUP BY比较类...

5.1K20

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励