Sie sind auf Seite 1von 24

08-Parameter, lokale Variablen und Überladen

Der Zustand eines Objekts besteht in den Daten, die es in seinen Instanzvariablen hält.
Instanzvariablen behalten ihren Wert bis sie explizit geändert werden oder bis das Objekt
zerstört wird.

Eine Objektmethode arbeitet häufig mit Werten, die nicht Teil des Objektzustands sind. Diese
Werte werden in lokalen Variablen und Parametern der Methoden gehalten. Dieses Kapitel
erörtert wie diese deklariert und verwendet werden.

Themen:

 Formale und aktuelle Parameter


 Gültigkeitsbereich von Parametern
 Lokale Variablen
 Gültigkeitsbereich von lokalen Variablen
 Überladen von Methoden
 Signatur einer Methode

(Rückblick:) Was ist ein Parameter einer Methode?

Ein Parameter ist ein Wert, der an eine aufgerufene Methode übergeben wird.

Parameter
Hier ist ein Beispiel einer Methode, die Parameter verwendet.

class Konto
{
. . . .
private int kontostand;

. . . .
void verarbeiteEinzahlung( int betrag )
{
kontostand = kontostand + betrag ;
}

Der Parameter betrag wird vom Aufrufer verwendet, um einen Wert an die Methode zu
schicken. Das wird "einen Wert an die Methode übergeben" genannt. Hier ist ein Teil einer
main() Methode, die den Parameter verwendet, um einen Wert an die Methode
verarbeiteEinzahlung() zu übergeben:

class KontoTester
{
public static void main( String[] args )
{
Konto bobsKonto = new Konto( "999", "Bob", 100 );
bobsKonto.verarbeiteEinzahlung( 200 );

1
// . . . .
}
}

Wenn die Anweisung

bobsKonto.verarbeiteEinzahlung( 200 );

ausgeführt wird, erhält der Parameter betrag den Wert 200. Wenn die Methode
verarbeiteEinzahlung() jetzt ausgeführt wird, wird dieser Wert der Instanzvariablen
kontostand des Objekts hinzugefügt.:

kontostand = kontostand + betrag ;

Dann wird die Methode beendet und die Kontrolle kehrt zurück zu main(). Der kontostand
von bobsKonto wird sich geändert haben.

1. Wird die Instanzvariable kontostand einen dauerhaften Wert enthalten?


2. Wird der Parameter betrag einen dauerhaften Wert enthalten?

1. Wird die Instanzvariable kontostand einen dauerhaften Wert enthalten?


o Ja — kontostand ist Teil des Zustands des Objekts und wird den Wert
solange enthalten wie das Objekt existiert.
2. Wird der Parameter betrag einen dauerhaften Wert enthalten?
o Nein — betrag wird nur verwendet, um einen Wert an eine Methode zu
übergeben. Er hat keine dauerhafte Existenz.

Formale und aktuelle Parameter


Die folgenden Definitionen sind nützlich:

 formaler Parameter — Bezeichner, der in einer Methode verwendet wird, um einen


Wert aufzunehmen, der an die Methode vom Aufrufer übergeben wird.
o Zum Beispiel ist betrag ein formaler Parameter von
verarbeiteEinzahlung()
 aktueller Parameter — der tatsächliche Wert, der an die Methode durch den
Aufrufer übergeben wird.
o Zum Beispiel werden die 200, die beim Aufruf an die Methode
verarbeiteEinzahlung() übergeben werden, als aktueller Parameter
bezeichnet.

Wenn eine Methode aufgerufen wird, dann wird der formale Parameter temporär an den
aktuellen Parameter "gebunden". Die Methode verwendet den formalen Parameter für den
aktuellen Wert, den der Aufrufer übergeben hat.

Zum Beispiel verwendet hier die Methode verarbeiteEinzahlung() den formalen


Parameter betrag an Stelle des aktuellen Werts , der im Methodenaufruf übergeben wurde:

2
kontostand = kontostand + betrag ;

Hinweis: Formale Parameter sind nur solange wie ihre Methode aktiv ist an einen aktuellen
Wert gebunden. Wenn eine Methode zum Aufrufer zurückkehrt, enthält der formale
Parameter keinen Wert mehr. Parameter können nicht dazu verwendet werden, um den
Zustand eines Objekts zu speichern.

Was wird verwendet, um den Zustand eines Objekts zu speichern?

Instanzvariablen werden verwendet, um den Zustand eines Objekts zu speichern. Sie werden
solange das Objekt existiert dessen Werte enthalten.

Parameter sind nur in ihrer eigenen Methode sichtbar


Die formalen Parameter einer Methode können nur von ihren eigenen Anweisungen
"gesehen" werden. Das heißt, wenn eine Methode versucht einen Parameter einer anderen
Methode zu verwenden, wird der Compiler einen Syntaxfehler finden.

Hier ist noch einmal die Konto-Klasse, dieses Mal mit einer neuen Definition der anzeigen()
Methode.

class Konto
{
. . . .
private int kontostand;

. . . .
void verarbeiteEinzahlung( int betrag )
{
kontostand = kontostand + betrag ;
}

// geänderte anzeigen() Methode


void anzeigen()
{
System.out.println( kontostand + "\t" + betrag );
}

Ist diese anzeigen() Methode korrekt?

Nein. Der formale Parameter betrag gehört zur Methode verarbeiteEinzahlung(). Er


kann nicht von einer anderen Methode verwendet werden.

Gültigkeitsbereich eines formalen Parameters


Der Gültigkeitsbereich eines formalen Parameters ist der Codeabschnitt, der den
Parameter "sehen" (verwenden) kann. Der Gültigkeitsbereich des formalen Parameters ist der
Körper der Methode. Zum Beispiel ist der Gültigkeitsbereich von betrag der Körper seiner
Methode:
3
class Konto
{
. . . .
private int kontostand;

. . . .
void verarbeiteEinzahlung( int betrag )
{ // Gültigkeitsbereich von betrag beginnt hier
kontostand = kontostand + betrag ;
} // Gültigkeitsbereich von betrag endet hier

// geänderte anzeigen() Methode


void anzeigen()
{
System.out.println( kontostand + "\t" + betrag ); // Syntaxfehler
}

Die anzeigen() Methode kann betrag nicht "sehen", da sie außerhalb des Gültigkeitsbereich
von betrag ist. Der Compiler wird das geänderte Programm nicht kompilieren.

Kann die anzeigen() Methode die Instanzvariablen des Objekts (wie kontostand) sehen?

Ja.

Was Anweisungen sehen können


Anweisungen einer Methode (so wie anzeigen() ) können die Instanzvariablen und die
anderen Methoden des Objekts sehen. Aber sie können die Parameter (und lokalen Variablen)
anderer Methoden nicht sehen. Hier ist ein weiterer Blick auf die Konto-Klasse:

class Konto
{
. . . .
private int kontostand;

. . . .
void verarbeiteEinzahlung( int betrag )
{ // Gültigkeitsbereich von betrag beginnt hier
kontostand = kontostand + betrag ;
} // Gültigkeitsbereich von betrag endet hier

void verarbeiteAuszahlung( int betrag )


{ // Gültigkeitsbereich von betrag beginnt hier
int gebuehr;

inkrementZaehler();
if ( kontostand < 100000 )
gebuehr = 15;
else
gebuehr = 0;

kontostand = kontostand - betrag - gebuehr ;


} // Gültigkeitsbereich von betrag endet hier

4
}

Zwei Methoden verwenden den gleichen Bezeichner betrag für zwei verschiedene formale
Parameter. Jede Methode hat ihre eigenen formalen Parameter, vollkommen unabhängig von
anderen Methoden. Das ist OK. Der Gültigkeitsbereich der zwei Parameter überschneidet sich
nicht, so dass die Anweisungen der einen Methode die formalen Parameter der anderen
Methode nicht "sehen" können.

Zum Beispiel kann die Anweisung

kontostand = kontostand - betrag - gebuehr ;

in der zweiten Methode nur den formalen Parameter ihrer Methode sehen — der
Gültigkeitsbereich des formalen Parameters der anderen Methode enthält diese Anweisung
nicht.

Können die zwei formalen Parameter, die beide betrag heißen, unterschiedliche Datentypen
haben. (Kann zum Beispiel der eine ein int und der andere ein long sein)?

Ja — da jeder nur im Körper seiner Methode sichtbar ist, kann er als ein beliebiger Typ
deklariert sein.

Verspiegeltes Glas
Es ist manchmal nützlich sich
Methoden visuell als eine Box aus
verspiegelten Glas vorzustellen. Eine
Methode kann lokale Variablen und
Parameter, die in der Box sind,
sehen. Eine Methode kann durch das
Glas, das sie umgibt, hindurchsehen.
Aber kein Außenstehender kann in
die Box hineinsehen.

Die Abbildung zeigt die verspiegelte


Box für das Beispielprogramm. Die
roten Linien zeigen das verspiegelte
Glas, das jede Methode umgibt. Die
Methode kann aus der Box
herausschauen. Zum Beispiel kann
jede Methode die Instanzvariable
kontostand sehen, aber andere
Methoden können von außerhalb
nicht in die verspiegelte Box
hineinsehen.

In verarbeiteEinzahlung() kann
die Anweisung die Variable
kontostand, die als Instanzvariable
5
deklariert ist, "sehen". Sie kann ebenfalls den Parameter betrag innerhalb der Box sehen.

Die Methode zeigeGebuehr() ist fehlerhaft, da sie eine Anweisung enthält, die versucht in
die Box von verarbeiteAuszahlung() hineinzusehen.

Die Namen der formalen Parameter (wie betrag) und lokalen Variablen (wie gebuehr) sind
innerhalb der Glasbox sichtbar. Aber dem Außenstehenden ist nur die Anzahl und der Typ
der für jede Methode erforderlichen Parameter bekannt.

Ist der Name einer Methode innerhalb oder außerhalb der Glasbox?

Außerhalb — so dass er von anderen Methoden "gesehen" und verwendet werden kann.

Zuweisung eines Werts an einen Parameter


Innerhalb des Körpers einer Methode kann ein Parameter genau wie eine Variable verwendet
werden. Er kann in arithmetischen Ausdrücken, in Zuweisungsanweisungen usw. verwendet
werden.

Aber, eine Änderung des Parameters hat keinen Effekt außerhalb des Methodenkörpers. Ein
Parameter ist eine "lokale Kopie", egal welchen Wert der Aufrufer an die Methode übergeben
hat. Jede Änderung, die an ihm vorgenommen wird, betrifft nur diese lokale Kopie. Zum
Beispiel:

class Konto
{
. . . .
private int kontostand;

void verarbeiteAuszahlung( int betrag )


{
int gebuehr;
if ( kontostand < 100000 )
gebuehr = 15;
else
gebuehr = 0;
kontostand = kontostand - betrag - gebuehr ;

// Ändern der lokalen Kopie "betrag"


betrag = 0 ;
}
}

class KontoTester
{
public static void main ( String[] args )
{
Konto aktuell;
int scheck = 5000;
aktuell = new Konto( "123-345-99",
"Wanda Fish", 100000 );

// gibt "5000" aus


System.out.println( "Scheck: " + scheck );
6
// Aufruf von verarbeiteAuszahlung() mit dem Wert 5000
aktuell.verarbeiteAuszahlung( scheck );

// gibt "5000" aus — "scheck" wurde nicht geändert


System.out.println( "Scheck: " + scheck );

}
}

Der formale Parameter betrag ist der Name, der von verarbeiteAuszahlung() für den
Wert 5000 verwendet wird, der ihm vom Aufrufer übergeben wurde. Die Methode kann den
Wert, der in betrag enthalten ist ändern, aber das hat keine Auswirkung auf die Variable des
Aufrufers.

Dieses Thema wird in den folgenden Kapiteln weiter erörtert. Betrachten Sie für jetzt einen
Parameter als eine "Einbahnstraßen"-Mitteilung, die der Aufrufer verwendet, um Werte an
die Methode zu übermitteln.

Angenommen, dass die main() Methode in dem Beispiel folgendes tat:

aktuell.verarbeiteAuszahlung( 7000 ); // verarbeiteAuszahlung()


// mit dem Wert 7000 aufrufen

Ist das OK? Was würde die Anweisung

betrag = 0 ; // lokale Kopie von "betrag" ändern

in der Methode tun?

Ist das OK? Sicherlich. Der Aufrufer hat den Wert 7000 an die Methode geschickt.

Was bewirkt betrag = 0 in der Methode? Das ändert die lokale Kopie des Wertes von 7000
in 0.

Aber das hat keine Auswirkung auf den Aufrufer. Betrachten Sie noch einmal den Aufrufer
— der Wert 7000 ist in keiner Variable enthalten. Es gibt nichts, was die aufgerufene
Methode ändern könnte.

Lokale Variablen
Eine lokale Variable ist eine Variable, die innerhalb des Körpers einer Methode deklariert ist.
Sie kann nur von den Anweisungen innerhalb der Methode, die nach ihrer Deklaration
kommen, gesehen werden. (Ihr Gültigkeitsbereich beginnt mit ihrer Deklaration und endet
mit dem Ende der Methode.) Zum Beispiel ist gebuehr von verarbeiteAuszahlung() eine
lokale Variable:

class Konto
{
. . . .
private int kontostand;

7
void verarbeiteAuszahlung( int betrag )
{
int gebuehr; // Gültigkeitsbereich von gebuehr beginnt hier

inkrementZaehler();
if ( kontostand < 100000 )
gebuehr = 15;
else
gebuehr = 0;

kontostand = kontostand - betrag - gebuehr ;


} // Gültigkeitsbereich von gebuehr endet hier

Die lokale Variable gebuehr wird in einer typischen Weise verwendet. Sie wird verwendet,
um einen temporären Wert zu halten, während etwas berechnet wird. Lokale Variablen
werden nicht verwendet, um die permanente Werte des Zustands eines Objekts zu halten. Sie
enthalten einen Wert nur während der kurzen Zeitspanne in der die Methode aktiv ist.

Ist der Gültigkeitsbereich einer lokalen Variablen immer der gesamte Körper einer Methode?

Nein — nur ab der Stelle, an der die Variable deklariert wurde bis zum Ende des
Methodenkörpers. Manchmal wird eine lokale Variable mitten im Körper deklariert, nahe der
Anweisung, die sie das erste Mal verwendet.

Sie können nicht den gleichen Namen in demselben


Gültigkeitsbereich verwenden
Es ist ein Fehler den gleichen Bezeichner zweimal in demselben Gültigkeitsbereich zu
verwenden. Zum Beispiel ist das Folgende ein Fehler:

class Konto
{
. . . .
private int kontostand;

void verarbeiteAuszahlung( int betrag )


{
int gebuehr, betrag;

inkrementZaehler();
if ( kontostand < 100000 )
gebuehr = 15;
else
gebuehr = 0;

kontostand = kontostand - betrag - gebuehr;


}

8
Der Gültigkeitsbereich des formalen Parameters (betrag) überschneidet sich mit dem
Gültigkeitsbereich der lokalen Variablen ( ebenfalls betrag genannt). Das ist ein Fehler. Hier
ist die Situation anders als beim vorherigen Beispiel, in dem zwei separate formale Parameter
beide betrag genannt wurden. In jener Situation hat sich der Gültigkeitsbereich nicht
überschnitten und es gab keine Überschneidung.

Kann der gleiche Bezeichner als Name für eine lokale Variable in zwei verschiedenen
Methoden verwendet werden?

Ja — der Gültigkeitsbereichs überschneidet sich nicht, also wird es zwei lokale Variablen
geben, eine pro Methode, jede mit dem gleichen Namen.

Instanzvariable und lokale Variable mit dem gleichen Namen


Obwohl es normalerweise eine schlechte Idee ist, können Sie einem formalen Parameter oder
einer lokalen Variablen den gleichen Namen geben wie einer Instanzvariablen, zum Beispiel:

class Konto
{
. . . .
private int kontostand;

. . . .
void verarbeiteEinzahlung( int betrag )
{
int kontostand = 0; // neue Deklaration von kontostand.
kontostand = kontostand + betrag ; // verwendet die lokale Variable
kontostand.
}

Das ist kein Syntaxfehler (obwohl es wahrscheinlich ein logischer Fehler, ein Bug ist). Der
Compiler wird diesen Code ohne Beschwerde kompilieren. Die zweite Deklaration von
kontostand (die in blau) erzeugt eine lokale Variable für die Methode
verarbeiteEinzahlung(). Der Gültigkeitsbereich dieser Variable beginnt mit ihrer
Deklaration und endet mit dem Ende der Methode (so wie bei allen lokalen Variablen). Also
verwendet die nächste Anweisung die lokale Variable, nicht die Instanzvariable.

Wenn diese modifizierte Methode aufgerufen wird, wird sie betrag der lokalen Variablen
kontostand hinzufügen und dann zum Aufrufer zurückkehren. Die lokale Variable wird
keinen Wert mehr enthalten, nachdem sie zum Aufrufer zurückgekehrt ist. Die Instanzvariable
wird sich nicht geändert haben.

Hinweis: Stellen Sie sich die Anweisungen vor, als wenn Sie von Ihrer Stelle ab "aufwärts"
suchen, um jede ihrer Variablen zu finden. Die Erste, die sie sehen, ist die welche Sie
verwenden. Sie können von ihrer "verspiegelten Glasbox" in jeder Richtung nach außen
sehen, wenn sie die Variable nicht in ihrer eigenen Methode finden.

9
Es ist fast immer ein Fehler den gleichen Namen für eine Instanzvariable und eine lokale
Variable (oder einen formalen Parameter) zu verwenden. Aber es ist kein Syntaxfehler, also
wird Sie der Compiler nicht vor dem drohenden Schicksal warnen.

Ist es schwierig dieses Problem abzuwenden?

Nein — normalerweise werden alle Instanzvariablen in der Klassendefinition an einer Stelle


deklariert und es ist leicht sie zu kontrollieren.

Methoden überladen
Wenn zwei oder mehr Methoden einer Klasse den gleichen Namen, aber verschiedene
Parameterlisten haben, spricht man vom Überladen der Methoden. Wenn eine der Methoden
(von main() oder einer anderen Methode) aufgerufen wird, ist es durch die Übereinstimmung
der aktuellen Parameter mit der formalen Parameterliste klar welche Methode gemeint ist.

Hier ist eine weitere Änderung der Konto Klasse:

class Konto
{
. . . .
private int kontostand;

. . . .
void verarbeiteEinzahlung( int betrag )
{
kontostand = kontostand + betrag ;
}

void verarbeiteEinzahlung( int betrag, int gebuehr )


{
kontostand = kontostand + betrag - gebuehr;
}

Angenommen, dass zwei verarbeiteEinzahlung() Methoden gebraucht werden:

 Eine für normale Einlagen, für die keine Gebühr erhoben wird.
 Eine für andere Einlagen, für die eine Gebühr erhoben werden kann.

Der oben stehende Code implementiert diese Anforderung. Hier ist ein Beispiel der main()
Methode, die beide Methoden verwendet:

class KontoTester
{
public static void main( String[] args )
{
Konto bobsKonto = new Konto( "999", "Bob", 100 );
bobsKonto.verarbeiteEinzahlung( 200 ); // Anweisung A
bobsKonto.verarbeiteEinzahlung( 200, 25 ); // Anweisung B
}
}
10
Es gibt zwei Methodenaufrufe und zwei Methoden, die dafür zur Verfügung stehen. Ein
Methodenaufruf ruft die Methode auf, die sowohl dem Namen als auch der Parameterliste
entspricht.

Prüfen Sie main().

Welche Methode, verarbeiteEinzahlung(int) oder verarbeiteEinzahlung(int, int),


werden jede der Anweisungen aufrufen?

1. Anweisung A ruft
2. Anweisung B ruft

1. Anweisung A ruft verarbeiteEinzahlung(int) auf.


2. Anweisung B ruft verarbeiteEinzahlung(int, int) auf.

Signatur von Methoden


Bei mehreren Methoden, die den gleichen Namen haben, ist es leicht zu bestimmen, welche
durch den Aufruf verlangt wird:

Die verwendete Methode ist die, deren formale Parameter mit den aktuellen Parameter beim
Aufruf übereinstimmen.

Zum Beispiel, der Aufruf

bobsKonto.verarbeiteEinzahlung( 200, 25 ); //Anweisung B

stimmt mit dieser Methodendeklaration überein:

void verarbeiteEinzahlung( int betrag, int gebuehr )

da die Anzahl und der Typ der aktuellen Parameter mit der Anzahl und dem Typ der formalen
Parameter übereinstimmt.

Die Signatur einer Methode ist:

 Ihr Name.
 Die Anzahl, der Typ und die Reihenfolge der Parameter.

Die Signatur von Methoden in einer Klasse muss eindeutig sein. Zum Beispiel sind die
Signaturen der zwei verarbeiteEinzahlung() Methoden folgende:

 verarbeiteEinzahlung( int )
 verarbeiteEinzahlung( int, int )

Kein Teil der Signatur ist der Rückgabetyp und die verwendeten Bezeichner für die
formalen Parameter.

11
Eine Klasse hat die folgenden zwei Methoden:

float strafGebuehr( int betrag ) { ... }


int strafGebuehr( int strafe ) { ... }

Haben diese Methoden eindeutige Signaturen?

Eine Klasse hat die folgenden zwei Methoden:

float strafGebuehr( int betrag ) { ... }


int strafGebuehr( int strafe ) { ... }

Haben diese Methoden eindeutige Siqnaturen?

Antwort:

Nein.

Der Rückgabetyp zählt nicht


Weder die Namen der formalen Parametern, noch der Rückgabetyp sind Teil der Signatur.
Die Signaturen der zwei Methoden sind:

strafGebuehr( int )
strafGebuehr( int )

Es mag seltsam anmuten, dass der Rückgabetyp nicht Teil der Signatur ist. Der Grund dafür
ist, dass dadurch ein anderes Problem vermieden wird. Angenommen, dass es zwei Methoden
geben würde:

float strafGebuehr( int betrag ) { ... }


int strafGebuehr( int strafe ) { ... }

und, dass die main() Methode den folgenden Aufruf durchführt:

class KontoTester
{
public static void main( String[] args )
{

Konto bobsKonto = new Konto( "999", "Bob", 100 );

double result = bobsKonto.strafGebuehr( 60 );

}
}

Welche Methode sollte aufgerufen werden? Da sowohl int als auch float in ein double
konvertiert werden können, gibt es wenig Gründe die eine Methode der anderen vorzuziehen.
(Sie könnten argumentieren, dass float näher an einem double ist als int, aber es gibt

12
andere Situationen, die weniger klar sind.) Um in solchen Situationen Konfusion zu
vermeiden, wird der Rückgabetyp nicht als Teil der Signatur gezählt.

Eine Klasse hat die folgenden zwei Methoden:

void aendernZinssatz( double neuerZinssatz ) { ... }


void aendernZinssatz( int neuerZinssatz ) { ... }

Haben diese Methoden eindeutige Signaturen?

Ja. Die Namen der formalen Parameter müssen nicht eindeutig sein.

Objekt-Parameter
Parameter werden verwendet, um Werte vom Aufrufer an eine Methode zu übergeben. Bis
jetzt haben alle Beispiele primitive Datentypen als Parameter verwendet. Dieses Kapitel
untersucht Parameter, die Objektreferenzen sind.

Themen:

 Rückblick auf Parameter


 Call-By-Value bei primitiven Datentypen
 Objekte als Parameter
 Unveränderbare Objekte als Parameter
 Veränderbare Objekte als Parameter

(Rückblick:) Was ist ein formaler Parameter? Was ist ein aktueller Parameter?

 formaler Parameter — der in einer Methode verwendete Bezeichner, der den Wert
übernimmt, der ihm vom Aufrufer übergeben wird.
 aktueller Parameter — der aktuelle Wert, der vom Aufrufer an die Methode
übergeben wird.

Call-By-Value
Der von Java verwendete Typ der Parameterübergabe wird Call-By-Value genannt. Einige
Programmiersprachen verwenden andere Methoden der Parameterübergabe und lassen dem
Programmierer die Wahl zwischen verschiedenen Möglichkeiten Parameter zu übergeben.
Die von Java verwendete Methode Call-By-Value ist die verbreiteste Methode in modernen
Sprachen und die einfachste und sicherste.

So funktioniert Call-By-Value:

1. Wenn der aufrufende Programmteil eine Methode aufruft, liefert er eine Liste von
Werten (die aktuellen Parameter) in der Parameterliste.

13
2. Wenn die aufgerufene Methode startet, werden diese Werte an die Namensliste (der
formalen Parameter) "gebunden".
3. Die aufgerufene Methode verwendet diese Namen "an Stelle" der aktuellen Werte.

(Rückblick:) Was ist ein primitiver Datentyp?

Ein primitiver Datentyp ist einer von acht verwendeten fundamentalen Methoden um Daten
zu repräsentieren, die in Java integriert sind.

Primitive Datentypen als Parameter


Die acht primitiven Datentypen sind:

byte short int long

float double char boolean

Das Wort primitiv bedeutet: "Ein fundamentales Teilstück, das verwendet wird um andere,
größere Teile zu erzeugen." Bis jetzt haben wir Parameter mit primitiven Datentypen
verwendet. Hier ist ein sehr kleines Programm, das das noch einmal verdeutlicht:

class EinfacheKlasse
{
public void ausgeben( int x )
{
System.out.println("Wert des Parameters: " + x );
}
}

class EinfachTester
{
public static void main ( String[] args )
{
int zahl = 7;
EinfacheKlasse einfach = new EinfacheKlasse();

System.out.println("Erster Wert der lokalen Variablen zahl : " + zahl


);
einfach.ausgeben( zahl );
System.out.println("Zweiter Wert der lokalen Variablen zahl : " + zahl
);
}
}

Was ist die Ausgabe des Programms?

Erster Wert der lokalen Variablen zahl : 7


Wert des Parameters: 7
Zweiter Wert der lokalen Variablen zahl : 7

14
Änderungen des formalen Parameters haben keine Auswirkung
auf den Aufrufer
Hier ist noch einmal das Programm mit einer kleinen Änderung. Jetzt führt die aufgerufene
Methode ausgeben() eine Änderung an ihrem formalen Parameter x (ihrer Kopie des
Werts) durch.

class EinfacheKlasse
{
public void ausgeben( int x )
{
System.out.println("Erster Wert des Parameters: " + x );
x = 100; // lokale Änderung des formalen Parameters
System.out.println("Zweiter Wert des Parameters: " + x );
}
}

class EinfachTester
{
public static void main ( String[] args )
{
int zahl = 7;
EinfacheKlasse einfach = new EinfacheKlasse();

System.out.println("Erster Wert der lokalen Variablen zahl : " + zahl


);
einfach.ausgeben( zahl );
System.out.println("Zweiter Wert der lokalen Variablen : " + zahl );
}
}

Erinnern Sie sich daran, dass hier Call-By-Value verwendet wird. Das bedeutet, dass die
Parameter verwendet werden, um Werte an eine Methode zu übergeben, aber sie werden nicht
verwendet, um etwas an den Aufrufer zurückzugeben.

Was ist jetzt die Ausgabe des Programms?

Erster Wert der lokalen Variablen zahl : 7


Erster Wert des Parameters: 7
Zweiter Wert des Parameters: 100
Zweiter Wert der lokalen Variablen zahl : 7

Einen Wert zurückgeben


Sobald der Wert an die aufgerufene Methode übergeben wurde (in dem Beispiel ausgeben()
) kann die aufgerufene Methode ihn verwenden oder ändern, aber die Änderung hat keine
Auswirkung auf den Aufrufer. Aber angenommen, dass die aufgerufene Methode einen Wert
an den Aufrufer zurückliefern soll. Wie kann das getan werden? Betrachten Sie das folgende:

class EinfacheKlasseZwei
{
public int verdoppeln( int x )
{

15
return 2*x;
}
}

class EinfachTesterZwei
{
public static void main ( String[] args )
{
int zahl = 7;
int ergebnis = 0;

EinfacheKlasseZwei einfach = new EinfacheKlasseZwei();

System.out.println("Erster Wert von ergebnis: " + ergebnis );


ergebnis = einfach.verdoppeln( zahl );
System.out.println("Zweiter Wert von ergebnis: " + ergebnis );
}
}

Was ist jetzt die Ausgabe des Programms?

Erster Wert von ergebnis: 0


Zweiter Wert von ergebnis: 14

Objektreferenzen als Parameter


Objektreferenzen können Parameter sein. Das funktioniert genauso wie mit primitiven Daten:
Call-By-Value wird verwendet, aber jetzt ist der Wert eine Referenz auf ein Objekt. Da die
aufgerufene Methode eine Referenz auf das Objekt hat, kann sie das Objekt wie jede andere
Methode verwenden. Hier ist ein Beispielprogramm:

class ObjektPrinter
{
public void ausgeben( String st )
{
System.out.println("Wert des Parameters: " + st );
}
}

class OPTester
{
public static void main ( String[] args )
{
String mitteilung = "Nur ein Objekt" ;

ObjektPrinter op = new ObjektPrinter();

System.out.println("Erster Wert von mitteilung: " + mitteilung );


op.ausgeben( mitteilung );
System.out.println("Zweiter Wert von mitteilung: " + mitteilung );
}
}

Was ist die Ausgabe des Programms?

Erster Wert von mitteilung: Nur ein Objekt


16
Wert des Parameters: Nur ein Objekt
Zweiter Wert von mitteilung: Nur ein Objekt

Nur ein Objekt

Das Programm funktioniert wie Sie es erwarten. Die Abbildung zeigt, was passiert. Die
main() Methode erzeugt ein Stringobjekt, das die Zeichen "Nur ein Objekt" enthält. Eine
Referenz auf dieses Objekt wird in der Objektreferenzvariablen mitteilung gehalten.

Denken Sie daran, dass eine Referenz auf ein Objekt der Weg ist, es im Arbeitsspeicher zu
finden. Wenn eine Methode eine Referenz auf ein Objekt hat, dann kann sie dieses Objekt
verwenden.

Wenn die ausgeben() Methode aufgerufen wird, wird die Referenz auf das Objekt als Wert
an den Parameter übergeben. Das ist genauso wie bei der Call-By-Value Übergabe eines
primitiven Datentyps, aber jetzt ist der Wert eine Referenz.

Die aufgerufene Methode ausgeben() verwendet ihren formalen Parameter st, um das
Objekt zu finden.

Wenn ausgeben() den Wert der in st enthalten ist ändert, wird das das aktuelle Objekt
ändern?

Nein. Nicht das aktuelle Objekt. Das Ändern der Objektreferenz wird das Objekt nicht
ändern.

Einen Referenz-Parameter ändern


17
Hier ist ein geändertes Programm, in dem die ausgeben() Methode den Wert ändert, den ihr
formaler Parameter enthält.

class ObjektPrinter2
{
public void ausgeben( String st )
{
System.out.println("Erster Wert des Parameters: " + st );

st = "Ha! Ein zweites Objekt!" ;

System.out.println("Zweiter Wert des Parameter: " + st );

}
}

class OPTester2
{
public static void main ( String[] args )
{
String mitteilung = "Originalobjekt" ;

ObjektPrinter2 op = new ObjektPrinter2();

System.out.println("Erster Wert von mitteilung: " + mitteilung );

op.ausgeben( mitteilung );

System.out.println("Zweiter Wert von mitteilung: " + mitteilung );


}
}

Was ist die Ausgabe des Programms?

Erster Wert von mitteilung: Originalobjekt


Erster Wert des Parameters: Originalobjekt
Zweiter Wert des Parameters: Ha! Ein zweites Objekt!
Zweiter Wert von mitteilung: Originalobjekt

Geänderte Abbildung

18
In dem überarbeiteten Programm erzeugt die ausgeben() Methode ein neues Objekt und
stellt eine Referenz auf das Objekt in den Parameter st. Solange Sie wissen was Sie tun, ist
das OK.

Jede Linienfarbe in der Abbildung repräsentiert einen anderen Referenzwert. Die Abbildung
zeigt wie die ausgeben() Methode den Wert des formalen Parameters ändert, so dass er auf
ein zweites Objekt verweist.

Weder das Originalobjekt noch die Variable mitteilung der main() Methode wurden
geändert. Die ausgeben() Methode kann ihren formalen Parameter genau wie jede andere
Variable verwenden, inklusive den Wert ändern, den er enthält.

Könnte ausgeben() die Referenz auf das ursprüngliche Stringobjekt verwenden, um den
Inhalt dieses Objekts zu ändern?

Nein — da Stringobjekte unveränderbar sind. Nicht einmal die main() Methode kann das
ursprüngliche Objekt ändern.

Unveränderbare Strings
class MeinPunkt
{
public int x=3, y=5;

public void ausgeben()


19
{
System.out.println("x = " + x + "; y = " + y );
}
}

class PunktTester
{
public static void main ( String[] args )
{
MeinPunkt pt = new MeinPunkt();

pt.ausgeben();

pt.x = 45; pt.y = 83;

pt.ausgeben();
}
}

Es ist gut, dass Stringobjekte unveränderbar sind, weil dadurch die main() Methode sicher
sein kann, dass ihre Variable mitteilung vollständig unter ihrer Kontrolle bleibt. Obwohl die
ausgeben() Methode eine Kopie der Referenz bekommt, kann sie das ursprüngliche Objekt
nicht ändern.

Nicht alle Objekte sind unveränderbar.

Wichtig: Öffentliche Instanzvariablen von Objekten können von jeder Methode, die eine
Referenz auf das Objekt hat, geändert werden, auch wenn die Referenz ein formaler
Parameter ist.

(Wenn eine Instanzvariable weder public noch private ist, kann sie von einer Methode, die
in demselben Paket ist, geändert werden. Bis jetzt ist all Ihr Code in demselben Paket, so dass
der Effekt der gleiche ist als wäre die Instanzvariable public.)

Das Programm hat eine benutzerdefinierte Klasse und ein Testprogramm.

Die main() Methode verwendet den Standardkonstruktor der Klasse MeinPunkt. Das ist der
Konstruktor den Sie automatisch bekommen, wenn Sie selbst keinen definieren.

Was ist die Ausgabe des Programms?


x = 3; y = 5
x = 45; y = 83

Veränderbares MeinPunkt-Objekt
Wie das Beispielprogramm zeigt, kann das MeinPunkt-Objekt durch jede Methode geändert
werden, die eine Referenz darauf hat.

Hinweis: Wenn eine Referenz auf ein MeinPunkt-Objekt als Parameter übergeben wird, dann
kann die aufgerufene Methode diese Referenz verwenden, um die öffentliche Instanzvariable
des Objekts zu ändern.

20
Hier ist das Beispielprogramm mit einer weiteren Klasse:

class MeinPunkt
{
public int x=3, y=5 ;

public void ausgeben()


{
System.out.println("x = " + x +
"; y = " + y );
}
}

class PunktDoppler
{
public void zweimal( MeinPunkt punkt )
{
System.out.println("Beginn PunktDoppler");
punkt.ausgeben() ;
punkt.x = punkt.x * 2 ;
punkt.y = punkt.y * 2 ;
punkt.ausgeben() ;
System.out.println("Ende PunktDoppler");
}
}

class PunktTester
{
public static void main ( String[] args )
{
MeinPunkt pt = new MeinPunkt();

PunktDoppler pd = new PunktDoppler();

pt.ausgeben();

pd.zweimal( pt );

pt.ausgeben();
}
}

Was ist die Ausgabe des Programms?

x = 3; y = 5
Beginn PunktDoppler
x = 3; y = 5
x = 6; y = 10
Ende PunktDoppler
x = 6; y = 10

Immer noch Call-By-Value


Hier sind einige Fakten:

21
 Wenn eine Methode eine andere Methode aufruft, indem sie primitive Daten als
Parameter verwendet, kann die aufgerufene Methode die Daten der ersten Methode
nicht ändern.
 Aber wenn eine Methode eine andere Methode aufruft, indem sie einen
Objektreferenz-Parameter verwendet, kann der Inhalt eines veränderbaren Objekts
geändert werden und die Änderung wird in der ersten Methode sichtbar sein.
 Unveränderbare Objekte können nicht geändert werden.

Diese Fakten sind bei Call-By-Value konsistent. Der "Wert" ist die Referenz auf das Objekt.
Die aufgerufene Methode hat ihre eigene Kopie dieses Werts und kann die Kopie der
aufrufenden Methode nicht ändern. Aber, sie kann das Objekt ändern.

Natürlich, sogar wenn eine Methode eine Referenz auf ein Objekt hat, kann das Objekt nur
geändert werden, wenn das Objekt Änderungen erlaubt (entweder durch öffentliche
Instanzvariablen oder durch Zugriffsmethoden).

Betrachten Sie die Definition der MeinPunkt Klasse. Überlegen Sie sich einen Weg, um
MeinPunkt-Objekte unveränderbar zu machen.

Die zwei Instanzvariablen können private gemacht werden.

MeinPunkt mit besserer Kapselung


Hier ist eine überarbeitete Version des Programms. Jetzt sind MeinPunkt-Objekte
unveränderbar, da selbst mit einer Referenz die Methoden das Objekt nicht ändern können.

class UnveraenderbarerPunkt
{
private int x, y;

22
public UnveraenderbarerPunkt( int px, int py )
{
x = px; y = py;
}

public void ausgeben()


{
System.out.println("x = " + x +
"; y = " + y );
}

class PunktPrinter
{
public void print( UnveraenderbarerPunkt p )
{
p.ausgeben();

p.x = 77 ; // FALSCH! das können Sie nicht tun


}
}

class PunktTester
{
public static void main ( String[] args )
{
UnveraenderbarerPunkt pt =
new UnveraenderbarerPunkt( 4, 8 );
pt.ausgeben();

pt.x = 88 ; // FALSCH! das können Sie nicht tun

PunktPrinter pprinter = new PunktPrinter();


pprinter.print( pt );

}
}

Da UnveraenderbarerPunkt-Objekte unveränderbar sind, wird ein Konstruktor benötigt, um


die Instanzvariablen mit ihren permanenten Werten zu initialisieren.

(Denkfrage:) Wäre es möglich eine PunktDoppler Klasse für UnveraenderbarerPunkt-


Objekte zu schreiben?

Nein — eine derartige Klasse kann keinen Zugang zu den Instanzvariablen bekommen, den
sie braucht, um sie zu verdoppeln.

23
24