Reine Wirkaufrufe in Java
Einführendes Beispiel
Bisher hatten alle behandelten Ausdrücke einen Wert. Dieser Wert konnte mit einer Ausgabeanweisung ausgegeben oder mit einer Aufrufanweisung verworfen werden. Aufrufe von »java.lang.System.setProperty« haben beispielsweise einen Wert und eine Wirkung.
Diese Lektion behandelt nun Ausdrücke, die keinen Wert haben. Solche Ausdrücke kommen mangels eines Wertes nicht als Ausgabeausdrücke einer Ausgabeanweisung in Frage. Sie können nur als Aufruf einer Aufrufanweisung verwendet werden.
Das folgende Programm veranlaßt die Auswertung des Ausdrucks »java.lang.Thread.dumpStack()«.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.Thread.dumpStack(); }}transcript
java.lang.Exception: Stack trace
at java.lang.Thread.dumpStack(Thread.java:1329)
at Main.main(Main.java:3)- Aussprachehinweis
- thread θrɛd
Die Auswertung des Aufrufausdrucks »java.lang.Thread.dumpStack()« führt zu einem Aufrufvorgang der Methode »java.lang.Thread.dumpStack()«, wodurch ein Text ausgegeben wird, wie oben zu sehen ist.
Stapelberichte
Der Stapelspeicher ist ein Teil des Speichers, der Informationen über den Stand der Ausführung des laufenden Programmes enthält. Ein Stapelbericht ist ein Text, der einige Informationen über den Inhalt des Stapelspeichers enthält. Bei der Auswertung von »java.lang.Thread.dumpStack()« wird ein Stapelbericht ausgegeben.
Mit dem in der Ausgabe des Programmes zu lesendem Wort »Exception« werden oft Fehler bezeichnet, die während der Programmausführung auftreten. Wenn man allerdings wie hier mit »dumpStack()« ausdrücklich einen Stapelbericht verlangt hat, so ist dessen Ausgabe kein Zeichen für einen Fehler, auch wenn das Wort »Exception« darin ausgegeben wird.
In diesem Stapelbericht ist vorwiegend seine letzte Zeile relevant, die anzeigt, daß das Programm gerade bei der Abarbeitung der Zeile 3 des Programms »Hello.java« ist.
(Die Details der Ausgabe könnten bei verschiedenen Programmausführungen etwas voneinander abweichen, solange in der ersten Zeile »Stack trace« steht und in der letzten »Hello.java:3« beziehungsweise »Main.java:3«.)
Dies ist übrigens das erste Mal, daß wir im Kurs eine Möglichkeit zur Erzeugung einer Ausgabe sehen, die nicht »java.lang.System.out.println« oder »java.lang.System.out.print« verwendet.
Auswertungen und Wirkungen
Die Auswertung des Aufrufausdrucks »java.lang.Thread.dumpStack()« liefert keinen Wert.
Die Auswertung des Ausdrucks »java.lang.Thread.dumpStack()« hat aber eine Wirkung : Sie führt zur Ausgabe eines Stapelberichts auf die Konsole.
Die Auswertung des Aufrufausdrucks »java.lang.Thread.dumpStack()« liefert keinen Wert, aber sie hat eine Wirkung.
Obwohl der Aufrufausdruck »java.lang.Thread.dumpStack()« gar keinen Wert hat, sagt man doch, daß er in dem obigen Programm ausgewertet werde. Hier ist die Auswertung – entgegen ihrer Bezeichnung – nicht die Ermittlung eines Wertes, sondern diese Auswertung hat nur eine Wirkung. Trotzdem spricht man auch in diesem Fall von einer „Auswertung“. Allgemein kann eine Auswertung einen Wert ergeben und/oder eine Wirkung haben. Es ist möglich, daß manchmal nur eines von beidem eintritt.
Die Rückgabespezifikation »void«
Ob eine Methode eine Wirkung und/oder einen Wert hat, kann der Dokumentation entnommen werden.
Direkt vor dem eigentlichen Namen (Verb) eine Methode steht in der Dokumentation die Rückgabespezifikation. Die Rückgabespezifikation kann ein Typ sein, was dann bedeutet, daß ein Aufruf der Methode jenen angegebenen Typ hat. Lautet die Rückgabespezifikation jedoch »void«, so liefert die Methode keinen Wert.
- Dokumentation von »java.lang.Math.random()« (gekürzt)
- Modul java.base
Paket java.lang
Klasse Math
static double random()
Ergibt einen double-Wert groesser oder gleich 0.0 und kleiner als 1.0. - Dokumentation von »java.lang.Thread.dumpStack()« (gekürzt)
- Modul java.base
Paket java.lang
Klasse Thread
static void dumpStack()
Gibt einen Stapelbericht aus.
Eine Methode, deren Aufruf keinen Wert hat, wird mit dem Wort »void« vor dem Methodennamen gekennzeichnet.
Laut der JLS8 bezeichnet »void« keinen Typ (aber der Java -Compiler »javac« bezeichnet ›void‹ als „Typ“ [Stand 2017]).
»void« ist kein Typ, sondern nur eine Kennzeichnung, die angibt, daß eine Methode keinen Wert liefert.
Ein Aufruf einer void-Methode stellt somit eine Ausnahme von der allgemeinen Regel dar, nach der jeder Ausdruck einen Typ hat. In der Praxis kann man allerdings – etwas unkorrekt – sagen, daß der Typ eines solchen Aufrufs »void« sei, wie dies etwa vom Java -Compiler ›javac‹ praktiziert wird.
Wir nennen eine Methode mit der Rückgabespezifikation »void« eine void-Methode.
Eine Methode, die keine void-Methode ist, nennen wir eine Wertmethode.
Verwendung von Aufrufen ohne Wert
Weil der Ausdruck »java.lang.Thread.dumpStack()« keinen Wert hat, kann er auch nicht in eine Ausgabeanweisung eingesetzt werden, denn diese erwartet einen Ausdruck mit einem Wert, da sie diesen Wert dann ausgeben soll.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println( java.lang.Thread.dumpStack() ); }}Ausgabe des Compilers (Übersetzt)
Main.java:3: Fehler: Typ 'void' ist hier nicht erlaubt
{ java.lang.System.out.println( java.lang.Thread.dumpStack() ); }}
^
1 Fehler
Die Fehlermeldung hat die folgende Ursache:
Ein Aufruf einer void-Methode (wie der Aufruf »java.lang.Thread.dumpStack()«) darf nur als Aufruf einer Aufrufanweisung verwendet werden.
Es ist damit also ausgeschlossen, einen Aufruf einer void-Methode als Ausdruck einer Ausgabeanweisung oder als Teilausdruck zu verwenden.
Aufrufmuster
Ein Ausdruck kann im allgemeinen entweder in eine Ausgabeanweisung geschrieben werden, wenn sein Wert ausgegeben werden soll, oder vor das Semikolon einer Aufrufanweisung, wenn sein Wert nicht verwendet werden soll.
Wir sagen zum ersteren auch, daß der Wert „in die println-Klammern “ geschrieben werde, also in die runden Klammern, welche direkt hinter »java.lang.System.out.println« stehen, und zum zweiten, daß der Wert „vor das Semikolon “, also vor das Semikolon der Aufrufanweisung, geschrieben werden. (Das hier für »java.lang.System.out.println« Gesagte gilt aber auch für »java.lang.System.out.print«.)
In dem folgenden Programm steht der Ausdruck »java.lang.System.setProperty( "user.country", "US" )« „in den println-Klammern“ und der Ausdruck »java.lang.System.setProperty( "user.country", "IT" )« „vor dem Semikolon“.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{java.lang.System.out.println( java.lang.System.setProperty( "user.country", "US" ));
java.lang.System.setProperty( "user.country", "IT" );
}}
ℛ Aufrufmuster für reine Wirkmethoden (Faustregel)
Wenn eine Methode keinen Wert liefert, dann muß ein Aufruf jener Methode als Aufruf einer Aufrufanweisung, also vor das Semikolon geschrieben werden.
wirkmethode();
ℛ Aufrufmuster für Wertmethoden (Faustregel)
Wenn eine Methode einen Wert liefert, dann kann ein Aufruf jener Methode als Aufruf einer Aufrufanweisung vor das Semikolon oder als Ausdruck einer Ausgabeanweisung, also in die println-Klammern geschrieben werden.
java.lang.System.out.println( wertmethode() );
Deskriptoren ⃗
Der Deskriptor einer Methode besteht aus der Signatur dieser Methode zusammen mit ihrer Rückgabespezifikation. Beispielsweise ist »double abs(double)« ein Methodendeskriptor.
(Die Proklamation der Dokumentation kann noch zusätzliche Methodenmodifizierer, wie »public« und »static« enthalten.)
(Der Begriff des Methodendeskriptors ist nicht zu verwechseln mit dem Begriff des Typdeskriptors wie »[I«.) *
Übungsfragen ⃗
(In diesen Übungsfragen werden Methodennamen wie »java.util.ResourceBundle.clearCache« vorgestellt, ohne daß die als Beispiel gewählten speziellen Methoden dabei im Vordergrund stehen sollen. Es geht vielmehr darum, das allgemeine Phänomen der Wirkungen und Werte vorzustellen. Deswegen werden an dieser Stelle die Anwendungsmöglichkeiten von Methoden wie »java.util.ResourceBundle.clearCache« nicht weiter vertieft. Es geht hier nicht um die speziellen Möglichkeiten der Anwendung von Methoden wie »java.util.ResourceBundle.clearCache«, sondern darum, wie es allgemein möglich ist, Werte und Wirkungen zu unterscheiden. Methoden wie »java.util.ResourceBundle.clearCache« dienen dann lediglich als ein Beispiel dafür.)
? Aufrufe klassifizieren ⃗
Geben Sie zu den folgenden Methoden jeweils an, ob die Auswertung eines Aufrufs dieser Methoden jeweils einen Wert ergibt oder ob sie eine Wirkung hat (oder beides oder keines von beidem).
- »java.lang.Math.signum(double)«
- »java.util.ResourceBundle.clearCache()«
- »java.lang.Character.toLowerCase(int)« ⃗
- »java.lang.System.exit(int)« ⃗
- Dokumentation von »java.lang.Math.signum()« (vereinfacht)
java.lang
Klasse Math
static double signum( double x )
Ergibt das Signum von x.- Die Dokumentation von »java.util.ResourceBundle.clearCache« (vereinfacht)
java.util
Klasse ResourceBundle
static void clearCache()
Entfernt alle Ressourcenbuendel aus dem Speicher.- Aussprachehinweise
- exit ˈɛg zɪt
- signum (englisch) ˈsɪgnəm
- Signum (deutsch) ˈzɪgnʊm
- clear cache ˈklɪɚ ˈkæʃ
? Die Aufrufanweisung ⃗
Der Wert von »java.lang.Math.cos( 0 )« ist gleich «1.0». Was passiert mit diesem Wert «1.0» bei der Ausführung der Aufrufanweisung »java.lang.Math.cos( 0 );«?
? Anweisungen erkennen ⃗
Geben Sie zu den folgenden Texten an, ob sie Anweisungen sind.
Falls es sich um eine Anweisung handelt, geben Sie an, ob es sich um eine Ausgabeanweisung oder um eine Aufrufanweisung handelt.
- »java.lang.Math.signum( .2 )«
- »java.lang.System.out.println( java.lang.Math.signum( .2 ));«
- »java.lang.Math.signum( .2 );«
? Aufrufmuster ⃗
Für diese Übungsfrage wird folgende Regel angenommen:
- Aufrufe von void-Methoden (Methoden ohne Wert) werden als Aufruf einer Aufrufanweisung geschrieben.
- Aufrufe von Wertmethoden (Methoden mit Wert, also nicht-void-Methoden) werden als Ausdruck einer Ausgabeanweisung geschrieben.
Welche der folgenden vier Anweisungen sind nach der gegebenen Regel korrekt?
- »java.lang.System.out.println( java.util.ResourceBundle.clearCache() );« Anton
- »java.util.ResourceBundle.clearCache();« Berta
- »java.lang.System.out.println( java.lang.Math.random() );« Cäsar
- »java.lang.Math.random();« Dora
? Ausgabe vorhersagen ⃗
Der Kosinus (»java.lang.Math.cos«) von «0» ist «1.0».
Welche Ausgabe erzeugt das folgende Programm?
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{java.lang.System.out.println( 2 );
java.lang.System.out.println( java.lang.Math.cos( 0 ));
java.lang.Math.cos( 0 ); }}
? Anweisungen erkennen *
Geben Sie zu den folgenden Texten jeweils an, ob sie eine Anweisung sind.
- »java.lang.Math.signum( .2 );«
- »java.util.ResourceBundle.clearCache();«
- »2 + 7;«
- »java.lang.Math.PI;«
Übungsaufgaben
/ Aufrufe schreiben
Für diese Übungsaufgabe werden die folgende Regeln angenommen:
- Aufrufe von void-Methoden (Methoden ohne Wert) werden als Aufruf einer Aufrufanweisung geschrieben.
- Die Werte der Aufrufe von Wertmethoden (Methoden mit Wert, also nicht-void-Methoden) werden mit einer Ausgabeanweisung ausgegeben.
Schreiben Sie ein Programm, das die beiden Aufrufe »java.lang.Math.random()« und »java.util.ResourceBundle.clearCache()« enthält. Das Programm soll den eben genannten Regeln entsprechen und ansonsten möglichst kurz sein (nichts Überflüssiges enthalten).
Wertausdrücke ⃗
Da nun nicht mehr jeder Ausdruck immer einen Wert hat, führen wir noch die spezielle Bezeichnung Wertausdruck für einen Ausdruck, der einen Wert hat, ein.
Mögliche Kombinationen ⃗
Es gibt alle möglichen Kombinationen von Werten und Wirkungen bei Methoden:
- Es kann in Java Methoden geben, bei denen die Auswertung eines Aufrufs weder einen Wert noch eine Wirkung hat. Solche Methoden haben auch einen Sinn, wie aber erst später verständlich gemacht werden können wird. Bis dahin können wir sie als sinnlos ansehen.
- Es gibt viele Methoden, bei denen die Auswertung eines Aufrufs nur einen Wert ergibt, aber keine Wirkung hat. Ihr Sinn besteht darin, eine bestimmte Information zu ergeben, die durch den Wert repräsentiert wird. Beispiel: »java.lang.Math.abs«. Der Name solcher Methoden ist in der Regel ein Substantiv, welches den sich ergebenden Wert beschreibt. Das Verhalten solch einer Methode besteht nur darin, einen bestimmten Wert zu ergeben.
- Es gibt auch viele Methoden, bei denen die Auswertung eines Aufrufs nur eine Wirkung hat. Ihr Sinn besteht darin, etwas Bestimmtes zu tun. Beispiel: »java.lang.Thread.dumpStack«. Sie enthalten in der Regel ein Verb im Namen. Falls eine Auswertung nur eine Wirkung hat, aber keinen Wert ergibt, so hat sie trotzdem ein Verhalten ; es besteht in diesem Fall dann nur im wirken.
- Es gibt auch Methoden, bei denen die Auswertung eines Aufrufs sowohl einen Wert ergibt als auch eine Wirkung hat. Beispiel: »java.lang.System.setProperty«. Sie enthalten ein Verb im Namen, wenn der Aspekt der Wirkung im Vordergrund steht. Das Verhalten solcher Methoden besteht darin, einen bestimmten Wert zu ergeben und zu wirken.
Die Magie des Wortes ⃗
Magier können angeblich durch die Kraft des Wortes wirken, hiervon berichtet etwas Goethe in seiner Ballade vom Zauberlehrling.
Wörter, die in einem Programm stehen, wie etwa »dumpStack«, werden nun ebenfalls auf scheinbar magische Weise in Taten umgesetzt.
Diese Magie geschieht bei der Auswertung von Wirkausdrücken. Die Auswertung ist der Prozeß, welcher aus dem Wort (Wirkausdruck) eine Tat (Wirkung) macht. Dieses Geschehen basiert jedoch letztendlich weniger auf Magie als auf Physik.
Man kann sich beispielsweise vorstellen, daß Ausbuchtungen auf der Walze einer Spieluhr beim Drehen der Walze Zungen in Schwingungen versetzen und so bestimmte Töne hervorrufen. Die Ausbuchtungen sind Aufzeichnungen, die Worten ähneln; sie werden aufgrund der physikalischen Gesetze beim Drehen der Walze in Aktivitäten (Schwingungen) umgesetzt.
Auch das Funktionieren eines Computers und die Art und Weise, wie er einen Java -Ausdruck auswertet, basiert letztendlich nicht auf Magie, sondern auf physikalischen Gesetzen ; nur ist das Geschehen dabei oft so kompliziert, daß es nicht mehr ohne weiteres wie bei einer Spieluhr direkt nachvollzogen werden kann.
Schon vor der Zeit der elektronischen Computer konnte man durch Worte wirken, wenn man Diener hatte die auf diese Worte hören. In den 1920er Jahren waren „Computer“ tatsächlich noch angestellte Menschen, die für ein Unternehmen Berechnungen durchführten. Auch Tiere, wie Collies oder Graupapageien, können Worte verstehen. Allerdings kann man nie sicher sein, ob ein gegebenes Kommando wirklich befolgt werden wird. Das Besondere an der Magie ist es, Dinge oder Personen zum Befolgen von Kommandos zu bringen, die normalerweise nicht auf Kommandos reagieren können oder wollen.
Übergabe der Kontrolle ⃗
Bei einer Methode ohne Parameter und ohne Rückgabewert wird nur die Kontrolle über den Computer hin- und hergereicht.
Die folgende Abbildung zeigt, wie die Hauptmethode »main« die Kontrolle an die Methode »java.lang.dumpStack« übergibt und die Kontrolle dann später wieder von »java.lang.dumpStack« an die Hauptmethode »main« zurückgegeben wird.
- »main« und »dumpStack« bei »java.lang.Thread.dumpStack()«
.-------------------------------------------------.
| main -----------------. |
| | Übergabe der Kontrolle |
| V |
| .-------------------------. |
| | java.lang.dumpStack | |
| '-------------------------' |
| | |
| | |
|<----------------------' |
| Rueckgabe der Kontrolle |
| |
| |
'-------------------------------------------------'
Eine Methode nennt man manchmal auch ein „Unterprogramm“. Die folgende Abbildung zeigt allgemein die Übergabe der Kontrolle von einem Hauptprogramm (wie beispielsweise »main«) an ein Unterprogram (wie beispielsweise »java.lang.dumpStack«).
- Übergabe und Rückgabe der Kontrolle zur Laufzeit
.-------------------------------------------------.
| Hauptprogramm---------. |
| | Übergabe der Kontrolle |
| V |
| .-------------------------. |
| | Unterprogramm | |
| '-------------------------' |
| | |
| | |
|<----------------------' |
| Rueckgabe der Kontrolle |
| |
'-------------------------------------------------'
Zitate *
- Zitat aus JLS 9, 8.4.5
- The result of a method declaration either declares the type of value that the method returns (the return type), or uses the keyword void to indicate that the method does not return a value.