Java Parameterübergabe in Java (call by value bei Objekten)

@Dark Power: Ohne hier die Aussagen anderer User beurteilen zu wollen, kommt da "nicht recht" raus, weil bei der Übergabe die Adresse der Referenz kopiert wird.

Natürlich kann man in Java Dinge machen, die sich wie CbR anfühlen.
Deshalb ist es für mich auch kein "echtes" CbV. Bei einem echten CbV würde ich erwarten, dass das Objekt kopiert wird. Das Kopieren von Objekten ist jedoch eine Wissenschaft für sich und Gott sei Dank macht Java das nicht :-)
Weil wie wird ein Objekt korrekt kopiert?

Also es ist vernünftig, dass Objekte nicht kopiert werden bei der Übergabe. Allerdings sollte man sich im Klaren sein, dass dennoch die Referenzen auf die Objekte kopiert werden. Das finde ich persönlich allerdings auch nicht schlimm. Ich finde Ausgabe-Parameter nicht wirklich hübsch und die Beispiele von mir halte ich für schlechten Stil.

@Redirion:
Ok, dann weise ich halt "null" zu. Besser?
Ich kann es anders nicht nachweisen.

Du hast nachgewiesen, dass die Referenz auf das Objekt übergeben wird. Ich habe nachgewiesen, dass es lediglich eine Kopie der Referenz ist.

call-by-reference übergibt aber keine Kopien von Referenzen, sondern die Referenzen selbst. Genau das wird bei Java aber nicht gemacht.

Und in Zeile 5 deines main-Methoden-Beispiels wird auch nur die Referenz auf dem Speicher kopiert. Genauso wie es bei der Parameterübergabe passiert. Du hast eine neue Variable mit dem selben Speicherinhalt. Genau das macht Java bei der Parameterübergabe. Dies ist allerdings kein call-by-reference.

call-by-reference heißt ja nicht, dass der übergebene Parameter eine Referenz ist, sondern dass statt dem Wert des Parameters (im Fall einer Referenz/Objekts die Adresse des Objekts) eine Referenz auf den Parameter übergeben wird.

Also wenn Java CbR hätte, dann würde es bei einem Aufruf wie diesem
foo(bar);
sowas draus machen:
call foo with address of variable bar

Aber was macht Java:
call foo with copy of the address in variable bar
 
Zuletzt bearbeitet:
1668mib schrieb:
Aber was macht Java:
call foo with copy of the address in variable bar

und wo soll die Kopie sein? Ist es nicht viel mehr so, dass du diese Kopie erst durch den new Operator verursachst?
x = null bewirkt doch auch nur die Freigabe deiner Variable auf die ursprüngliche Referenz. Damit wird nicht sofort der Speicherbereich freigegeben! Der GC arbeitet doch genau nach diesem Prinzip und sucht nach Hinweisen zur Nutzung der ursprünglichen Referenz. Werden keine Referenzen auf die ursprüngliche Referenz mehr gefunden, wird der Speicherbereich schließlich freigegeben.
 
Zuletzt bearbeitet:
Also nochmals.

Code:
StringBuilder bar = new StringBuilder("xyz");  // intern: bar liegt auf Adresse 0815, Wert von bar = 1234 (Adresse!)
fooValue(bar);             // intern: fooValue(1234) <-- bar = 1234 wird kopiert
fooReference(bar);         // intern: fooReference(0815) <-- Adresse von bar wird übergeben
Wenn du es jetzt nicht verstehst, dann ist es mir ehrlich gesagt zu blöd.

x = null bewirkt doch auch nur die Freigabe deiner Variable auf die ursprüngliche Referenz
x = null schreibt schlicht 0 in die Adresse von x. Mit freigeben hat das ganze gar nichts zu tun...
Also soll heißen auf mein Beispiel übertragen:
bar = null; => intern: In Adresse 0815 steht nun 0, in Adresse 1234 gibt es immer noch einen StringBuilder
Irgendwann wird der GC merken, dass der StringBuilder in 1234 nicht mehr benötigt wird...
 
Zuletzt bearbeitet:
@1668mib Eben hats klick gemacht, verbessere mich bitte falls nicht ;)

Habe mir schnell ein Beispiel gebastelt was es für mich verständlich gemacht hat:

Code:
....
public static void testcBR (String [] strArray1, String [] strArray2){
   strArray1[0] = "z";
   strArray2 = new String [] {"x", "y"};

   System.out.println(""+strArray1[0]);
   System.out.println(""+strArray2[0]);
}

public static void main (String[] args){
   String [] strArray1 = {"a", "b"};
   String [] strArray2 = {"b", "c"};

   testcBR (strArray1, strArray2);

   System.out.println(""+strArray1[0]);
   System.out.println(""+strArray2[0]);
}

Als Ausgabe erscheint auf der Konsole:
z
x
z
b

Demnach wird wie bereits gesagt eine Kopie der Referenz(der Zeiger/Deskriptor...) übergeben.
Durch die Zeile:
strArray1[0] = "z";
weise ich via kopierter Referenz, die aber trotzdem auf den selben Speicherbereich im Heap zeigt, dem Objekt (strArray1) auf dem Heap an der Stelle 0 des Arrays den wert "z" zu.
Ich benutze also die Referenz-Kopie um auf den Wert zuzugreifen bzw ihn zu überschreiben.

Bei strArray2 überschreibe ich die kopierte Referenz/Deskriptor mit der Speicheradresse eines neuen Arrays. Diese kopierte Referenz hat aber dann keinen Einfluss auf die eigendliche Referenz die unter main in strArray2 gespeichert wird --> Thema Sichtbarkeit von Variablen.

So wie ich das verstehe ist es also eigendlich call by value.
Es wird also einfach eine Kopie der Variable erstellt die den selben Inhalt (Wert bzw value) hat.
Bei primitiven Datentypen (int, char, long...) ist das dementsprechend eine Zahl bzw ein Symbol...

Bei Objekten wird aber immer der Deskriptor/Referenz als Inhalt/Wert/value in der Variable gespeichert.
Dieser Inhalt wird wie bei den primitiven Datentypen auch einfach kopiert und ist innerhalb des Sichbarkeitsbereichs gültig.
Die Referenz kann hier überschrieben werden, geschieht dies nicht zeigt sie auf dieselbe Speicheradresse wie vorher auch.

Ich hoffe ich habe es nun richtig verstanden und konnte villt für Verständnis sorgen und nicht noch für mehr Verwirrung :D
 
Perfekt, danke auch wenn ich nicht TE war.^^
Wieder was gelernt.
Ergänzung ()

Redirion schrieb:
und wo soll die Kopie sein? Ist es nicht viel mehr so, dass du diese Kopie erst durch den new Operator verursachst?

Bei jedem Aufruf wird die Methode mit all ihren Variablen auf dem Stack oben neu angelegt. (Das nennt man ...Satz, fällt mir grad nicht mehr ein-.-).
Dabei werden nicht nur die neu definierten Variablen in der Methode sondern vermutlich auch die Kopieen der übergebenen Parameter mit angelegt.
 
Bei einer Referenzkopie auf den gleichen Speicherbereich kann man doch nicht von Call by Value sprechen. Der Wert selbst wird nicht kopiert. Wir arbeiten hier doch nur mit Referenzen.

Das ist auch unabhängig von meinem Einwurf, dass man mit "new" immer eine neue Referenz erhält.
 
ok das klingt einleuchtend.

Für Anfänger ist es mit call by reference sicher etwas verständlicher und deswegen auch von der Begrifflichkeit vom ersten Semester von damals bei mir noch so kleben geblieben.
 
Das Beispiel von Dark Power veranschaulicht das doch recht gut.
Auch das Galileo Open Book Java Insel erklärt es kurz:
http://openbook.galileocomputing.de/javainsel/javainsel_03_007.html#dodtp2a7806e2-5c81-4e05-9344-9f559eba6e72

Wertübergabe und Referenzübergabe per »Call by Value«

Primitive Variablen werden immer per Wert kopiert (engl. Call by Value). Das Gleiche gilt für Referenzen. Daher hat auch die folgende statische Methode keine Nebenwirkungen:

Listing 3.16: JavaIsAlwaysCallByValue.java

Code:
import java.awt.Point;

public class JavaIsAlwaysCallByValue
{
  static void clear( Point p )
  {
    p = new Point();
  }

  public static void main( String[] args )
  {
    Point p = new Point( 10, 20 );
    clear( p );
    System.out.println( p );  // java.awt.Point[x=10,y=20]
  }
}
Nach der Zuweisung in der clear()-Methode referenziert die Parameter-Variable p ein anderes Punkt-Objekt, und der der Methode übergebene Verweis geht damit verloren. Diese Änderung wird nach außen hin natürlich nicht sichtbar, denn das Methoden-p ist ja nur ein temporärer alternativer Name für das main-p; eine Neuzuweisung an das Methoden-p ändert nicht das main-p, was bedeutet, dass der Aufrufer von clear() kein neues Objekt unter sich hat.

Und auch in den Oracle Tutorials steht da etwas zu:
http://docs.oracle.com/javase/tutorial/java/javaOO/arguments.html

Reference data type parameters, such as objects, are also passed into methods by value. This means that when the method returns, the passed-in reference still references the same object as before. However, the values of the object's fields can be changed in the method, if they have the proper access level.

For example, consider a method in an arbitrary class that moves Circle objects:

Code:
public void moveCircle(Circle circle, int deltaX, int deltaY) {
    // code to move origin of circle to x+deltaX, y+deltaY
    circle.setX(circle.getX() + deltaX);
    circle.setY(circle.getY() + deltaY);
        
    // code to assign a new reference to circle
    circle = new Circle(0, 0);
}
Let the method be invoked with these arguments:

Code:
moveCircle(myCircle, 23, 56)
Inside the method, circle initially refers to myCircle. The method changes the x and y coordinates of the object that circle references (i.e., myCircle) by 23 and 56, respectively. These changes will persist when the method returns. Then circle is assigned a reference to a new Circle object with x = y = 0. This reassignment has no permanence, however, because the reference was passed in by value and cannot change. Within the method, the object pointed to by circle has changed, but, when the method returns, myCircle still references the same Circle object as before the method was called.
 
Zurück
Oben