Java中的String是不可变的,传递的是引用还是值?

6

大家好,我正在学习SCJA考试,并且有一个关于字符串按引用/值传递以及它们是不可变的问题。以下代码输出“abc abcfg”。

我想知道的是为什么会发生这种情况?我不理解方法f内部发生了什么。字符串通过值传递,所以它应该在方法内部更改为“abcde”,对吗?因为如果b + =“fg”附加到字符串,为什么在方法内部不起作用呢?

谢谢!

public class Test {

    public static void main(String[] args){
        String a =new String("abc");
        String b = a;
        f(b);
        b+="fg"
        System.out.println(a + " " + b);
    }

    public static void f(String b){
        b+="de";
        b=null;
    }
}

1
需要记住的关键是引用按值传递。f获取一个引用的副本,该引用最初是指向“fg”的指针。 - Patricia Shanahan
5个回答

8
在函数 void f(String b) 中,行 b+="de"; 创建了一个全新的 String 对象,不会影响到在 main 函数中的对象。
因此,当我们说 String不可变的时,这意味着对 String 对象进行的任何更改都会导致创建一个全新的 String 对象。
public class Test {
    public static void main(String[] args){
        String a =new String("abc");

        String b = a; // both a & b points to the same memory address

        f(b); // has no effect

        // b still has the value of "abc"
        b+="fg" // a new String object that points to different location than "a"

        System.out.println(a + " " + b); // should print "abc abcfg" 
    }

 public static void f(String b){
    b+="de";  // creates a new object that does not affect "b" variable in main
    b=null;
 }
}

3
在你的方法f()中,你给参数b赋了一个新的字符串,但是参数就像局部变量一样,对它们进行赋值不会对方法外的任何东西产生影响。这就是为什么在方法调用后传入的字符串保持不变的原因。

那么我必须执行 b = f(b) 并从方法 f 返回一个字符串? - Taobitz
没错!即使这样,直到你返回字符串,方法外部都没有任何影响。 - Bohemian
它还指出,如果你按照当前的编码方式进行 b = f(b) 并返回一个 String,你会得到输出结果 "abc nullfg",因为你将 b 设为了 null。 - Andrew Martin
我非常确定这是非常不正确的。 字符串作为值传递,该值是指向字符串的指针。 当你执行任何 b+="de"; 或 null 操作时,你会影响指针。 b+="de"; 相当于 b = new String; 如果你做同样的事情,但只是将字符串放在包装器中,并改变包装器中的数据,你会看到更改,因为你改变了包装器中的数据指针,但没有改变包装器本身。 - StarWind0
2
@StarWind 确定。如果您改变对象,则是在调用代码中分配给变量的相同对象被更改,因此在方法外部也可以“看到”更改。在调用代码中,它只是赋值而已,不会有任何变化。 - Bohemian
显示剩余3条评论

3
这行代码:
b += "de";

粗略地翻译为:
b = b + "de";

这段代码创建了一个新的字符串,并将其存储在本地变量 'b' 中。该方法外部的引用没有发生任何改变。当试图理解为什么会这样时,有几个关键点需要记住。

  1. 在Java中,所有的方法参数都是按值传递的。
  2. 方法具有本地堆栈,其中存储了它们的所有数据,包括方法参数。

当您将一个新对象分配给传递到方法中的变量时,您只是用新创建的对象的地址替换了本地堆栈中的地址。方法外部实际对象的地址保持不变。


0

当你像这样说b+="de"时,你正在创建一个新的String对象。

这就像是在说b = new String(b+"de");

如果你真的想保留传入的值,那么可以使用StringBuilder,像这样:

    StringBuilder a =new StringBuilder("abc");
        StringBuilder b = a;
        f(b);
        b.append("fg");
        System.out.println(a + " " + b);
    }

  public static void f(StringBuilder b){
        b.append("de");
        b=null;
    }

现在你的输出将会是"abcdefg",因为stringbuilder对象b作为对象本身的值传递给了f。我修改了该值,而没有使用"new"来指向另一个对象。希望现在清楚了。


0
当您将字符串传递到方法中时,它会在方法中创建一个新的字符串对象。如果您在方法中放置了println语句,就像这样:
public static void f(String b){
    b+="de";
    System.out.println(b);
    b=null;
}

你会看到输出结果为“abcde”。
然而,一旦你退出方法,你回到了原始对象,它并没有被改变。因此,将fg附加到它上面只会产生一个新的字符串(b),包含“abcfg”。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接