【问题标题】:Pass-by-value (StringBuilder vs String) [duplicate]按值传递(StringBuilder vs String)[重复]
【发布时间】:2015-10-01 16:26:55
【问题描述】:

我不明白为什么System.out.println(name)输出Sam不受方法的concat函数影响,而System.out.println (names) 输出 Sam4 作为方法的 append 方法的结果。为什么 StringBuilder 受影响而不是 String?通常,对对象的引用调用方法会影响调用者,所以我不明白为什么 String 结果保持不变。提前致谢

public static String speak(String name) {
    name = name.concat("4");
    return name;
}

public static StringBuilder test(StringBuilder names) {
    names = names.append("4");
    return names; 
}

public static void main(String[] args) {
    String name = "Sam";
    speak(name);
    System.out.println(name); //Sam
    StringBuilder names = new StringBuilder("Sam");
    test(names);
    System.out.println(names); //Sam4
}

【问题讨论】:

  • 这里有很多答案,基本上都说同样的话。很难选择一个来投票:)
  • @ArnaudDenoyelle 幸运的是你不需要只选择一个;)
  • @ArnaudDenoyelle 正如 Mark Rotteveel 所说,Helenesh 是必须做出艰难选择的人 ;)

标签: java string parameter-passing stringbuilder pass-by-value


【解决方案1】:

因为当你打电话给speak(name);时,你在里面说话

name = name.concat("4");

它创建了一个新对象,因为Strings 是不可变的。当您更改原始字符串时,它会创建一个新对象,我同意您正在返回它但您没有捕获它。

所以本质上你正在做的是:

name(new) = name(original) + '4'; // but you should notice that both the names are different objects.

试试

String name = "Sam";
name = speak(name);

当然,现在我认为没有必要解释为什么它与 StringBuilder 一起工作,除非你不知道 StringBuilder 是可变的。

【讨论】:

  • "它创建了一个新对象,因为字符串是不可变的。"语言中没有“可变性”的概念。它返回一个新对象,因为这是记录该方法要做的事情。它不会再次修改它所调用的对象,因为这是记录该方法要做的事情。
  • @newacct 对不起,先生,我确定我没有你知道的那么多,但我认为如果我说“它返回一个新对象,因为它的方法已记录在案,所以 OP 会不高兴所以”。所以这就是为什么我想给出一些理由。
  • 但我们只说该类是“不可变的”,只是因为它的所有方法都被记录在案,不会对其进行修改。因此,如果说方法不会修改它,因为它是不可变的,那将是循环推理。
  • @newacct 可能就像“鸡还是蛋”:P
【解决方案2】:

看看Javadoc for String,你会读到

[...] 字符串对象是不可变的 [...]。

这意味着concat(String)不会改变String本身,而是构造一个新的String

另一方面,StringBuilders 是可变的。通过调用append(CharSequence),对象本身发生了变异。

【讨论】:

    【解决方案3】:

    因为String 是不可变的,因此String#concat 不会修改原始的String 实例,它只返回一个新的String,而原始的保持不变,而StringBuilder 是可变的并且更改反映在StringBuilder 实例作为参数传递。

    【讨论】:

      【解决方案4】:

      好的,speak 方法在做什么?

      首先,

      name.concat("4");
      

      创建新对象,等于name,与"4"连接。

      所以,这条线

      name = name.concat(4);
      

      重新定义本地(用于speak 方法)变量name

      然后你返回对这个新值的引用

      return name;
      

      因此,在方法内传递的原始变量没有被修改,但方法返回修改后的值。

      test 方法中,您实际上是在不修改引用的情况下修改变量(StringBuilder 类是可变的,因此如果可以修改此类型,则变量也是如此)。

      然后我们可以看到另一个问题:为什么StringBuilder.append 返回值,这似乎是多余的。这个问题的答案在于对“builder”模式的描述,它是实现修改方法的常用方式。见wikipedia on Builder pattern

      【讨论】:

        【解决方案5】:

        String 在 java 中是不可变的。只要您在名称上调用concat 方法。一个新的字符串被创建,当你使用System.out.println(name)中的旧引用时。如果你想使用修改后的字符串,你应该明确地返回引用。 而StringBuilder 是可变的,它总是返回相同的引用。

        【讨论】:

          【解决方案6】:

          当您调用 speak(name) 时,它会计算新值,但会丢弃它。

          如果你用

          替换它
          name = speak(name);
          

          结果将是您所期望的。

          使用StringBuilder,你传递的对象是可变的:所以

          names.append(names);
          

          更改当前对象的状态(它还返回对同一对象的引用,这只是为了方便您编写names.append(...).append(...)等代码)。所以在StringBuilder 的情况下,当您调用该方法时,您所引用的对象实际上已经发生了变化,因此您会看到这些变化。

          【讨论】:

          • 你没有回答这个问题:为什么它与 StringBuilder 一起工作?
          • 是的,没错。查看更新。
          【解决方案7】:

          在您的方法speak 中,concat 方法返回一个新字符串,调用它的原始对象不变(字符串是不可变的)。据记载:

          如果参数字符串的长度为0,则返回此String 对象。否则,将返回一个 String 对象,该对象表示一个字符序列,该字符序列是由该 String 对象表示的字符序列和由参数字符串表示的字符序列的串联。

          调用name.concat("4") 相当于name + "4"

          在您的test 方法中,append 方法修改了StringBuilder 的内容。据记载:

          StringBuilder 上的主要操作是appendinsert 方法,它们被重载以便接受任何类型的数据。每个都有效地将给定的数据转换为字符串,然后将该字符串的字符附加或插入到字符串构建器。 append 方法总是将这些字符添加到构建器的末尾; insert 方法在指定点添加字符。

          在您的 main 方法中,namenames 仍然是方法调用之前的 相同 对象,但 name 的内容不变,因为字符串是不可变的,而内容的names 已更改。

          如果您使用了这两种方法的返回值,那么您将得到您期望的结果。

          【讨论】:

            【解决方案8】:

            首先,String 是 Java 中的一个不可变类不可变类只是一个实例不能被修改的类。实例中的所有信息都是在创建实例时初始化的,不能修改。

            其次,在java中参数是通过值而不是通过引用发送的。

            在您的“测试”方法中,您不需要names = names.append("4"),而names.append("4") 就足够了。

            如果你检查 java docs 的 String 对象,你会看到那里的大多数方法,包括 concat,都会生成一个新的 String。

            所以要输出 Sam4 也为字符串,你需要在 main 方法中有这个name = speak(name)

            【讨论】:

              【解决方案9】:

              字符串

              String 是不可变的(一旦创建就不能更改)对象。这 作为字符串创建的对象存储在常量字符串池中。 Java 中的每个不可变对象都是线程安全的,这意味着 String 是 也是线程安全的。字符串不能被两个线程使用 同时地。字符串一旦分配就不能更改。

              字符串演示 = "你好"; // 上面的对象存储在常量中 字符串池及其值不可修改。

              demo="再见" ; //在常量池中创建新的“Bye”字符串并且 由演示变量//“hello”字符串引用 存在于字符串常量池中并且它的值没有被覆盖但是我们 丢失了对“hello”字符串的引用

              StringBuilder

              StringBuilder 与 StringBuffer 相同,即存储 堆中的对象,也可以修改。主要区别 StringBuffer 和 StringBuilder 之间是 StringBuilder 是 也不是线程安全的。 StringBuilder 速度很快,因为它不是线程安全的 .

              更多详情请查看this

              结论: 您不需要再次将值重新分配给StringBuilder,因为它已经是一个参考 测试方法应该是

              public static void test(StringBuilder names) {
                  names.append("4");
                 }
              

              但说话应该是

               String name = "Sam";
                 name =  speak(name);
              

              【讨论】:

              • 问的不是这个。
              • 好的,我更新了答案
              猜你喜欢
              • 2014-03-01
              • 1970-01-01
              • 2015-12-13
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-12-23
              • 2019-01-01
              • 2013-12-02
              相关资源
              最近更新 更多