Perché un parametro ArrayList è stato modificato, ma non un parametro String?

public class StackOverFlow { public static void main(String[] args) { ArrayList al = new ArrayList(); al.add("A"); al.add("B"); markAsNull(al); System.out.println("ArrayList elements are "+al); String str = "Hello"; markStringAsNull(str); System.out.println("str "+ str); } private static void markAsNull(ArrayList str){ str.add("C"); str= null; } private static void markStringAsNull(String str){ str = str + "Append me"; str = null; } } 

Questo produce:

 ArrayList elements are [A, B, C] str Hello 

Nel caso di ArrayList , gli elementi aggiunti vengono recuperati. In caso di String la chiamata al metodo non ha alcun effetto sulla stringa trasmessa. Che cosa sta facendo esattamente la JVM? Qualcuno può spiegare in dettaglio?

Nel caso degli oggetti stringa Arraylist gli elementi aggiunti vengono recuperati. In caso di stringa, la chiamata al metodo non ha alcun effetto sulla stringa trasmessa.

Succede perché Java è Pass-by-Value e String s è immutabile

Quando chiami

 markAsNull(ArrayList str) 

Viene creato un nuovo riferimento per nome str per la stessa ArrayList puntata da al . Quando add un elemento su str viene aggiunto allo stesso object. Più tardi metti str a null ma l’object ha i nuovi valori aggiunti ed è puntato da a1 .

Quando chiami

 markStringAsNull(String str) { str = str + "Append me"; // ... } 

La riga str = str + "Append me"; crea un nuovo object String aggiungendo la stringa specificata e assegnandola a str . ma ancora una volta è solo un riferimento alla stringa attuale che ora punta alla stringa appena creata. (a causa dell’immutabilità) e la stringa originale non è cambiata.

I metodi markXAsNull impostano i riferimenti locali come null . Ciò non ha alcun effetto sul valore effettivo archiviato in quella posizione. Il metodo main ha ancora i propri riferimenti ai valori e può chiamare println usando quelli.

Inoltre, quando si esegue la concatenazione di stringhe, toString() viene richiamato sull’Oggetto ed è per questo che ArrayList viene emesso come una lista dei suoi valori tra parentesi.

Java segue il concetto di valore passivo (non c’è passaggio per riferimento in java). Quindi quando si passa una stringa alla funzione invia una “copia di riferimento” a quella stringa alla funzione. Quindi, anche se si imposta la variabile su null nella funzione, quando ritorna al chiamante fa riferimento solo al suo valore originale. Ecco perché la stringa originale non ha alcun effetto.

In caso di Arraylist, la copia di riferimento si riferisce al Arraylist originale (che è anche lo stesso in caso di stringa). Ma quando aggiungi qualcosa in ArrayList, si riferisce all’object originale e puoi vedere l’effetto. (prova nel metodo arraylist = new ArrayList (), il tuo arraylist originale rimarrà così com’è).

In caso di stringa, quando lo fai

str = str + “abc”;

Java crea un nuovo object String che avrà riferimento alla stringa “xyzabc” (ad es. Str = “xyz”) e “xyz” sarà idoneo per la garbage collection. Ma dato che “xyz” ha ancora una variabile che si riferisce ad essa (stringa originale) non sarà raccolta dei dati inutili. Ma non appena la chiamata alla funzione si risolve, “xyzabc” fa la garbage collection.

Riassunto della discussione è, finché un riferimento fa riferimento allo stesso object è ansible apportare modifiche alla funzione, ma quando si tenta di cambiare il riferimento (str = str + “abc”) si fa riferimento al nuovo object nel metodo in modo che il tuo object originale rimarrà così com’è.

From the Book: SCJP – Programmatore Sun Certified per Java 6 Guida allo studio (Katty Sierra – Bert Bates) Capitolo 3 Obiettivo 7.3 – Passare le variabili nei metodi

Java è in realtà un valore pass-by per tutte le variabili in esecuzione all’interno di una singola VM. Pass-by-value significa valore pass-by-variable. E questo significa, passaggio per copia della variabile!

La linea di fondo su pass-by-value: il metodo chiamato non può cambiare la variabile del chiamante, sebbene per le variabili di riferimento object, il metodo chiamato può cambiare l’object a cui la variabile fa riferimento. Qual è la differenza tra cambiare la variabile e cambiare l’object? Per i riferimenti agli oggetti, significa che il metodo chiamato non può riassegnare la variabile di riferimento originale del chiamante e farlo riferirsi a un object diverso, o null. Ad esempio, nel seguente frammento di codice,

 void bar() { Foo f = new Foo(); doStuff(f); } void doStuff(Foo g) { g.setName("Boo"); g = new Foo(); } 

riassegnare g non riassegna f! Alla fine del metodo bar (), sono stati creati due oggetti Foo, uno a cui fa riferimento la variabile locale f e uno a cui fa riferimento la variabile locale (argomento) g. Poiché il metodo doStuff () ha una copia della variabile di riferimento, ha un modo per arrivare all’object Foo originale, ad esempio per chiamare il metodo setName (). Ma il metodo doStuff () non ha un modo per arrivare alla variabile di riferimento f. Quindi doStuff () può cambiare i valori all’interno dell’object f fa riferimento a, ma doStuff () non può cambiare il contenuto effettivo (pattern a bit) di f. In altre parole, doStuff () può cambiare lo stato dell’object a cui fa riferimento f, ma non può far riferimento a un object diverso!

In Java, è ansible creare un object e fare riferimento a più puntatori. Chiamare un metodo mutatore su qualsiasi puntatore modificherà effettivamente l’object unico, aggiornando quindi tutti gli altri riferimenti.

Ma se chiamate istruzioni di assegnazione variabile su un riferimento, verrà modificato solo quel puntatore, poiché non esegue alcun lavoro sul lato object (questo è il meglio che potrei spiegare …).

Passare un object a un parametro copierà efficacemente il riferimento, risultante in un singolo object, con due puntatori: uno globale e l’altro locale.

Un altro punto, dato che String è immutabile, otterrai effettivamente un nuovo object, che è distinto dall’originale (dal fatto che devi dire a = a + "a" ), ecco perché non modificherà il stringa originale.