Beruflich Dokumente
Kultur Dokumente
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:
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
// . . . .
}
}
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.:
Dann wird die Methode beendet und die Kontrolle kehrt zurück zu main(). Der kontostand
von bobsKonto wird sich geändert haben.
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.
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.
Instanzvariablen werden verwendet, um den Zustand eines Objekts zu speichern. Sie werden
solange das Objekt existiert dessen Werte enthalten.
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 ;
}
. . . .
void verarbeiteEinzahlung( int betrag )
{ // Gültigkeitsbereich von betrag beginnt hier
kontostand = kontostand + betrag ;
} // Gültigkeitsbereich von betrag endet hier
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.
class Konto
{
. . . .
private int kontostand;
. . . .
void verarbeiteEinzahlung( int betrag )
{ // Gültigkeitsbereich von betrag beginnt hier
kontostand = kontostand + betrag ;
} // Gültigkeitsbereich von betrag endet hier
inkrementZaehler();
if ( kontostand < 100000 )
gebuehr = 15;
else
gebuehr = 0;
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.
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.
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.
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;
class KontoTester
{
public static void main ( String[] args )
{
Konto aktuell;
int scheck = 5000;
aktuell = new Konto( "123-345-99",
"Wanda Fish", 100000 );
}
}
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.
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;
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.
class Konto
{
. . . .
private int kontostand;
inkrementZaehler();
if ( kontostand < 100000 )
gebuehr = 15;
else
gebuehr = 0;
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.
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.
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.
class Konto
{
. . . .
private int kontostand;
. . . .
void verarbeiteEinzahlung( int betrag )
{
kontostand = kontostand + betrag ;
}
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.
1. Anweisung A ruft
2. Anweisung B ruft
Die verwendete Methode ist die, deren formale Parameter mit den aktuellen Parameter beim
Aufruf übereinstimmen.
da die Anzahl und der Typ der aktuellen Parameter mit der Anzahl und dem Typ der formalen
Parameter übereinstimmt.
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:
Antwort:
Nein.
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:
class KontoTester
{
public static void main( String[] args )
{
}
}
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.
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:) 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.
Ein primitiver Datentyp ist einer von acht verwendeten fundamentalen Methoden um Daten
zu repräsentieren, die in Java integriert sind.
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();
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();
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.
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;
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" ;
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.
class ObjektPrinter2
{
public void ausgeben( String st )
{
System.out.println("Erster Wert des Parameters: " + st );
}
}
class OPTester2
{
public static void main ( String[] args )
{
String mitteilung = "Originalobjekt" ;
op.ausgeben( mitteilung );
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;
class PunktTester
{
public static void main ( String[] args )
{
MeinPunkt pt = new MeinPunkt();
pt.ausgeben();
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.
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.)
Die main() Methode verwendet den Standardkonstruktor der Klasse MeinPunkt. Das ist der
Konstruktor den Sie automatisch bekommen, wenn Sie selbst keinen definieren.
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 ;
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();
pt.ausgeben();
pd.zweimal( pt );
pt.ausgeben();
}
}
x = 3; y = 5
Beginn PunktDoppler
x = 3; y = 5
x = 6; y = 10
Ende PunktDoppler
x = 6; y = 10
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.
class UnveraenderbarerPunkt
{
private int x, y;
22
public UnveraenderbarerPunkt( int px, int py )
{
x = px; y = py;
}
class PunktPrinter
{
public void print( UnveraenderbarerPunkt p )
{
p.ausgeben();
class PunktTester
{
public static void main ( String[] args )
{
UnveraenderbarerPunkt pt =
new UnveraenderbarerPunkt( 4, 8 );
pt.ausgeben();
}
}
Nein — eine derartige Klasse kann keinen Zugang zu den Instanzvariablen bekommen, den
sie braucht, um sie zu verdoppeln.
23
24