Müsste nicht, die Ausgabe "nicht a" ausgeben???
Ich meine ich erzeuge zwei String Variablen f und g. Und da String ein Referenztyp/Klasse ist (und kein elementarer Wert) müsste Java doch auf dem Stack für f ung g den gleichen Zeiger in dem Heap haben mit dem Wert "a". Nun setze ich den Wert auf den g zeigt(welcher ja auch der von f sein sollte) auf:
"nicht a". Und gebe am Ende f aus... Meine Vermutung war, dass nun auch "nicht a" herauskommen muss.. aber es kommt "a" heraus. Woran liegt das!?!?
f referenziert das Objekt "a" aus dem String-Pool und selbiges macht g. Sie referenzieren das selbe Objekt, denn es gibt im String-Pool nur ein "a". Deswegen ist f == g auch true. Trotzdem sind f und g eigenständige Referenzen, was zur Folge hat, dass eine Änderung von f nicht auch g verändert.
ein neues StringObjekt erzeugt habe??? Und somit g darauf zeigt und f immernoch auf das alte???
- Das wäre momentan der einzige Weg der mir schlüssig erscheint. - Andererseits finde ich es
halt noch nicht 100% schlüssig, weil dann wäre der Code gleichzusetzen mit:
Verändert sehr wohl den Inhalt des Arrays "array". Ich dachte daher, dass es bei Strings genauso sein muss, da meines Wissens es in Java zu einem CallByReference kommen kann, wenn man mit nicht elementaren Datentypen arbeitet.(und String ist ja sogesehen nicht elementar wie zb. int, double etc.)
ist eine Variable, die als Wert eine Referenz speichert. Bei einer Zuweisung wird der Wert, also die Referenz, geändert. Wenn der Variablen eine Referenz auf ein String-Objekt aus dem String-Pool zugewiesen wird, kann es natürlich sein, dass zwei Variablen als Wert die gleiche Referenz haben. Wenn dann allerdings eine neue Zuweisung erfolgt, ändert sich nur die Referenz der einen Variablen. Das beeinflusst nicht die Referenz der anderen Variablen
Sobald du irgendeine Methode mit einem Array aufrufst, liegt ein Objekt vor, dessen Werte/Elemente/Attribute/Variablen, bzw. dessen Zustand, geändert werden kann. Strings sind aber unveränderlich. Trotzdem würde eine Zuweisung an die Array-Variable nichts am ursprünglichen Array ändern
Stimmt, Strings sind unveränderlich... denke das ist es es!!!
Zum anderen Code: Guckt euch den doch mal genauer an.. Ich erzeuge in der Main eine Array mit drei Zahlen. Übergebe
dieses Array einer Methode welches in einem NEUENarray eine zahl darin abändert soll. Gebt nun aber mal das ALTE array aus.... siehe da... es hat sich auch verändert!!
Das liegt darin begründet, dass Arrays bei der Übergabe als Parameter wie Objekte behandelt werden. Es wird also die Referenz auf das Array kopiert und nciht das Array selbst, weshalb Änderungen an diesem auch anderswo sichtbar werden.
@b1zarRe
Ich glaube du hast das noch nicht ganz durchschau. Dass Strings immutable sind hat mit dem verhalten rein gar nichts zu tun. Das funktioniert genau mit jedem anderen Typ.
Dein Beispiel oben hat mit zuletzt geposteten auch rein gar nichts zu tun. Das zweite zeigt nur dass man schnell mal call-by-reference und call-by-value durcheinander bringen kann (was du da scheinbar gemacht hast).
Und deshalb stellte sich mir die Frage, warum es nicht bei Strings genauso funktioniert, da Strings ja kein elementarer
Typ sind... aber denke das liegt damit zusammen, da Strings immutable sind...?
Und ja, ich lese mir die Antworten hier durch... Aber es bringt nichts zu sagen "Gibt es nicht" oder Ähnliches.
Ich will ja verstehen, warum es bei Arrays so klappt und bei Strings anders.
Jetzt verstehe ich langsam, worauf du überhaupt hinaus willst.
Und ja, du hast recht, das liegt daran, dass Strings immutable sind. Wenn du statt Strings ein mutable Objekt wie StringBuilder verwendest, kommt es auch zu diesem "call-by-reference-Effekt".
Wenn ihr in diesem Zusammenhang von call-by-reference unterhaltet, kommt es schnell zu Missverständnissen. Die Parameterübergabe ist immer call-by-value. Der Wert einer Referenzvariablen ist eben eine Referenz, die dann kopiert wird. Der Wert eines primitive data types ist eben der Wert an sich, der in diesem Fall kopiert wird
Bei einem Methodenaufruf wird immer der Wert des actual parameters kopiert und an den formal parameter zugewiesen
[OT]Gib ihm doch einen Hinweis xD Aber das hat doch jetzt keine Relevanz für dieses Thema. Edit: Also hat der Link auch nichts mit dem Thema zu tun[/OT]
@new!
Ja genau... Ich finde das Beispiel garnicht so dumm und die Erklärung(ein Video vorher) auch sehr sinnig.
Tipp doch das Beispiel mal ein, anstatt direkt zu behaupten das es quatsch ist... Man sollte kritisch sein/bleiben
und nicht direkt alles niederschmettern.
@hüteüberhüte
was du in dem letzten Post geschrieben hast.... !? Verstehe ich nicht..
und o diesem Objekt übergibst, ändert sich o nicht! o bleibt IMMER gleich, außer du veränderst o direkt. o würde in diesem Fall also weiterhin "Hallo!" ausgeben.
Das gleiche gilt für Strings.
Java:
String s ="Hallo!";String t ="Hallo!";
t ="Tschüss!";
Warum ändert sich dann hier der Inhalt von von a indirekt:
Java:
ArrayList a =newArrayList();ArrayList b =newArrayList();
a.add("Hallo");
a.add(100);
b = a;
b.add("noch ein neues Element");Iterator it = a.iterator();while(it.hasNext()){System.out.println(it.next());}
- Korrekt, weil b(genauso wie a) auf den gleichen Wert im Heap zeigen. Und nun addiert b etwas
zu diesem 'Bereich' im Heap dazu. Also hat auch a diesen dazu bekommen. Bei elementaren Werten passiert
dieses nicht, weil die keinen "Zeiger" auf den Heap haben. Bei nicht elementaren Werten(Array,Objekte, ArrayListen etc.)
hingegen schon... und genau DESHALB habe ich nun gefragt, warum es bei Strings wohl nicht so ist und diese
sich ähnlich wie elementare Datentypen verhalten.
Das liegt wohl daran das diese immutable sind... also sorry, aber ich finde das alles nicht so trivial!! Und habe langsam
die Vermutung das einige von euch das auch nicht wissen, aber direkt abklatschen mit "nein soetwas gibst in java garnicht...."
Du beschreibst einen komplett anderen Sachverhalt als Mujahiddin
Mujahiddin hat dir gezeigt dass es in Java kein call-by-referrnce gibt (das ändern der (kopierten) Referenz hat keine Auswirkungen auf den Aufrufer).
Du hingegen hast "nur" gezeigt dass wenn zwei Variablen auf das gleiche Objekt zeigen, beide das selbe Objekt verändern, was ja eigentlich auch klar und logisch ist. Wäre das nicht so würde man sich ganz schön schwer tun beim programmieren
Wie oben schon erwähnt, mit immutable und nicht-immutable bist du da aufm falschen Dampfer, das hat damit wenig zu tun.
publicstaticvoidmain(String[] args){ArrayList a =newArrayList();
a.add("Hallo");
a.add(100);machWas(a);Iterator it = a.iterator();while(it.hasNext()){System.out.println(it.next());//Output: Hallo, 100, ganz neu ---> Würde man nun die ganze Sache mit elementaren Typen, wie zb. int anstelle ArrayList realisieren, hätte die Methode KEINE Änderungen für das ursprüungliche int gemacht !!!!!!}}publicstaticvoidmachWas(ArrayList neu){
neu.add("ganz neu");}}
die kopierte Referenz sehr wohl Auswirkung auf den Aufrufer???
Bei der ArrayList änderst du mit dem add die "inneren Daten" der ArrayList instanz. Einen String kannst du nicht ändern. Deshalb ist das Beispiel auf Strings nicht übertragbar.
Könnte man Strings ändern, bspw. mit einer setCharAt() Methode könnte man das Beispiel auch 1 zu 1 auf Strings übertragen.
Jawohl... und deshalb zitiere ich dich nochmal:
"Wie oben schon erwähnt, mit immutable und nicht-immutable bist du da aufm falschen Dampfer, das hat damit wenig zu tun."
Also, hat dies sehr wohl was mit immutable(und in den anderen Fällen ob elementarer Datenyp bzw. nicht elemntar) zu tun
- Mehr wollte ich nicht wissen... habe das ganze als CallByReference Effekt benannt, weil der Prof. das in der Vorlesung auch so benannt hatte.... Und muss sagen, das mir meine Codes nun soweit klar sind.
Was mir dagegen nichtmehr ganz so klar ist, ist was nun genau CallByReference ist, wenn es das nicht ist, was ich genannt habe.
Der Link "CallByValue/CallByReference" habe ich mir schon zugemüte geführt... bringt mich allerdings nicht wirklich weiter... jendefalls nicht 100 Prozent:
Hier wird nun gesagt, dass beides mal 1 2 herauskommt. Also es kein CallByReference gibt(!= einer ähnlichen Umsetzung in C),
ABER: kann man das überhaupt so vergleichen??? Integer ist nämlich der Wrapper für ein int.. Also eine Hülle welche auch nur ein int in sich trägt. Meiner Meinung nach wird genau DESHALB zweimal 1 2 ausgegeben. Man möge das Beispiel doch mit ArrayListen oder Arrays machen... ich glaube dann sieht das ganze anders aus...... - deshalb ist dieses FAQ hier aus dem Forum entweder falsch oder ich steige da noch nicht 100 Pro durch.
Jawohl... und deshalb zitiere ich dich nochmal:
"Wie oben schon erwähnt, mit immutable und nicht-immutable bist du da aufm falschen Dampfer, das hat damit wenig zu tun."
Also, hat dies sehr wohl was mit immutable(und in den anderen Fällen ob elementarer Datenyp bzw. nicht elemntar) zu tun
Nur weil Äpfel rund sind, heißt das nicht dass alles was rund ist nen Apfel sein muss.
Zu deinem Beispiel. Probiers einfach mit irgend einer beliebigen Klasse, erstelle dir selbst eine Klasse. Du wirst immer das selbe verhalten feststellen, eben weil es in Java nunmal kein call-by-reference gibt
Edit: Durch eine Zuweisung (weil call-by-value) lässt sich kein Objekt ändern. Ich hab früher in Grundlagenvorlesung gelernt, eine Zuweisung weist einer Variablen einen Wert zu, nicht mehr, nicht weniger. Und der Wert ist bei einer Referenzvariablen eine Referenz
Method parameters (§8.4.1) name argument values passed to a method. For every parameter declared in a method declaration, a new parameter variable is created each time that method is invoked (§15.12). The new variable is initialized with the corresponding argument value from the method invocation. The method parameter effectively ceases to exist when the execution of the body of the method is complete.
Also die Variable (der formale Parameter) wird mit dem Wert des Arguments initialisiert
Bin mir sicher, es wird auch noch etwas mehr darüber stehen. Explizit "call-by-value" bzw. "pass-by-value" habe ich jetzt nicht gefunden, aber es wird genau dieser Vorgang beschrieben
ABER: kann man das überhaupt so vergleichen??? Integer ist nämlich der Wrapper für ein int.. Also eine Hülle welche auch nur ein int in sich trägt. Meiner Meinung nach wird genau DESHALB zweimal 1 2 ausgegeben. Man möge das Beispiel doch mit ArrayListen oder Arrays machen... ich glaube dann sieht das ganze anders aus...... - deshalb ist dieses FAQ hier aus dem Forum entweder falsch oder ich steige da noch nicht 100 Pro durch.
Wenn du das wirklich swappen wollen würdest, dann müsstest du das erste Element in der Datenstruktur austauschen. Dies ist dann aber ein ganz anderer Fall, weil da etwas ganz anderes passiert.
Lass doch den Stack einfach mal Stack sein. Es reicht wenn du dir vorstellst dass es irgendwo ein Objekt gibt und irgendwo ander eine Variable die auf dieses Objekt zeigt.
Beim ersten mal tauschst du nur die Objekte aus auf die i bzw. j zeigen. Das hat auf den Aufrufer aber keine Auswirkungen.
Beim zweiten mal hast du zwei Referenzen die auf dein Array (Objekt) zeigen. Einmal die Referenz des Aufrufers, und zum anderen die Referenz in der Methode. Jetzt änderst du das Objekt in der Methode, das bekommt der andere natürlich mit.
Bei dem einen veränderst du den Inhalt eines Objekts (das Objekt ist in diesem Fall das Array, der Inhalt ist der Wert an Index 0). Bei Arrays wird der Zeiger übergeben und keine komplette Kopie des Arrays.
Bei dem anderen veränderst du die Adresse auf die die Variable zeigt. Dieser Zeiger wird allerdings bei der Übergabe kopiert. Weißt du dieser Kopie einen neuen Zeiger/Adresse zu, ändert sich ja nicht der Zeiger der Variable die du übergeben hast.
Java macht es hierbei wie C und den Pointern (*): Call by Value.
Nicht wie C++ bei Referenzen (&): Call by Reference.
Um den Unterschied zwischen Call by Value und Call by Reference zu verstehen, kann es sich lohnen eine Sprache zu betrachten, die auch wirklich beide Varianten unterstützt. Ich wähle dazu C++:
Java:
// [1] Normale Variable vom Typ "Foo" mit Namen "var"Foovar;// es wird automatisch der Konstruktor von Foo aufgerufen// [2] Zeiger auf eine Variable vom Typ "Foo" mit dem Namen "ptr"Foo* ptr =&var;// dem Zeiger muss die Adresse einer Variable vom Typ "Foo" zugewiesen werden// [3] Zeiger auf einen Zeiger auf eine Variable vom Typ "Foo" mit Namen "dblptr"Foo** dblptr =&ptr;// dem Zeiger muss die Adresse eines Zeigers auf eine Variable vom Typ "Foo" zugewiesen werden// [4] Referenz auf eine Variable vom Typ "Foo" mit Namen "ref"Foo& ref =var;// der Referenz muss eine Variable vom Typ "Foo" zugewiesen werden
Das besondere an der Referenz ist, dass "ref" ein Alias für "var" ist. Ob man ref oder var schreibt ist egal, beides meint die gleiche Variable. Im Gegensatz dazu ist der Wert von "ptr" die Adresse von "var" und damit ein anderer als der Wert von "var".
Java:
// [5] Beim Aufruf von "callByValue" werden die Variablen kopiertvoidcallByValue(Foo var1,Foo var2){Foo tmp = var1;
var1 = var2;
var2 = tmp;}// "var1" und "var2" sind nach Aufruf der Funktion unverändertFoo var1;Foo var2;callByValue(var1, var2);// [6] Beim Aufruf von "callByValue2" werden die Zeiger kopiertvoidcallByValue2(Foo* ptr1,Foo* ptr2){Foo* tmp = ptr1;
ptr1 = ptr2;
ptr2 = tmp;}// "ptr1" und "ptr2" sind nach Aufruf der Funktion unverändertFoo ptr1 =&var1;Foo ptr2 =&var2;callByValue(ptr1, ptr2);// [7] Beim Aufruf von "callByValue3" werden die Zeiger kopiert, aber es werden nicht die Kopien verändert, sondern die Variablen auf die sie zeigenvoidcallByValue3(Foo* ptr1,Foo* ptr2){Foo tmp =*ptr1;*ptr1 =*ptr2;*ptr2 =&tmp;}// "ptr1" und "ptr2" sind nach Aufruf der Funktion unverändert, aber die Variablen auf die sie zeigen, also "var1" und "var2" wurden verändertcallByValue(ptr1, ptr2);// [8] Beim Aufruf von "callByReference" werden die Variablen nicht kopiert! "ref1" ist ein Alias für die übergebene VariablevoidcallByReference(Foo& ref1,Foo& ref2){Foo tmp = ref1;
ref1 = ref2;
ref2 = tmp;}// "var1" und "var" sind nach Aufruf der Funktion verändertcallByReference(var1, var2);
Wie verhält sich das ganze nun in Java?
Ist Foo einer der Typen byte, short, int, long, char, float oder double, dann entspricht das dem Fall [1] in C++.
Ist Foo vom Typ Object oder einer anderen Klasse wie String, int[] oder ArrayList, dann entspricht das dem Fall [2] in C++ und wir haben es die ganze Zeit mit einem Zeiger auf eine Variable zu tun. Den * braucht man in Java nicht, da durch den Typ der Variable direkt klar ist ob wir einen Zeiger haben oder nicht. Die Möglichkeit von Zeigern auf Zeiger oder höheres [3] gibt es genausowenig wie richtige Referenzen [4] die als Alias wirken.
Java:
// [9] Beim Aufruf von "callByValue" werden die Variablen kopiertvoidcallByValue(int var1,int var2){int tmp = var1;
var1 = var2;
var2 = tmp;}// "var1" und "var2" sind nach Aufruf der Funktion unverändertint var1;int var2;callbyValue(var1, var2);// [10] Beim Aufruf von "callByValue2" werden die Zeiger kopiertvoidcallByValue2(int[] ptr1,int[] ptr2){int[] tmp = ptr1;
ptr1 = ptr2;
ptr2 = tmp;}// "ptr1" und "ptr2" sind nach Aufruf der Funktion unverändertint[] ptr1 ={1};int[] ptr2 ={2};callByValue2(ptr1, ptr2}// [11] Beim Aufruf von "callByValue3" werden die Zeiger kopiert, aber es werden nicht die Kopien verändert, sondern die Variablen auf die sie zeigenvoidcallByValue3(int[] ptr1,int[] ptr2){int tmp = ptr1[0];
ptr1[0]= ptr2[0];
ptr2[0]= tmp;}// "ptr1" und "ptr2" zeigen nach Aufruf der Funktion weiter auf die gleichen Objekte, aber der Inhalt der Objekte wurde verändertcallByValue(ptr1, ptr2);
Der Fall [9] in Java entspricht genau dem Fall [5] in C++.
Der Fall [10] in Java entspricht dem Fall [6] in C++, um den Zeiger selbst zu verändern müsste man entweder einen Zeiger auf einen Zeiger übergeben (call-by-value-Variante) oder eine Referenz auf einen Zeiger (call-by-reference-Variante), in Java ist so etwas nicht möglich.
Der Fall [11] entspricht dem Fall [7] in C++, wir lassen uns einen Zeiger übergeben und verändern das Objekt auf das er zeigt. Diese Wirkung ist auch von außen sichtbar.
Etwas analoges zu Fall [8] gibt es in Java nicht, denn Java kennt kein "call-by-reference".
Was ist das besondere an "immutable" Objekten in Java? Variante [10] ist bekanntlich wirkungslos und Variante [11] unmöglich, da das Objekt auf das "ptr" zeigt keine Möglichkeit zur Veränderung bietet. Der Außenstehende hat also das Gefühl er hätte es mit Variante [9] zu tun.
Was soll nun der "call-by-reference"-Effekt sein? Wenn Variante [11] mit einem "mutable"-Objekt verwendet wird, dann ist nach außen eine Wirkung sichtbar. Den Außenstehenden erinnert der Funktionsaufruf nun an Variante [8] aus C++ (richtiges Call by Reference), in Wahrheit haben wir es aber mit Fall [7] aus C++ zu tun (Call by Value).
Die Verständnisprobleme sind also nur deshalb da, weil Java nicht durch zusätzliche Syntax deutlich macht, wann wir mit normalen Variablen und wann mit Zeigern herum hantieren.
Ja, das ist korrekt. In Java gibt es aber kein Alias, weil immer ein Kopie des Werts erstellt wird und den Parametern zugewiesen wird. Außerdem gibt es keine Zeiger/Referenzen auf selbige