Kapitel-Index
7.1 Methoden-Vereinbarung (ggf. generisch)
7.2 Methoden-Aufruf
7.3 Rekursive Methoden
7.4 Parameterübergabe (by value)
7.4.1 Nur-Eingabe über PrimitiveType-Parameter
7.4.2 Ein/Ausgabe über ReferenceType-Parameter
7.5 Überladen (overloading) von Methoden
7.6 Ergebnistyp und Rücksprung (return)
7.7 Hauptprogramm mit main, Argumente
7.8 Testfragen
Methoden sind Mitglieder von Klassen (9.1.1). Eine Methode ist
ein Unterprogramm (subprogram) mit Parametern bei der Methoden
-Vereinbarung (7.1) und entsprechenden Argumenten beim Methoden
-Aufruf (7.2).
----------------------
VVVVVVVVVVVVVVVVVVV
Methoden (Übersicht)
VVVVVVVVVVVVVVVVVVV
VV
----------------------
VV
VV Man unterteilt Methoden in VV
VV VV
VV
nonvoid Methoden, d.h. VV
VV VV
VV Funktionen vom Ergebnistyp Type (mit Ergebnis), VV
VV die einen Ergebniswert berechnen und VV
VV aufgerufen werden als Ausdruck, z.B. sin(x) VV
VV VV
VV
void Methoden, d.h. VV
VV VV
VV Prozeduren vom Ergebnistyp void (ohne Ergebniswert), VV
VV die nur eine Tätigkeit ausführen und VV
VV aufgerufen werden als Anweisung, z.B. out.print(x); VV
VV VV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
Auch Operationen (4.2.) sind Unterprogramme. Sie sind aber nicht
als Methoden in Klassen vereinbart, sondern in der Sprache vordefiniert, und können in Java nicht vom Programmierer vereinbart
werden.
7.1 Methoden-Vereinbarung (ggf. generisch)
Zu einer Methoden-Vereinbarung kann es mehrere Methoden-Aufrufe
(7.2) geben. Vereinbarungen mit generischen Typ-Parametern (siehe
Beispiel Generic 9.2.2) oder mit rekursivem (7.3) Methoden-Aufruf
oder mit Überladen (7.5) des Methoden-Bezeichners sind zulässig.
Eine Methoden-Vereinbarung (MethodDeclaration) ist nach Syntaxdiagramm 083 (hier Erweiterung 083° durch Einsetzen von MethodDeclarator Syntax.081) von der Form
083°:MethodDeclaration MethodenVereinbarung
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
H
---------------------
H
H
----------------
H
H -
-
MethodModifier
-
H
H
----------------
H
H
---------------------
H
H
-------------------------------
TypeParameterClause
-
H
H
---------------------
H
H
of generic method
H
H
H
H
--------------------------------------------------------
H
H
H
H
Method- H
H
----------
------------
-----------------------
H
H
-
ResultType
-
Identifier
-
FormalParameterClause
-
H
H
----------
------------
-----------------------
H
H of method
H
H
H
H
--------------------------------------------
H
H
H
H
MethodBody H
H
--------
-------
H
H
-
Throws
-
-
-
Block
-
-
H
H
--------
-------
H
H
H
H
-------------
----
; ----
H
H abstract or native H
H H
H An AbstractMethodDeclaration Eine AbstraktMethodenVereinba- H
H is a MethodDeclaration with rung ist eine MethodenVerein- H
H AbstractMethodModifier(s) and barung mit AbstraktMethoden- H
H Semicolon instead of Method- Modifizierer(n) und Semikolon H
H Body. anstatt MethodenRumpf. H
H H
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
z.B. double ariMed(double a,double b){return (a+b)/2;}
Eine Methode kann als Mitglied einer Klasse (siehe 9.1.1) auf
vielfältige Weise modifiziert werden (MethodModifier Syntax.070),
z.B. static double ariMed(double a,double b){return (a+b)/2;} .
Werden Parameter einer Methode als final modifiziert, so bedeutet
das nicht, dass nur Konstanten als aktuelle Parameter eingesetzt
werden dürfen, sondern dass die Parameter im Methoden-Rumpf konstant bleiben,
z.B. double ariMed(final double a,final double b){return (a+b)/2;}
Eine Methode kann vereinbart werden ggf. generisch mit einer
Typ-Parameter-Klausel, mit einem Ergebnistyp ggf. generisch mit
einer Typ-Variablen, mit einem Methoden-Bezeichner und mit einer
leeren oder nichtleeren Formalparameter-Klausel ggf. generisch mit
Typ-Variablen,
z.B. (siehe Generic 9.2.2)
<S> void swap(S[] s){S memo=s[0];s[0]=s[1];s[1]=memo;} .
Eine Methode kann Ausnahmen auswerfen (Throws Syntax.082,11.1.1),
z.B. static double geoMed(double a,double b)
throws RuntimeException
{if(a*b<0){throw new RuntimeException(a+"*"+b+"<0");}
return sqrt(a*b);} ,
und besitzt einen Methoden-Rumpf oder im Falle einer abstrakten
Methode (9.1.4, 9.2) oder einer plattformabhängigen (native) Methode an Stelle des Methoden-Rumpfs ein Semikolon,
z.B. abstract int hebSteuer(int einkommen); .
Eine Methode wird aufgerufen, indem vom Ort des Aufrufs zum Ort
der Vereinbarung gesprungen wird, nachdem vorher die Argumente,
und die Rücksprungadresse übergeben worden sind. Nach Durchlaufen
des Methoden-Rumpfs wird zur Rücksprungadresse an den Ort des Aufrufs zurückgesprungen, nachdem ggf. bei einer Funktion (nonvoid
method) der Ergebniswert zurückübergeben (return, 7.6) wurde.
Ein Methoden-Aufruf (siehe unten) kann ggf. qualifiziert werden
mit dem Namen einer Klasse. Aufgerufen werden kann ggf. generisch
mit einer Typ-Argument-Klausel. Es folgt ein Methoden-Bezeichner
und eine leere oder nicht leere Argument-Klausel,
z.B. (siehe Generic 9.2.2)
this.<T>swap(t);
Wurde eine Methode vereinbart, die Ausnahmen auswirft (Throws
Syntax.082, 11.1.1), dann muss die ausgeworfene Ausnahme beim
Methoden-Aufruf abgefangen werden,
z.B. try // geoMed(double,double) siehe 7.1
{out.println("geoMed(-5,20)="+geoMed(-5,20));}
catch(RuntimeException e)
{err.println(e.toString());}
// java.lang.RuntimeException: -5*20<0
Ein Methoden-Aufruf (syntaktisch MethodInvocation) ist nach Syntaxdiagramm 085 von der Form
085: MethodInvocation MethodenAufruf
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
H H
H Class- H
H
------
H
H -
----
Name
-----
. ----
H
H
------
H
H
H
H
Class-
H
H
------
H
H
-
-
Name
-----
. -
H
H
------
H
H
--------------------
H
H
H
H
--
super -----
. -
H
H
class
H
H
H
H
---------
---------------------------
H
H
----
Primary
--
. -
-
-
NonWildTypeArgumentClause
-
H
H
---------
---------------------------
H
H
e.g. this
of generic method
H
H
H
H
--------------------
-----------------------------------
H
H
Method- H
H
------------
H
H
-
Identifier
----
H
H
------------
H
H
Method-
H
H
------
----------------
H
H
----
Name
----------
----
ArgumentClause
-
H
H
------
----------------
H
H of method H
H H
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
z.B. ariMed(-5,20) // 7.5 ariMED(double,double) siehe 7.1
Rekursive Aufrufe von Methoden in ihrer eigenen Vereinbarung
(direkt) oder wechselseitig in der anderen Vereinbarung (indirekt)
sind zulässig. Unendliche Folgen von Aufrufen werden durch Setzen
von Abbruch-Prüfungen vermieden,
z.B. static void MergeSort(int L,int R) // MergeSort siehe 6.4
{ ...
if (L <M) {MergeSort(alt(L ),alt(M));}
if (M+2<R) {MergeSort(alt(M+2),alt(R));}
...
} .
Es wird stets nur ein Exemplar der Methode vom Compiler vorgehalten. Lediglich die jeweiligen aktuellen Parameter und die Rücksprungadresse werden in einem Gedächtnis gekellert.
In modernen Compilern ist die Parameterübergabe und Sprung-Kellerung so effizient implementiert, dass Methoden in rekursiver
Version und in repetiver Version etwa gleiche Laufzeiten ergeben.
//********************** Sum.java ********************************
// Summe ueber 5000 Glieder: sqrt(random())+...+sqrt(random()) *
// rekursiv oder mit for-Schleife *
//****************************************************************
// java.lang. Object
import static java.lang.System.*; // |`System
import static java.lang.Math.*; // `Math
class Sum
{static double recSum(int length)
{return (length==0 ? 0 : recSum(length-1)+sqrt(random()));
}
static double forSum(int length)
{double sum=0;
for(int i=0;i<length;i++) {sum+=sqrt(random());}
return sum;
}
public static void main(String[] args) // args ungenutzt
{long ms=currentTimeMillis();
out.print ("recSum="+recSum(5000)+" ms=");
out.println(currentTimeMillis()-ms);
ms=currentTimeMillis();
out.print ("forSum="+forSum(5000)+" ms=");
out.println(currentTimeMillis()-ms);
}
}
//****************************************************************
Output
-------------------------------
recSum=3331.9981175605744 ms=10
forSum=3323.2189966095066 ms=10
7.4 Parameterübergabe (by value)
Die Parameter einer Methoden-Vereinbarung (7.1) nennt man "formale Parameter", synonym "Parameter", die Parameter eines Methoden
-Aufrufs (7.2) "aktuelle Parameter", synonym "Argumente". Die
Parameter und Argumente einer Methode müssen in Anzahl, Anordnung
und Typ zueinander passen, brauchen jedoch nicht in ihren Namen
übereinzustimmen. Mit final-Modifizierer sind Parameter Konstanten
im Methoden-Rumpf, sonst Variablen. Ihre zugehörigen Argumente
können beliebige Ausdrücke sein.
Eine formale Parameter-Klausel kann auch Mehrfach-Parameter
enthalten, die durch Auslassung (ellipsis) "..." gekennzeichnet
werden (Syntax.076), z.B. (Format-String, 2.7)
String format(String format_string,Object... args)
Zu Beginn des Methoden-Aufrufs wird der Wert jedes Arguments an
den entsprechenden Parameter in Kopie übergeben (pass by value).
Am Ende des Methoden-Aufrufs erfolgt keine Rück-Kopierung von
Parameterwerten, d.h. die Argumente sind vor Überschreibung durch
die entsprechenden Parameter geschützt.
7.4.1 Nur-Eingabe über PrimitiveType-Parameter
Über Grundtyp-Parameter (PrimitiveType Syntax.030, 2) ist nur
Eingabe, aber keine Ausgabe möglich.
//*************************** GGT.java ***************************
// Groesster gemeinsamer Teiler von zwei pos. Zahlen nach Euklid.*
// Demonstration der Nur-Eingabe ueber PrimitiveType-Parameter. *
// Command: java GGT args[0] args[1] *
//****************************************************************
// java.lang. Object
import static java.lang.System.out; // |`System
import static java.lang.Integer.parseInt; // `Integer
class GGT
{static int findGGT(int a,int b) // a,b vom PrimitiveType
{while (a!=b) if(a>b) {a-=b;} else {b-=a;}
return a;
}
public static void main(String[] args) //args[0],args[1] genutzt
{out.println(findGGT(parseInt(args[0]),parseInt(args[1]))+
"=GGT(" +args[0] + "," + args[1]+")");
}
}
//****************************************************************
Nur-Eingabe der Werte
args
----
---
4711
Command a
----
--------------- Adr.2:
----
Adr.4711:
-----
java GGT 66 385
66
----------
-"66"
----
parseInt
b
-----
Output Adr.9:
----
--------------
385
----------
"385"
11=GGT(66,385)
----
parseInt
-----
Keine Ausgabe der Werte
Im obigen Programm GGT wird der größte gemeinsame Teiler zweier
positiver Zahlen nach dem Reduktionsverfahren von Euklid (3.Jh.v.
Chr.) durch forlaufende Subtraktion bestimmt:
"Da jeder Teiler von a,b auch Teiler von a-b und b-a ist,
reduziert man das Problem im Falle a>b auf die Bestimmung des
größten gemeinsamen Teilers von a-b,b und im Falle von b>a
auf die Bestimmung des größten gemeinsamen Teilers von a,b-a.
Gilt für das neue Zahlenpaar a==b , so ist a oder b der ge-
suchte größte gemeinsame Teiler, sonst wird weiter reduziert.
Bei jeder Reduktion nähern sich die beiden Zahlen a,b um
mindestens 1, d.h. die Reduktion führt nach endlich vielen
Schritten zum Ziel a==b ."
7.4.2 Ein/Ausgabe über ReferenceType-Parameter
Über Referenztyp-Parameter (RefereceType Syntax.0.30, 2) ist zwar
auch nur Eingabe einer Adresse möglich, keine Ausgabe einer Adresse, aber der Wert, auf den diese Adresse zeigt, kann verändert
werden, was einen Ausgabe-Effekt ergibt. Der Parameter und das
zugrhörige Argument, müssen vom gleichen Referenztyp sein. Für den
Ausgabe-Effekt muss das Argument speziell eine Variable sein und
nicht, wie sonst zulässig, ein allgemeiner Ausdruck.
Es folgt das Beispiel Tausch mit formalem Parameter String[] paar
und entsprechendem aktuellen Parameter String[] args.
//************************* Tausch.java **************************
// Vertauschen der Werte der Komponenten einer Paar-Reihung. *
// Demonstration der Ein/Ausgabe ueber ReferenceType-Parameter. *
// Command: java Tausch args[0] args[1] *
//****************************************************************
// java.lang Object
import static java.lang.System.out; // `System
class Tausch
{
static void tausch(String[] paar) // paar vom ReferenceType
{
final String MEMO=paar[0];paar[0]=paar[1];paar[1]=MEMO;
}
public static void main(String[] args) //args[0],args[1] genutzt
{
tausch (args); // args vom ReferenceType
out.println (args[0]+" "+args[1]);
}
}
//****************************************************************
Nur-Eingabe der Adresse
Command paar args
--------------------- Adr.2:
----
Adr.7:
----
java Tausch Dick Doof
4711
---------
4711
--
-
-
--
------
------
Output
------
------
--------- Adr.4711:
"Dick"
"Doof"
Doof Dick
------
------
Ein/Ausgabe der Werte
Ein Zahlen-Tausch void tausch(double[] paar) wäre auch ohne
Hilfsspeicher MEMO möglich,
z.B. für paar[0]>=paar[1]>=0:
paar[0]-=paar[1];paar[1]+=paar[0];paar[0]=paar[1]-paar[0];
7.5 Überladen (overloading) von Methoden
Der Bezeichner einer Methode sowie die Anzahl, Anordnung und
Typen der Parameter einer Methode bilden die "Signatur" der Methode. Die Bezeichner der Parameter und der Ergebnistyp der Methode
gehören nicht zur Signatur der Methode.
Methoden können hinsichtlich ihres Bezeichners "überladen" sein
(overloading), d.h. den gleichen Bezeichner besitzen, sofern sie
sich in der übrigen Signatur, d.h. bezüglich der Anzahl, Anordnung
und Typen ihrer Parameter, unterscheiden.
Im nachfolgenden Programm Overloading bestimmt der Typ des aktuellen Parameters von overload(), welche der verschiedenen überladenen Methoden mit dem gleichen Bezeichner overload aufgerufen
wird. Als Argumente werden Null-Werte (siehe DefaultNullValue
Syntax.039), eingegeben.
//*********************** Overloading.java ***********************
// Demonstration des Ueberladens einer Methode overload(p). *
// Die Parameter p sind von 6 verschiedenen Typen. *
// Die Argumente sind 13 NullWerte von 10 verschiedenen Typen. *
//****************************************************************
class Overloading
{static String overload(boolean p) {return("boolean");}
static String overload(char p) {return("char ");}
static String overload(int p) {return("int ");}
static String overload(double p) {return("double ");}
static String overload(String p) {return("String ");}
static String overload(char[] p) {return("char[] ");}
public static void main(String[] args) // args ungenutzt
{System.out.println(
" false -> "+overload( false)+"\r\n"
+" (char) 0 -> "+overload( (char) 0 )+"\r\n"
+" '0' -> "+overload( '0' )+"\r\n"
+" (byte) 0 -> "+overload( (byte) 0 )+"\r\n"
+" (short) 0 -> "+overload( (short) 0 )+"\r\n"
+" 0 -> "+overload( 0 )+"\r\n"
+" (long) 0 -> "+overload( (long) 0 )+"\r\n"
+" (float) 0.0 -> "+overload( (float) 0.0)+"\r\n"
+" 0.0 -> "+overload( 0.0)+"\r\n"
+" (String)null -> "+overload( (String) null)+"\r\n"
+" \"\" -> "+overload( "" )+"\r\n"
+" (char[])null -> "+overload( (char[]) null)+"\r\n"
+"new char[]{'0'} -> "+overload(new char[] {'0'})+"\r\n");
}
}
//****************************************************************
Output
--------------------------
false -> boolean
(char) 0 -> char ISOControl NUL mit der Nummer 0
'0' -> char Zeichen '0' mit der Nummer 48
(byte) 0 -> int kein Parameter byte p, aber int p
(short) 0 -> int kein Parameter short p, aber int p
0 -> int
(long) 0 -> double kein Parameter long p, aber double p
(float) 0.0 -> double kein Parameter float p, aber double p
0.0 -> double
(String)null -> String null vom ReferenceType String
"" -> String Zeichenkette der Länge 0
(char[])null -> char[] null vom ReferenceType char[]
new char[]{'0'} -> char[] 1-dim.Reihung mit 1 Komponente '0'
In Kapitel 2 wurde unter "Implizite Konvertierung" bereits das
Prinzip des minimalen Aufwands beim Overloading formuliert:
----------------------------------------------
VVVVVVVV
Minimaler Aufwand beim Overloading (Prinzip)
VVVVVVVV
VV
----------------------------------------------
VV
VV VV
VV Wenn mehrere Methoden bezüglich ihrer Parametertypen als VV
VV "numerisch verlustfrei konvertierbar oder zum nächsten VV
VV Gleitpunktzahl-Wert erweiterbar" in Frage kommen, wird VV
VV diejenige ausgewählt, die den geringsten Konvertierungs- VV
VV aufwand (siehe "Grundtypen und String" Kap.2) erfordert. VV
VV VV
VV z.B. int i=9;double d;d=Math.sqrt(i); // d=3.0 VV
VV VV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
Im Programm Overloading fehlen overload(byte), overload(short),
overload(long) und overload(float). Für (byte)0 und (short)0 wird
ersatzweise overload(int) ausgewählt. Für (long)0 und (float)0
wird ersatzweise overload(double) ausgewählt.
7.6 Ergebnistyp und Rücksprung (return)
Für die Übergabe des Funktionswertes und für den anschließenden
Rücksprung zum Ort des Funktions-Aufrufs (7.2) ist die return-Anweisung vorgesehen. Als strukturierter Sprung wurde die return-Anweisung bereits in 5.4.2 behandelt.
Eine Methode vom Ergebnistyp Type (nonvoid), d.h. eine Funktion
(7), muss am Ende ihres Methoden-Rumpfs eine return-Anweisung mit
Type Expression besitzen. In ihrem Methoden-Rumpf kann es weitere
return-Anweisungen mit Type Expression geben. Eine Methode vom
Ergebnistyp void, d.h. eine Prozedur (7), z.B. main(), kann in
ihrem Methoden-Rumpf return-Anweisungen ohne Expression besitzen.
Im Falle von Prozeduren wirkt auch das Erreichen des Endes des Methoden-Rumpfs wie eine return-Anweisung.
7.7 Hauptprogramm mit main, Argumente
Die (Haupt-) Methode main(String[] args) ist vom Ergebnistyp
void, d.h. eine Prozedur (7), die in einer (Haupt-) Klasse, z.B.
MyMainClass, vereinbart wird und aufgerufen wird mit dem Kommando
Um (public) aufrufbar im Paket der Vereinbarung der Klasse
MyMainClass zu sein, bzw. im aktuellen Vereichnis (default-Paket),
und um (static) instantiiert zu werden ohne dynamische Vereinbarung eines Objekts von MyMainClass im Programm, muss main modifiziert werden (MethodModifier Syntax.070) als
public static void main(String[] args) .
Dem Parameter String[] args von main() können beim Aufruf von
main() Werte zugewiesen werden, z.B.
java MyMainClass "mit" "Argumenten"
(entspricht args=new String[]{"mit","Argumenten"};) .
Gänsefüßchen um die String-Literale können fortgelassen werden,
z.B.
java MyMainClass mit Argumenten .
Die Länge der entstandenen Reihung args kann mit args.length abgefragt werden, hier args.length==2 .
Da es ausser dem Konstruktor für void main(String[] args) keinen
Konstruktor für void main() gibt, muss args auch dann vereinbart
werden, wenn args nicht genutzt wird. Zu Testzwecken kann der
Programmierer auch in Applets ( 13.3 ) zunächst eine Methode
main(String[] args) vereinbaren.
zu Frage
abdeckbare Antwort
-----------------------------------------------
------------------
7 Wie heißt der Ergebnistyp, mit dem
void
eine Prozedur-Vereinbarung beginnt?
7/7.1 Kann der Funktionswert ein Zeiger sein,
ja
d.h. vom Referenztyp?
7/7.1 Können Funktionen neben ihrem Ergebnis-
ja
Wert auch Seiteneffekte haben, wie z.B.
Fehler-Ausdrucke oder globale Zuweisun-
gen?
7.2 Wie findet das Programm nach einem Me-
beim Ansprung
thoden-Aufruf, d.h. einem Ansprung des
wird die Rück-
Unterprogramms, zurück zur betreffenden
sprung- Adresse
Aufruf-Stelle, da es mehrere geben kann?
dem Unterprogramm
mitgeteilt.
7.3 class LoreLey // indirekte Rekursion
{
static void Lore(String s) //ruft Ley
{if(!s.equals("Lore")) {Ley (s);}
else {System.out.print(s+" ");}}
static void Ley (String s) //ruft Lore
{if(!s.equals("Ley" )) {Lore(s);}
else {System.out.print(s+" ");}}
public static void main(String[] args)
{for(String each:args) {Lore(each);}}
}
Was ergeben die folgenden Kommandos?
java LoreLey
java LoreLey Lore
Lore
java LoreLey Ley
Ley
java LoreLey Lore Ley
Lore Ley
java LoreLey LoreLey
StackOverflowError
7.4 Gibt es in Java eine Parameterübergabe
nein (leider)
ähnlich dem 'pass by name' in ALGOL_60,
d.h. inline-Einsetzen des Parameters in
Ausdrücke ohne sofortige Berechnung des
Ausdrucks?
7.4.1 Ersetze in GGT
die "mehrmalige Subtraktion a-=b"
if(a>b) {a%=b;}
durch "einmalige Restbildung a%=b" .
else {b%=a;}
7.4.2 Ist 'pass by reference' in C++ ver-
ja im Effekt,
gleichbar mit Ein/Ausgabe über Reference-
aber reference
Type Parameter in Java?
ist alias in C++
2.1/7.5 Muß beim Überladen die aufrufende Me-
nein, implizite
thode genau die gleiche Signatur aufwei-
Konvertierung
sen wie die aufgerufene Methode?
möglich
7.6 Kann im Methoden-Rumpf von main stehen:
eine return-Anweisung ohne Expression?
ja
eine return-Anweisung mit Expression?
nein
7.7 Schreibe ein Programm Echo, das den
letzten Argument-String des java-Komman-
dos ausgibt, z.B.
java Echo Bürgermeister von W E_S_E_L
E_S_E_L