Pasando una cadena por referencia en Java?

Estoy acostumbrado a hacer lo siguiente en C :

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

Y la salida es:

 foo 

Sin embargo, en Java, esto no parece funcionar. Supongo que porque el objeto String se copia en lugar de pasar por referencia . Pensé que las cuerdas eran objetos, que siempre pasan por referencia.

¿Que esta pasando aqui?

Tienes tres opciones:

  1. Use un StringBuilder:

     StringBuilder zText = new StringBuilder (); void fillString(StringBuilder zText) { zText.append ("foo"); } 
  2. Cree una clase de contenedor y pase una instancia del contenedor a su método:

     public class Container { public String data; } void fillString(Container c) { c.data += "foo"; } 
  3. Crear una matriz:

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

Desde el punto de vista del rendimiento, StringBuilder suele ser la mejor opción.

En Java, nada se pasa por referencia . Todo se pasa por valor . Las referencias a objetos se pasan por valor. Además, las cadenas son inmutables . Entonces cuando agregas el String pasado, obtienes un nuevo String. Puede usar un valor de retorno o pasar un StringBuffer en su lugar.

Lo que está sucediendo es que la referencia se pasa por valor, es decir, se pasa una copia de la referencia. Nada en Java se pasa por referencia, y como una cadena es inmutable, esa asignación crea un nuevo objeto de cadena al que ahora apunta la copia de la referencia . La referencia original aún apunta a la cadena vacía.

Esto sería lo mismo para cualquier objeto, es decir, establecerlo en un nuevo valor en un método. El siguiente ejemplo simplemente hace que lo que está pasando sea más obvio, pero concatenar una cadena es realmente lo mismo.

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

java.lang.String es inmutable.

No me gusta pegar las URL, pero https://docs.oracle.com/javase/10/docs/api/java/lang/String.html es esencial para que las leas y comprendas si estás en java-land.

los objetos se pasan por referencia, los primitivos se pasan por valor.

La cadena no es primitiva, es un objeto, y es un caso especial de objeto.

Esto es para fines de ahorro de memoria. En JVM, hay un grupo de cadenas. Para cada cadena creada, JVM intentará ver si existe la misma cadena en el grupo de cadenas, y apuntará a ella si ya hay una.

 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 

el == está buscando la dirección de la memoria (referencia), y el .equals está buscando contenido (valor)

String es un objeto inmutable en Java. Puede usar la clase StringBuilder para hacer el trabajo que está tratando de lograr, de la siguiente manera:

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

Otra opción es crear una clase con una cadena como variable de ámbito (muy desaconsejada) de la siguiente manera:

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

Las cadenas se pasan por referencia (bueno, estrictamente hablando, como señala Ed Swangren, una referencia a la cadena se pasa por valor ) pero son inmutables.

 zText += foo; 

es equivalente a:

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

Es decir, modifica la variable (local) zText modo que ahora apunte a una nueva ubicación de memoria, en la que se encuentra una String con el contenido original de zText , con "foo" adjunto. El objeto original no se modifica y la variable local del método main() zText aún apunta a la cadena original (vacía).

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

huellas dactilares:

 Original value: Local value: foo Final value: 

Si desea modificar la cadena, puede, como se indica, utilizar StringBuilder o, de lo contrario, algún contenedor (una matriz o una clase de contenedor personalizado) que le proporcione un nivel adicional de direccionamiento indirecto del puntero. Alternativamente, simplemente devuelva el nuevo valor y asígnelo:

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

huellas dactilares:

 Original value: Local value: foo Final value: foo 

Esta es probablemente la solución más parecida a Java en el caso general; consulte el elemento de Java efectivo “Favorecer la inmutabilidad”.

Sin embargo, como se señaló, StringBuilder a menudo le dará un mejor rendimiento: si tiene mucho que hacer, especialmente dentro de un bucle, use StringBuilder .

Pero si lo prefiere, intente pasar Strings inmutables en lugar de StringBuilders mutables: su código será más fácil de leer y más fácil de mantener. Considere la posibilidad de finalizar los parámetros y configurar su IDE para advertirle cuando reasigne un parámetro de método a un nuevo valor.

String es una clase especial en Java. Es Thread Save lo que significa “Una vez que se crea una instancia de String, el contenido de la instancia de String nunca cambiará”.

Esto es lo que está pasando para

  zText += "foo"; 

En primer lugar, el comstackdor de Java obtendrá el valor de la instancia de zText String y luego creará una nueva instancia de String cuyo valor sea zText y se agregará “foo”. Entonces sabes por qué la instancia a la que zText apunta no cambia. Es totalmente una nueva instancia. De hecho, incluso String “foo” es una nueva instancia de String. Por lo tanto, para esta afirmación, Java creará dos instancias String, una es “foo”, otra es el valor de zText, agregue “foo”. La regla es simple: el valor de la instancia de String nunca cambiará.

Para method fillString, puede usar un StringBuffer como parámetro, o puede cambiarlo así:

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

La respuesta es simple. En java, las cadenas son inmutables. Por lo tanto, es como usar el modificador ‘final’ (o ‘const’ en C / C ++). Entonces, una vez asignado, no puedes cambiarlo como lo hiciste.

Puede cambiar el valor al que apunta una cadena, pero NO puede cambiar el valor real al que apunta actualmente esta cadena.

Es decir. String s1 = "hey" . Puedes hacer s1 = "woah" , y eso está totalmente bien, pero no puedes cambiar el valor subyacente de la cadena (en este caso: “hey”) para que sea otra cosa una vez que se asigne usando plusEquals, etc. . s1 += " whatup != "hey whatup" ).

Para hacer eso, use las clases StringBuilder o StringBuffer u otros contenedores mutables, luego simplemente llame a .toString () para convertir el objeto a una cadena.

Nota: Las cadenas se utilizan a menudo como teclas de almohadilla, por lo que es parte de la razón por la que son inmutables.

Las cadenas son inmutables en Java.

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

Mejor aún use 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"); } } 

La cadena es inmutable en Java. no puede modificar / cambiar un literal / objeto de cadena existente.

String s = “Hola”; s = s + “hola”;

Aquí, la referencia anterior s se reemplaza por la nueva referencia que apunta al valor “HelloHi”.

Sin embargo, para llevar mutabilidad tenemos StringBuilder y StringBuffer.

StringBuilder s = new StringBuilder (); s.append (“Hola”);

esto agrega el nuevo valor “Hola” a la misma referencia. //

Aaron Digulla tiene la mejor respuesta hasta ahora. Una variación de su segunda opción es usar el tipo de contenedor o contenedor clase MutableObject de commons lang library versión 3+:

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

guarda la statement de la clase de contenedor. El inconveniente es una dependencia de commons lang lib. Pero la lib tiene muchas funciones útiles y casi cualquier proyecto más grande en el que haya trabajado lo usó.

Para pasar un objeto (incluido String) por referencia en java, puede pasarlo como miembro de un adaptador circundante. Una solución con un genérico está aquí:

 import java.io.Serializable; public class ByRef 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 zText) { zText.set(zText.get() + "foo"); } public static void main(String args[]) { final ByRef zText = new ByRef(new String("")); fillString(zText); System.out.println(zText.get()); } }