Call by value / Call by reference

Werden Parameter in Java „by value“ oder „by reference“ übergeben?

In den Weiten des Internet finden sich zu diesem Thema die merkwürdigsten Aussagen, u.a. die, in Java würden Primitivtypen „by value“, Objekte hingegen „by reference“ übergeben. Das ist Unsinn: Parameterübergaben erfolgen in Java immer „by value“, d.h. es wird eine Kopie des übergebenen Wertes dem formalen Parameter zugewiesen. Diese Aussage bedarf allerdings einer zusätzlichen Erläuterung, schließlich hat die teilweise existierende Verwirrung ja ihren Grund.

Zunächst einmal: Zugriff auf Objekte hat man in Java ausschließlich über Referenzen. Variablen enthalten nie Objekte, sondern – Primitivtypen mal außen vor gelassen – immer nur Referenzen, Methoden liefern keine Objekte, sondern Referenzen. Wenn also einer Methode „ein Objekt als Parameter übergeben“ wird, wird in Wirklichkeit eine Referenz übergeben.

Und damit wird die obige Aussage verständlich: Wenn der übergebene Wert eine Referenz ist, wird eben eine Kopie der Referenz erzeugt und dem formalen Parameter zugewiesen! Diese Kopie verweist natürlich auf genau das Objekt, auf das auch die Original-Referenz verweist.

Wenn man das verstanden hat, wird auch klar, warum eine Zuweisung an den formalen Parameter keine Wirkung nach außen erzeugt: Dieser referenziert danach schlicht ein anderes Objekt. Hingegen können Methodenaufrufe oder schreibende Attributzugriffe über den formalen Parameter sehr wohl den Zustand des referenzierten Objekts verändern! Denn bei solchen Zugriffen erfolgt ja eine Dereferenzierung, man greift also auf das referenzierte Objekt zu! Und damit werden solche Veränderungen auch außerhalb der Methode wirksam.

Man sollte sich – gerade wenn man Erfahrung in Sprachen wie C hat – klar machen, dass das eben nicht dem Verhalten entspricht, welches man erwarten müsste, wenn es stimmte, dass Objekte „by reference“ übergeben würden!

Und wie ist das bei Arrays?

Arrays sind in Java Objekte. Eine Variable, welche „einen Array enthält“, enthält in Wirklichkeit also lediglich eine Referenz auf ein solches Objekt. Wird an eine Methode „ein Array übergeben“, wird wie oben beschrieben, die Referenz „by value“ übergeben und der methodenlokalen Variablen zugewiesen, wie bei jedem anderen Objekt auch.

Wenn man nun über eine Array-Referenz auf ein Element des Arrays zuzugreift, erfolgt ebenso eine Dereferenzierung, wie beim Zugriff auf eine Methode oder ein Attribut eines „normalen“ Objekts. Man kann sich die Elemente eines Arrays als Attribute des Array-Exemlars vor-stellen, nur dass die Selektion nicht über den Punkt (= die Dereferenzierung), gefolgt vom Att-ributnamen passiert, sondern mit einer speziellen Syntax.

Und so ist auch das Verhalten völlig analog dem anderer Objekte: Erfolgt innerhalb einer Methode eine Zuweisung an ein Element eines übergebenen Arrays, wirkt sich diese sehr wohl nach außen aus, denn beim Elementzugriff wird dereferenziert, und das referenzierte Array ist ja innen wie außen dasselbe. Eine Zuweisung an die lokale Variable selbst hingegen bleibt natürlich nach außen wirkungslos.

public class ArrayTest {
    public static void main(String[] args) {
        String[] array = { "eins", "zwei" };

        test(array);
        for (String elem : array) {
            System.out.println(elem);
        }

        swap(array);
        for (String elem : array) {
            System.out.println(elem);
        }
    }

    static void test(String[] array) {
        array = new String[] { "Alles", "ganz", "anders" };
    }

    static void swap(String[] array) {
        String temp = array[0];
        array[0] = array[1];
        array[1] = temp;
    }
}

Die Ausgabe lautet:

eins
zwei
zwei
eins