Передача строки по ссылке в Java?

Я использую следующее в C:

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

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

И результат:

foo

Однако в Java это, похоже, не работает. Я предполагаю, что объект String копируется, а не передается по ссылке. Я думал, что Строки были объектами, которые всегда передаются по ссылке.

Что здесь происходит?

Ответ 1

У вас есть три варианта:

  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 обычно является наилучшим вариантом.

Ответ 2

В Java ничего не передается по ссылке. Все передано значением. Ссылки на объекты передаются по значению. Кроме того, строки неизменяемы. Поэтому, когда вы добавляете в переданную строку, вы просто получаете новую строку. Вы можете использовать возвращаемое значение или вместо этого передать StringBuffer.

Ответ 3

Что происходит, так это то, что ссылка передается по значению, т.е. передается копия ссылки. Ничто в java не передается по ссылке, и поскольку строка является неизменной, это присваивание создает новый строковый объект, на который указывает копия ссылки. Исходная ссылка по-прежнему указывает на пустую строку.

Это будет одинаково для любого объекта, т.е. установить его в новое значение в методе. Пример ниже просто делает то, что происходит более очевидным, но конкатенация строки - это одно и то же.

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

Ответ 5

объекты передаются по ссылке, примитивы передаются по значению.

Строка не является примитивным, это объект, и это особый случай объекта.

Это предназначено для экономии памяти. В 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));
    }
}

/* OUTPUT */

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

== проверяет адрес памяти (ссылку), а .equals проверяет содержимое (значение)

Ответ 6

Все аргументы в Java передаются по значению. Когда вы передаете 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" "Постоянная предпочтительность".

Как уже отмечалось, StringBuilder часто дает вам лучшую производительность - если у вас много добавлений, особенно внутри цикла, используйте StringBuilder.

Но попробуйте обойти неизменяемыми Strings а не изменяемыми StringBuilders если вы можете - ваш код будет легче читать и легче обслуживать. Подумайте о том, чтобы сделать ваши параметры final и сконфигурировать вашу среду IDE, чтобы она предупреждала вас, когда вы переназначаете параметр метода на новое значение.

Ответ 7

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

Другой вариант - создать класс со строкой в ​​качестве переменной области (сильно обескуражен) следующим образом:

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

Ответ 8

String - это специальный класс в Java. Это Потокобезопасный, что означает "После создания экземпляра String содержимое экземпляра String никогда не изменится".

Вот что происходит

 zText += "foo";

Сначала компилятор Java получит значение экземпляра zText String, затем создаст новый экземпляр String, значением которого является zText с добавлением "foo". Итак, вы знаете, почему экземпляр, на который указывает zText, не изменился. Это совершенно новый экземпляр. Фактически, даже String "foo" является новым экземпляром String. Таким образом, для этого оператора Java создаст два экземпляра String, один - "foo", другой - значение zText append "foo". Правило простое: значение экземпляра String никогда не изменится.

Для метода fillString вы можете использовать StringBuffer в качестве параметра или изменить его следующим образом:

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

Ответ 9

Ответ прост. В java строки неизменяемы. Следовательно, это похоже на использование "final" модификатора (или "const" в C/С++). Итак, как только вы назначены, вы не можете изменить его так, как вы.

Вы можете изменить значение, на которое указывает строка, но вы не можете изменить фактическое значение, на которое указывает эта строка.

Т.е. String s1 = "hey". Вы можете сделать s1 = "woah", и это полностью нормально, но вы не можете фактически изменить базовое значение строки (в данном случае: "hey" ), чтобы быть чем-то еще после того, как ее назначили с помощью plusEquals и т.д. (Т. Е. s1 += " whatup != "hey whatup").

Для этого используйте классы StringBuilder или StringBuffer или другие изменяемые контейнеры, а затем просто вызовите .toString(), чтобы преобразовать объект обратно в строку.

note: Строки часто используются в качестве хеш-ключей, поэтому эта часть причины, по которой они являются неизменяемыми.

Ответ 10

Строки immutable в Java.

Ответ 11

В этом случае используется StringBuffer

public class test {
 public static void main(String[] args) {
 StringBuffer zText = new StringBuffer("");
    fillString(zText);
    System.out.println(zText.toString());
 }
  static void fillString(StringBuffer zText) {
    zText .append("foo");
}
}

Еще лучше использовать StringBuilder

public class test {
 public static void main(String[] args) {
 StringBuilder zText = new StringBuilder("");
    fillString(zText);
    System.out.println(zText.toString());
 }
  static void fillString(StringBuilder zText) {
    zText .append("foo");
}
}

Ответ 12

Строка неизменна в java. вы не можете изменять/изменять, существующий строковый литерал/объект.

String s = "Hello"; S = S + "Привет" ;

Здесь предыдущая ссылка s заменяется новой ссылкой, указывающей на значение "HelloHi".

Однако для приведения изменчивости мы имеем StringBuilder и StringBuffer.

StringBuilder s = new StringBuilder(); s.append( "Привет" );

это добавляет новое значение "Привет" к той же ссылке. //

Ответ 13

У Аарона Дигуллы есть лучший ответ. Вариантом его второго варианта является использование оболочки или класса контейнера MutableObject библиотеки commons lang версии 3 +:

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

вы сохраняете объявление класса контейнера. Недостатком является зависимость от сообщества lang lib. Но lib имеет довольно много полезной функции и почти любой более крупный проект, над которым я работал, использовал его.

Ответ 14

Для передачи объекта (включая String) по ссылке в Java вы можете передать его как член окружающего адаптера. Решение с универсальным здесь:

import java.io.Serializable;

public class ByRef<T extends Object> implements Serializable
{
    private static final long serialVersionUID = 6310102145974374589L;

    T v;

    public ByRef(T v)
    {
        this.v = v;
    }

    public ByRef()
    {
        v = null;
    }

    public void set(T nv)
    {
        v = nv;
    }

    public T get()
    {
        return v;
    }

// ------------------------------------------------------------------

    static void fillString(ByRef<String> zText)
    {
        zText.set(zText.get() + "foo");
    }

    public static void main(String args[])
    {
        final ByRef<String> zText = new ByRef<String>(new String(""));
        fillString(zText);
        System.out.println(zText.get());
    }
}

Ответ 15

Для кого-то, кто более любопытен

class Testt {
    static void Display(String s , String varname){
        System.out.println(varname + " variable data = "+ s + " :: address hashmap =  " + s.hashCode());
    }

    static void changeto(String s , String t){
        System.out.println("entered function");
        Display(s , "s");
        s = t ;
        Display(s,"s");
        System.out.println("exiting function");
    }

    public static void main(String args[]){
        String s =  "hi" ;
        Display(s,"s");
        changeto(s,"bye");
        Display(s,"s");
    }
}

Теперь, запустив приведенный выше код, вы можете увидеть, как hashcodes коды адресов меняются с помощью строковой переменной s. новый объект назначается переменной s в функции changeto при изменении s