179

で次のことを行うことに慣れていますC

void main() {
    String zText = "";
    fillString(zText);
    printf(zText);
}

void fillString(String zText) {
    zText += "foo";
}

出力は次のとおりです。

foo

ただし、Java では、これは機能しないようです。Stringオブジェクトがreferenced で渡されるのではなくコピーされるためだと思います。文字列は常に参照渡しされるオブジェクトだと思っていました。

ここで何が起こっているのですか?

4

15 に答える 15

215

次の 3 つのオプションがあります。

  1. StringBuilder を使用します。

    StringBuilder zText = new StringBuilder ();
    void fillString(StringBuilder zText) { zText.append ("foo"); }
    
  2. コンテナー クラスを作成し、コンテナーのインスタンスをメソッドに渡します。

    public class Container { public String data; }
    void fillString(Container c) { c.data += "foo"; }
    
  3. 配列を作成します。

    new String[] zText = new String[1];
    zText[0] = "";
    
    void fillString(String[] zText) { zText[0] += "foo"; }
    

パフォーマンスの観点からは、通常、StringBuilder が最適なオプションです。

于 2009-08-13T08:30:08.380 に答える
92

Java では、何も参照によって渡されません。すべてが値渡しされます。オブジェクト参照は値渡しです。さらに、文字列は不変です。したがって、渡された文字列に追加すると、新しい文字列が取得されます。戻り値を使用するか、代わりに StringBuffer を渡すことができます。

于 2009-08-13T08:30:46.583 に答える
55

起こっていることは、参照が値によって渡されることです。つまり、参照のコピーが渡されます。Java では参照によって渡されるものはなく、文字列は不変であるため、その割り当てにより、参照のコピーが指す新しい文字列オブジェクトが作成されます。元の参照はまだ空の文字列を指しています。

これはどのオブジェクトでも同じです。つまり、メソッドで新しい値に設定します。以下の例は何が起こっているかをより明白にしていますが、文字列を連結することは実際には同じことです.

void foo( object o )
{
    o = new Object( );  // original reference still points to old value on the heap
}
于 2009-08-13T08:32:24.257 に答える
18

java.lang.String は不変です。

URL を貼り付けるのは嫌いですが、https://docs.oracle.com/javase/10/docs/api/java/lang/String.htmlは、Java ランドにいる場合に読んで理解するために不可欠です。

于 2009-08-13T08:26:57.200 に答える
11

Java のすべての引数は値渡しです。String関数にa を渡す場合、渡される値は String オブジェクトへの参照ですが、その参照を変更することはできず、基になる String オブジェクトは不変です。

割り当て

zText += foo;

次と同等です。

zText = new String(zText + "foo");

つまり、パラメーターzTextを新しい参照として (ローカルに) 再割り当てします。これは、新しいメモリ位置を指し、その中Stringには、元の内容が追加された new が含まれていzTextます"foo"

元のオブジェクトは変更されず、main()メソッドのローカル変数zTextは引き続き元の (空の) 文字列を指します。

class StringFiller {
  static void fillString(String zText) {
    zText += "foo";
    System.out.println("Local value: " + zText);
  }

  public static void main(String[] args) {
    String zText = "";
    System.out.println("Original value: " + zText);
    fillString(zText);
    System.out.println("Final value: " + zText);
  }
}

プリント:

Original value:
Local value: foo
Final value:

文字列を変更する場合は、前述のように、追加レベルのポインター間接化を提供するStringBuilderコンテナー (配列またはカスタム コンテナー クラス) を使用することができます。AtomicReferenceまたは、新しい値を返して代入します。

class StringFiller2 {
  static String fillString(String zText) {
    zText += "foo";
    System.out.println("Local value: " + zText);
    return zText;
  }

  public static void main(String[] args) {
    String zText = "";
    System.out.println("Original value: " + zText);
    zText = fillString(zText);
    System.out.println("Final value: " + zText);
  }
}

プリント:

Original value:
Local value: foo
Final value: foo

これはおそらく、一般的なケースで最も Java に似たソリューションです。Effective Javaの項目「Favor immutability」を参照してください。

ただし、前述のように、StringBuilderパフォーマンスが向上することがよくあります。特にループ内で多くの追加を行う場合は、 を使用してStringBuilderください。

Stringsただし、可能であれば、可変ではなく不変を渡すStringBuildersようにしてください。コードは読みやすく、保守しやすくなります。finalパラメータを作成し、メソッド パラメータを新しい値に再割り当てするときに警告するように IDE を構成することを検討してください。

于 2009-08-13T08:43:12.190 に答える
8

オブジェクトは参照によって渡され、プリミティブは値によって渡されます。

文字列はプリミティブではなく、オブジェクトであり、オブジェクトの特殊なケースです。

これはメモリ節約のためです。JVM には文字列プールがあります。作成された文字列ごとに、JVM は同じ文字列が文字列プールに存在するかどうかを確認し、既に存在する場合はそれを指そうとします。

public class TestString
{
    private static String a = "hello world";
    private static String b = "hello world";
    private static String c = "hello " + "world";
    private static String d = new String("hello world");

    private static Object o1 = new Object();
    private static Object o2 = new Object();

    public static void main(String[] args)
    {
        System.out.println("a==b:"+(a == b));
        System.out.println("a==c:"+(a == c));
        System.out.println("a==d:"+(a == d));
        System.out.println("a.equals(d):"+(a.equals(d)));
        System.out.println("o1==o2:"+(o1 == o2));

        passString(a);
        passString(d);
    }

    public static void passString(String s)
    {
        System.out.println("passString:"+(a == s));
    }
}

/* 出力 */

a==b:true
a==c:true
a==d:false
a.equals(d):true
o1==o2:false
passString:true
passString:false

== はメモリ アドレス (参照) をチェックしており、.equals は内容 (値) をチェックしています。

于 2009-08-13T23:55:53.547 に答える
6

String は Java の不変オブジェクトです。次のように、StringBuilder クラスを使用して、達成しようとしている作業を実行できます。

public static void main(String[] args)
{
    StringBuilder sb = new StringBuilder("hello, world!");
    System.out.println(sb);
    foo(sb);
    System.out.println(sb);

}

public static void foo(StringBuilder str)
{
    str.delete(0, str.length());
    str.append("String has been modified");
}

別のオプションは、次のように String をスコープ変数として使用してクラスを作成することです (強くお勧めしません)。

class MyString
{
    public String value;
}

public static void main(String[] args)
{
    MyString ms = new MyString();
    ms.value = "Hello, World!";

}

public static void foo(MyString str)
{
    str.value = "String has been modified";
}
于 2013-06-30T23:27:37.090 に答える
2

文字列は Java の特別なクラスです。これはスレッド セーフであり、「一度 String インスタンスが作成されると、String インスタンスの内容は決して変更されません」という意味です。

これが何が起こっているかです

 zText += "foo";

最初に、Java コンパイラは zText String インスタンスの値を取得し、次に値が「foo」を付加した zText である新しい String インスタンスを作成します。これで、zText が指すインスタンスが変更されない理由がわかります。まったく新しいインスタンスです。実際、String "foo" も新しい String インスタンスです。したがって、このステートメントでは、Java は 2 つの String インスタンスを作成します。1 つは "foo" で、もう 1 つは zText append "foo" の値です。ルールは単純です: String インスタンスの値は決して変更されません。

メソッド fillString では、StringBuffer をパラメーターとして使用するか、次のように変更できます。

String fillString(String zText) {
    return zText += "foo";
}
于 2009-08-13T09:23:23.140 に答える
1

文字列は Java では不変です。

于 2009-08-13T08:29:00.317 に答える
1

文字列は Java では不変です。既存の文字列リテラル/オブジェクトを変更/変更することはできません。

文字列 s="こんにちは"; s=s+"こんにちは";

ここで、以前の参照 s は、値 "HelloHi" を指す新しい参照 s に置き換えられます。

ただし、可変性をもたらすために、StringBuilder と StringBuffer があります。

StringBuilder s=new StringBuilder(); s.append("こんにちは");

これにより、新しい値「Hi」が同じ参照 s に追加されます。///

于 2017-09-25T07:23:55.260 に答える
0

これまでのところ、Aaron Digulla が最良の回答をしています。彼の 2 番目のオプションのバリエーションは、commons lang ライブラリ バージョン 3+ のラッパーまたはコンテナー クラス MutableObject を使用することです。

void fillString(MutableObject<String> c) { c.setValue(c.getValue() + "foo"); }

コンテナ クラスの宣言を保存します。欠点は、commons lang lib への依存です。しかし、ライブラリには非常に多くの便利な機能があり、私が取り組んだほとんどの大規模プロジェクトで使用されています。

于 2014-11-23T21:58:04.653 に答える