[java] Java에서 참조로 문자열 전달?

나는 다음을 수행하는 데 익숙하다 C.

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

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

그리고 출력은 다음과 같습니다

foo

그러나 Java에서는 작동하지 않는 것 같습니다. String객체는 reference에 의해 전달되는 대신 복사 되기 때문에 가정합니다 . Strings는 객체이며 항상 참조로 전달된다고 생각했습니다.

무슨 일이야?



답변

세 가지 옵션이 있습니다.

  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가 일반적으로 가장 좋은 옵션입니다.


답변

Java에서는 아무것도 참조로 전달되지 않습니다 . 모든 것이 value에 의해 전달됩니다 . 객체 참조는 값으로 전달됩니다. 또한 문자열은 변경할 수 없습니다 . 따라서 전달 된 문자열에 추가하면 새 문자열이 생깁니다. 반환 값을 사용하거나 대신 StringBuffer를 전달할 수 있습니다.


답변

일어나고있는 것은 참조가 값으로 전달된다는 것입니다. 즉, 참조 의 사본 이 전달됩니다. java의 어떤 것도 참조로 전달되지 않으며 문자열은 변경할 수 없으므로 해당 지정은 참조 사본이 가리키는 새 문자열 객체를 만듭니다 . 원래 참조는 여전히 빈 문자열을 가리 킵니다.

이것은 어떤 객체에서도 동일합니다. 즉, 메소드에서 새로운 값으로 설정합니다. 아래 예제는 진행 상황을보다 명확하게하지만 문자열 연결은 실제로 동일합니다.

void foo( object o )
{
    o = new Object( );  // original reference still points to old value on the heap
}


답변

java.lang.String은 변경할 수 없습니다.

URL을 붙여 넣는 것이 싫지만 https://docs.oracle.com/javase/10/docs/api/java/lang/String.html 은 자바 사이트에 있는지 읽고 이해하는 데 필수적입니다.


답변

객체는 참조로 전달되고 프리미티브는 값으로 전달됩니다.

문자열은 프리미티브가 아니며 객체이며 특수한 경우입니다.

이것은 메모리 절약을위한 것입니다. 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는 내용 (값)을 확인합니다


답변

Java의 모든 인수는 값으로 전달됩니다. a String를 함수에 전달하면 전달 된 값 String 개체에 대한 참조,하지만 당신은 그 기준을 수정할 수 있으며, 기본 String 객체는 불변이다.

과제

zText += foo;

다음과 같습니다.

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

즉,이 (로컬) 파라미터 재 할당된다 zText하는 새로운 새로운 메모리 위치를 가리키는 새로운 참조로서 String원래 포함 된 내용 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와 유사한 솔루션 일 것입니다. 효과적인 Java 항목 “Favor immutability”를 참조하십시오 .

그러나 언급했듯이, StringBuilder종종 루프 내부에서 할 일이 많으면을 사용하여 성능을 향상시킬 수 있습니다 StringBuilder.

그러나 Strings가능 StringBuilders하다면 변경 불가능한 것이 아니라 변경 불가능한 것을 전달 하십시오. 코드를보다 쉽게 ​​읽고 유지 관리 할 수 ​​있습니다. final메소드 매개 변수를 새 값으로 재 할당 할 때 경고하도록 매개 변수 작성 및 IDE 구성을 고려하십시오 .


답변

문자열은 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");
}

또 다른 옵션은 다음과 같이 문자열을 범위 변수로 사용하지 않는 클래스를 만드는 것입니다.

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";
}