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

DefconDev

Commander
Registriert
Jan. 2008
Beiträge
2.538
Ich möchte mich eigentlich absichern.

In Java gibt es nur call by value, wenn eine Methode eine globale Variable(primitiver Datentyp) übergeben bekommt, kann die lokale Variable die globale nicht verändern, weil es nur eine Kopie des Wertes ist und nicht der Wert selber.

Bei Objekten verhält sich das ähnlich, meine ich jedenfalls, hier wird nicht die Referenz(Zeiger) übergeben sondern auch nur die Kopie der Referenz. Damit kann ich dann mit zwei Zeigern auf das selbe Objekt bzw. Speicheradresse zeigen. Und mit der kopierten Referenz kann ich dann auch globale Änderungen vornehmen, also Eigenschaften des Objekts.

Was ich mich nun Frage, im Vergleich zu anderen Programmiersprachen, was ist der Sinn oder der Unterschied defacto wenn ich eine Kopie eines Zeigers erstelle und übergebe oder nur den original Zeiger!?

Wenn ich mit dieser Erklärung auch falsch liege, dann tut es mir leid, aber im Inet wird da einiges durcheinander geworfen.

Ich persönlich würde es in zwei Sachen unterteilen

call by value
und
call by copie reference
 
Zuletzt bearbeitet:
Der Punkt ist, dass die JVM keinen Unterschied macht, ob es ein Objekt (Zeiger) oder ein primitiver Typ ist, deshalb wird beides schlicht kopiert bei der Übergabe. Und ja, deshalb ist die Objekt-Übergabe technisch weder echtes call-by-value, noch echtes call-by-reference.
Technisch ist es also call-by-value. Allerdings würde bei echtem call-by-value ja eine Kopie des Objekts erzeugt werden müssen, und naja, das ist eine Wissenschaft für sich...

Andere "Bezeichnungen" für die Übergabe von Objekten haben auch schon andere "eingeführt", die in eine ähnliche Richtung gehen.
Finde leider die Beispie-Seite dazu nicht, reiche sie aber nach...

Andere schreiben auch: "Object references are passed by value"
 
@1668mib
Es ist echtes call-by-value, weil der Wert, der in der Objektvariable steht, kopiert wird. Dass darin eben nur die Referenz steht spielt keine Rolle.

Was die Frage angeht, macht es keinen Unterschied, ob die original Referenz oder eine Kopie davon übergeben wird, da ja schließlich beide auf das selbe Objekt an der selben Speicheradresse zeigen.
 
Es macht sehr wohl einen Unterschied, was übergeben wird...

Der Aufrufer bekommt hier nichts mit - so wie man es bei Call-by-Value auch erwartet.
Code:
void doSomething(String s)
{
  s = new String("Hallo");
}

Dass es aus technischer Sicht echtes Call-by-Value ist, habe ich angedeutet. Aber der Programmierer betrachtet die Sprache nicht technisch. Es ist leider eine Ironie, dass die Sprache, die uns von der Zeigerarithmetik befreit hat, uns dann bei der Parameterübergabe damit doch wieder konfrontiert...
 
Was ich mich nun Frage, im Vergleich zu anderen Programmiersprachen, was ist der Sinn oder der Unterschied defacto wenn ich eine Kopie eines Zeigers erstelle und übergebe oder nur den original Zeiger!?

Ob du vorher selbst eine Kopie erzeugst oder nicht, ist völlig egal. Du hast dann halt eine Kopie mehr ;)

...hier wird nicht die Referenz(Zeiger) übergeben sondern auch nur die Kopie der Referenz
"by reference" drückt doch letztlich nur aus, dass man eben nicht mit einer Kopie der eigentlichen Nutzdaten, sondern mit dem Original arbeitet. Dagegen sagt "by value" aus, dass eine Kopie der Nutzdaten erzeugt wird, Änderungen also nicht auf das Original durchschlagen.

Die Referenz (also der Verweis auf das eigentliche Objekt) ist hierbei nur Mittel zum Zweck. Irgendwie muß ja auf die Nutzdaten verwiesen werden, wenn sie schon nicht kopiert werden.
 
1668mib schrieb:
Es macht sehr wohl einen Unterschied, was übergeben wird...

Nein eben nicht, da sowohl bei der Referenz, als auch bei dessen Kopie weiterhin auf den selben Speicherbereich gezeigt wird. Es fängt an einen Unterschied zu machen, wenn man mit call-by-reference arbeitet, wie in C/C++ mit dem &-Operator des formalen Parameters.
 
KillerCow schrieb:
Ob du vorher selbst eine Kopie erzeugst oder nicht, ist völlig egal. Du hast dann halt eine Kopie mehr ;)
Das erklärt aber nicht, warum kopiert wird.
Der Grund dürfte, wie ich schon schrieb, der sein, dass man die Implementierung der Parameterübergabe einfacher machen wollte, in dem alle Typen dabei gleich behandelt werden. Egal was für ein Typ, die JVM kopiert den Parameter. Bei einem Objekt ist der Parameter intern eine Speicherreferenz.

"by reference" drückt doch letztlich nur aus, dass man eben nicht mit einer Kopie der eigentlichen Nutzdaten, sondern mit dem Original arbeitet. Dagegen sagt "by value" aus, dass eine Kopie der Nutzdaten erzeugt wird, Änderungen also nicht auf das Original durchschlagen.
Mit dem Original von was? Vom Zeiger oder vom Objekt...
Denn bei Java arbeitet man bei Objekt-Übergabe mit dem Original was die Nutzdaten angeht, aber nicht mit dem Original was den Zeiger angeht. Das ist ein Unterschied. Wie würdest du Java dann einordnen?

Für mich hört es sich so an, als würdest du sagen, dass Java
- primitive Typen mit call-by-value übergibt
- Objekte mit call-by-reference übergibt

Habe ich dich richtig verstanden?

@kinglouy:
Natürlich macht es bei diesem Code einen Unterschied, ob die Referenz als Kopie übergeben wird oder nicht:
Code:
public class Besserwisser
{

  static void kinglouySuperHack(String wert)
  {
    wert = new String("immer recht");
  }

  public static void main(String[] args)
  {
    String wert = "nicht recht";
    kinglouySuperHack(wert);
    System.out.println("kinglouy hat " + wert);
  }

}
 
Zuletzt bearbeitet:
Einen String zu verwenden, ist meiner Meinung nach ein ganz schlechtes Beispiel. Der String nimmt doch als Unveränderliche eine besondere Rolle ein. Bei einem String ist es genau wie mit anderen primitiven Datentypen. Es ist hier immer "call by value".
 
@1668mib
Willst du mich mit dem Codeschnipsel für blöd verkaufen? Du kannst in Java nichts produzieren, wo es einen Unterschied macht, ob Referenz oder dessen Kopie, da es immer identisch ist.
 
kinglouy schrieb:
Nein eben nicht, da sowohl bei der Referenz, als auch bei dessen Kopie weiterhin auf den selben Speicherbereich gezeigt wird.

Hast du sein Codebeispiel nicht gesehen? Die Original-Referenz (in Java nicht möglich) ließe sich umbiegen (z.B. auf null oder new), eine kopierte Referenz hingegen hat außerhalb der Methode keine Relevanz. Klar zeigen beide auf das selbe Objekt, aber das ist ja nicht alles...

Wenn man im Hinterkopf behält, dass man eigentlich keine Objekte an Methoden übergibt, sondern Referenzen auf Objekte, dann sollte call-by-value nachvollziehbar sein.
 
Ich hab das Beispiel nochmal verbessert, so wie ich es weiter oben in der Kurzfassung schon hatte. Statt der impliziten String-Erstellung ein "new String(...)" reingemacht. Ich glaube carom hat es dennoch verstanden.

@kinglouy: Du sagst, es würde keinen Unterschied machen, ob Java die Referenzen kopiert oder nicht. Genau das habe ich widerlegt.

@Redirion: Es spielt gar keine Rolle, ob es ein veränderbarer oder unveränderbarer Typ ist. Unveränderbare Typen mit primitiven Typen gleichzusetzen ist absurd... Du kannst auch gerne String durch StringBuilder ersetzen:

Code:
public class Besserwisser
{

  static void kinglouySuperHack(StringBuilder wert)
  {
    wert = new StringBuilder("immer recht");
  }

  public static void main(String[] args)
  {
    StringBuilder wert = new StringBuilder("nicht recht");
    kinglouySuperHack(wert);
    System.out.println("kinglouy hat " + wert);
  }

}
 
Zuletzt bearbeitet: (Typo und Ergänzung, StringBuilder-Beispiel hinzugefügt)
Du kannst das nicht dadurch widerlegen, dass du die Referenz einfach überschreibst... in Java gibt es nur call-by-value, deswegen ist das Beispiel auch eigentlich völlig hirnrissig. Es wird hier eine Referenz auf das Objekt übergeben, nicht auf die Objektvariable, in der die tatsächliche Referenz steht. Nur im letzteren Fall würde es einen Unterschied machen, der ist in Java aber nicht möglich.


Ich habe übrigens KEIN Verständnisproblem mit der Parameterübergabe in Java. Hades85 fragte allerdings, ob es bei der ÜBERGABE einen Unterschied macht, ob es die originale oder kopierte Referenz ist. Er fragte nicht, ob es einen Unterschied macht, wie man in der aufgerufenen Methode damit arbeitet.
 
Zuletzt bearbeitet:
Ich habe dir nirgends widersprochen, dass die Parameterübergabe (technisch) call-by-value ist.
Du sagtest aber, dass es keinen Unterschied machen würde, ob die Referenz nun kopiert wird bei der Übergabe oder nicht. Genau das habe ich kritisiert und widerlegt.

Der Punkt ist doch: Würde die JVM die Referenzen nicht kopieren, wäre es ja call-by-reference.
Deine Begründung, dass es in Java nur call-by-vaue gibt, ist so stichhaltig wie "Es macht keinen Unterschied weil es nicht geht."
 
Zuletzt bearbeitet:
Das erklärt aber nicht, warum kopiert wird.
Ich bin kein Spezialist in Sachen Compiler etc, aber kopiert wird wohl deshalb, weil eine Methode (oder Funktion) einen eigenen Gültigkeitsbereich hat. Die übergebenen Parameter werden ja in nur lokal gültige Variablen verfrachtet. Deshalb wohl die Kopie.

Für mich hört es sich so an, als würdest du sagen, dass Java
- primitive Typen mit call-by-value übergibt
- Objekte mit call-by-reference übergibt
Ich habe keine Ahnung von Java (bzw. den Innereien der JVM), daher habe ich garnichts in der Richtung gemeint oder meinen wollen.
 
1668mib schrieb:
Der Punkt ist doch: Würde die JVM die Referenzen nicht kopieren, wäre es ja call-by-reference.
Deine Begründung, dass es in Java nur call-by-vaue gibt, ist so stichhaltig wie "Es macht keinen Unterschied weil es nicht geht."

Ich werde das Gefühl nicht los, dass wir einfach nur aneinander vorbeireden.

Mir ging es darum: Kein Unterschied, da der kopierte Wert des Parameters immer der originalen Referenz entspricht, nämlich eine Speicheradresse, die bis zum Überschreiben identisch ist. Wichtig ist, dass hier keine Referenz des tatsächlichen Parameters bei der Übergabe gemeint ist, sondern nur des Objekts. Sogesehen wird hier eigentlich schon die Referenz auf das Objekt im Original übergeben. Da aber keine Referenzierung der Objektvariable besteht, lässt sich diese halt einfach überschreiben. Bei call-by-reference würde eben eine Referenz auf den übergebenen Parameter existieren. Das ist genau das was bei Verwendung des &-Operators bei der Parameterdeklaration in C/C++ erreicht wird.
 
Zuletzt bearbeitet:
Vermutlich ;-)
Ich vermute, dir war nicht klar in #4, dass ich mit "Unterschied" nicht CbV vs CbR sondern "Kopie der Referenz übergeben" vs "Adresse der Referenz übergeben".

Edit:
Ja, gut, und ich habe nicht ganz rausgelesen, dass du es nur darauf bezogst, auch wenn du geschrieben hast "auf die Frage" ;-) Der Text war doch etwas länger davor :-)
 
Zuletzt bearbeitet:
Ich verstehe nicht, was es veranschaulichen sollte, wenn mit dem new Operator ein neues Objekt erzeugt wird. Hier wird doch immer ein neuer Speicherbereich belegt, nie jedoch der alte Überschrieben.

Hier mal ein anderes Beispiel mit call by reference:
Code:
private static void changeMyList(List<Integer> myList) {
		myList.remove(0);
		myList.add(5);
}

public static void main(String[] args) {
                List<Integer> test = new ArrayList<Integer>();
		test.add(2);
		test.add(4);
		System.out.println("Vorher");
		for(Integer i : test) {
			System.out.println(i);
		}
		changeMyList(test);
		System.out.println("Nachher");
		for(Integer i : test) {
			System.out.println(i);
		}
}
Ausgabe:
Vorher
2
4
Nachher
4
5

Ganz ohne Methodenparameter dargelegt, dass es überhaupt nicht relevant ist, an welcher Stelle eine neue Referenz erzeugt wird(auch mal mit Strings und expliziter Nutzung von new):
Code:
public static void main(String[] args) {
		String test = new String("Test");
		String ref = test;
		test = new String("Bla");
		
		System.out.println(test);
		System.out.println(ref);
}
Ausgabe:
Bla
test

und nochmal mit einer Liste:

Code:
public static void main(String[] args) {
	List<Integer> test = new ArrayList<Integer>();
	test.add(2);
	test.add(4);
	List<Integer> bla = test;
	test = new ArrayList<Integer>();
	test.add(3);
		
	System.out.println("Alt:");
	for(Integer i : bla) {
		System.out.println(i);
	}
	System.out.println("Neu:");
	for(Integer i : test) {
		System.out.println(i);
	}
}

Ausgabe:
Alt:
2
4
Neu:
3
 
Zuletzt bearbeitet:
Hier mal ein anderes Beispiel mit call by reference:
Autsch.

Wäre es call-by-reference, könnte man die Referenzen ja ändern... Java ist immer call-by-value. Auch wenn es sich bei Objekten ein wenig wie call-by-reference anfühlt.

Das Überschreiben des Speichers ist doch genau das, was bei call-by-reference möglich sein sollte! Ist es aber in Java nicht.
Wenn ich eine Variable mit dem Wert 5 mit call-by-reference übergeben würde, und ich weise dem Parameter dann 6 zu, dann ist die 6 auch beim Aufrufer. Der Speicherbereich wurde also überschrieben. Wenn ich einen Zeiger übergebe call-by-reference übergebe, und ich den Parameter ändere, dann bekommt das der Aufrufer mit.
 
Zuletzt bearbeitet:
Wenn ich mich nicht irre wird doch bei Objekten grundsätzlich nur der Deskriptor im Stack gespeichert in diesem steht wiederum ein Verweis/Zeiger, der Datentyp und die Größe(abhängig von den Feldern etc). Der Zeiger markiert die Startadresse auf dem Heap wo das eigendliche Objekt gespeichert ist.
Wenn ich also in der Variable a ein Objekt erstelle wird dort IMMER nur der Deskriptor reingespeichert, mehr nicht.
Demnach wäre das anhand des oben genannten Beispiels also ein Seiteneffekt und es müsste "immer recht" rauskommen.

EDIT:
Redirion schrieb:
Ich verstehe nicht, was es veranschaulichen sollte, wenn mit dem new Operator ein neues Objekt erzeugt wird. Hier wird doch immer ein neuer Speicherbereich belegt, nie jedoch der alte Überschrieben.
Eben
 
Zuletzt bearbeitet:
1668mib schrieb:
Wäre es call-by-reference, könnte man die Referenzen ja ändern...

Das habe ich doch bei meinem Beispiel durch Manipulation der Liste getan.
Bitte beweise mir das Gegenteil, ohne mit dem new Operator wieder eine neue Referenz zu erzeugen.
Ich habe bereits nachgewiesen, dass es völlig unabhängig von Methodenparametern immer den Bezug zur alten Referenz verliert. Siehe meine Beispiele mit der ausschließlichen Nutzung der main-Methode. Hier gibt es kein "call by irgendwas", da hier überhaupt keine Parameter genutzt werden.
 
Zurück
Oben