wiki:Skript/2. Java/ 5. Methoden

Version 21 (modified by tr, 7 years ago) (diff)

--

Methoden

Eine Methode ist ein geschlossenes Stück Programmcode, das wiederkehrende Aufgaben mit demselben Algorithmus löst. Durch die Verwendung von Methoden lässt sich Redundanz vermeiden.

Eine Methode besteht aus einem Methodenkopf und einem Methodenrumpf. Alle Methoden, die nicht den Rückgabetyp void haben, müssen mittels return einen Wert vom Rückgabetyp zurückliefern. Tatsächlich ist void kein in Java existierender Datentyp sondern nur eine Kennzeichnung für "nichts".

Allgemeine Form
Sichtbarkeitsattribut [static] [final] Rückgabetyp Methodenname(Parameterliste) {
  Anweisungsblock / Methodenrumpf
}

Bedeutung der einzelnen Elemente des Methodenkopfes:

  • Sichtbarkeitsattribut: gibt an, wo die Methode verwendet werden kann. Möglich sind die vier Werte public, protected, private sowie ohne Angabe. Details siehe Objektorientierte Programmierung
  • static, optional: Die Methode ist eine Klassenmethode und kann keine Attribute von Objekten verändern. Achtung! die eckigen Klammern [] kennzeichnen nur, dass es sich um eine optionale Angabe handelt. Sie werden nicht im Methodenkopf notiert.
  • final, optional: Die Methode ist final und kann nicht überschrieben werden. Details siehe Objektorientierte Programmierung. Achtung! die eckigen Klammern [] kennzeichnen nur, dass es sich um eine optionale Angabe handelt. Sie werden nicht im Methodenkopf notiert.
  • Rückgabetyp: jeder beliebige Typ oder void. Falls etwas anderes als void angegeben wird, muss die Methode einen Wert des angegebenen Typs mittels return zurückliefern.
  • Parameterliste: besteht aus kommagetrennten Paaren aus Typ und Name, z. B.: int xm, int ym, int radius. Die Parameter sind Variable, die der Methode vom aufrufenden Programmteil übergeben werden, damit die Methode ihre Aufgabe erfüllen kann. Die entsprechenden Werte sind in der Methode unter den in der Parameterliste aufgeführten Namen verfügbar. Zuweisungen an Parameter innerhalb der Methode haben keinen Einfluss auf Variable im aufrufenden Programmteil, sofern es sich um primitive Datentypen handelt.

Signatur

Die Signatur ist ein Teil des Methodenkopfes und besteht aus dem Methodennamen und der Parameterliste. Der Rückgabetyp ist kein Bestandteil der Signatur.

In ein und derselben Klasse darf es nicht mehrere Methoden mit derselben Signatur geben, da der Compiler sonst Methodenaufrufe nicht eindeutig zuordnen könnte.

Externe Sicht und interne Sicht

Bei der Entwicklung von Software gibt es zwei Sichten auf Methoden.

Externe Sicht / Black Box

Die externe Sicht auf eine Methode spiegelt deren Verwendung wider: Entwickler wissen nicht zwingend, wie die Methode intern aufgebaut ist, kennen aber deren Signatur und eine Funktionsbeschreibung. Beispiel dafür sind die Methoden der Java API. In der externen Sicht kann eine Methode als Black Box aufgefasst werden, deren Eingabedaten, Ausgabedaten und Zweck bekannt, deren konkrete Implementierung jedoch unbekannt ist:

sqrt x x Ihr Browser beherrscht kein SVG.
Beispiel
// Verwendung der Methode sqrt aus der Klasse java.lang.Math
// Externe Sicht: wir wissen nicht, wie sqrt implementiert ist
double r = Math.sqrt(42);

// Verwendung der Methode fakultaet, nicht aus java.lang.Math sondern aus dieser
// Klasse, in der wir sie notieren (s. u.)
// wir wissen zwar, wie fakultaet implementiert ist (s. u.), nehmen an dieser
// Stelle aber die externe Sicht ein, da wir die Methode verwenden.
int x = fakultaet(4);

Interne Sicht

Die interne Sicht auf eine Methode spiegelt deren Implementierung wider: Entwickler haben ein konkretes fachliches Problem, für dessen Lösung sich eine Methode anbietet. Es wird definiert,

  • welche Daten für die Lösung notwendig sind (daraus entsteht die Parameterliste),
  • von welchem Typ der Rückgabewert ist,
  • wie die Methode heißen soll (spiegelt üblicherweise das fachliche Problem wider).
Beispiel
// Methode zur Berechnung der Fakultät einer Ganzzahl n
public static int fakultaet(int n) {
  // Zwischenergebnis
  int f = 1;

  for (int i = 1; i <= n; i++) {
    f = f * i;
  }

  // Rückgabe des Ergebnisses an den Aufrufer
  return f;
}

Bei der Entwicklung von Programmen findet häufig eine Vermischung von interner und externer Sicht statt: man programmiert eine Methode, die man in einem anderen Programmteil dann verwendet.

Beispiel
import java.util.Scanner;

public class Beispiel {

  // Methode zur Berechnung der Fakultät einer Ganzzahl n
  public static int fakultaet(int n) {
    // Zwischenergebnis
    int f = 1;

    for (int i = 1; i <= n; i++) {
      f = f * i;
    }

    // Rückgabe des Ergebnisses an den Aufrufer
    return f;
  }

  public static void main(String[] args) {
    Scanner scan = new Scanner(System.in);

    System.out.print("Geben Sie eine Zahl ein: ");
    int x = scan.nextInt();

    // Aufruf der Methode fakultaet ist Teil des Parameters von println
    System.out.println(x + "! = " + fakultaet(x));
  }
}

Im obigen Beispiel sieht man bei der Ausgabe mit println auch, dass Methoden in Ausdrücken überall dort verwendet werden können, wo ein Wert stehen darf, der vom Rückgabetyp der Methode ist. Es ist also nicht erforderlich, dass der Rückgabewert immer einer Variablen zugewiesen wird, sondern der Rückgabewert kann auch implizit in einem Ausdruck verwendet werden.

Ausgabe vs. Rückgabe

Im Zusammenhang mit Methoden ist zwischen den Begriffen Ausgabe und Rückgabe zu unterscheiden:

  • Ausgabe: Eine Ausgabe ist eine Information, die dem Benutzer bereitgestellt wird. Beispiele dafür sind Ausgaben auf der Konsole mittels System.out.print... oder die Anzeige von Daten in einer grafischen Benutzeroberfläche.
  • Rückgabe: Ein Rückgabewert ist eine Information, die zwischen zwei Programmteilen ausgetauscht wird. Üblicherweise ist dies für den Benutzer nicht erkennbar. Konkret handelt es sich bei der Rückgabe von Werten um die Übermittlung des Ergebnisses einer Operation (z. B. Berechnung) an einen Programmteil, der diese Operation initiiert (eine Methode aufgerufen) hat. Methoden geben Rückgabewerte mit Hilfe des Schlüsselwortes return zurück.

lokaler Scope

Variable gelten in Java nur in dem Block, in dem sie deklariert wurden. Dies gilt ebenso für Methoden. In Methoden gibt es zwei verschiedene Arten von lokalen Variablen: Parameter und lokal definierte Variable:

// Methode zur Berechnung der Fakultät einer Ganzzahl n
// n ist der formale Parameter der Methode. Er wird innerhalb der Methode wie eine normale Veriable verwendet.
public static int fakultaet(int n) {
  // Zwischenergebnis
  // f ist eine normale Variable, die nur innerhalb der Methode existiert.
  int f = 1;

  for (int i = 1; i <= n; i++) {
    f = f * i;
  }

  // Rückgabe des Ergebnisses an den Aufrufer
  return f;
}

Methoden können die Werte von Parametern verändern, indem sie ihnen Werte zuweisen. Sofern es sich um primitive Typen handelt, hat dies keinen Einfluss auf Variable außerhalb der Methode. Eine Methode kann Variable von primitiven Typen, die außerhalb der Methode deklariert wurden, nicht verändern. Man spricht dabei von call-by-value: Beim Aufruf der Methode wird vom außerhalb der Methode befindlichen Wert eine Kopie angelegt und der Methode zur Verfügung gestellt. Die Methode hat ausschließlich Zugriff auf diese Kopie. Deswegen haben die Namen von Parametern nur lokale Bedeutung: Ein konkreter Parameter kann denselben Namen wie oder einen anderen Namen als ein formaler Parameter tragen: Es wird keine Verbindung zwischen den Werten innerhalb und außerhalb der Methode hergestellt.

Unabhängig davon ist es schlechter Stil, wenn man Parameter von primitiven Typen innerhalb einer Methode ändert. Man sollte statt dessen eine zusätzliche Hilfsvariable verwenden.

Beispiel für formale und konkrete Parameter
// Methode zur Berechnung der Fakultät einer Ganzzahl n
// n ist der formale Parameter der Methode. Er wird innerhalb der Methode wie eine normale Veriable verwendet.
public static int fakultaet(int n) {
  // Zwischenergebnis
  // f ist eine normale Variable, die nur innerhalb der Methode existiert.
  int f = 1;

  for (int i = 1; i <= n; i++) {
    f = f * i;
  }

  // Rückgabe des Ergebnisses an den Aufrufer
  return f;
}

public static void main(String[] args) {
  // n ist lokal in der Methode main
  int n = 4;
  // n wird zum konkreten Parameter beim Aufruf von fakultaet
  // innerhalb der Methode fakultaet wird ein zweites n durch Kopie dieses n erzeugt
  // es existieren also zwei Variable mit dem Namen n: eines in main, eines in fakultaet
  int x = fakultaet(n);

  // m ist lokal in der Methode main
  int m = 5;
  // m wird zum konkreten Parameter beim Aufruf von fakultaet
  // innerhalb der Methode fakultaet wird n durch Kopie von m erzeugt
  // es existieren also zwei Variable: m in main, n in fakultaet
  int y = fakultaet(m);
}

Falls es sich bei den Parametern nicht um primitve Typen sondern um Objekte handelt, verhält sich die Veränderbarkeit von außerhalb der Methode befindlichen Variablen anders (Details siehe dort). In diesem Fall spricht man von call-by-reference (Aufruf mit einer Referenz oder Zeiger, Adresse).

Vorzeitiges Ende eines Methodenaufrufes

Sobald innerhalb einer Methode das Berechnungsergebnis feststeht, kann die Methode mit return verlassen werden. Dies ist insbesondere im Zusammenhang mit Schleifen und dem Rückgabetyp boolean ein häufig auftretendes Muster:

// Die Methode soll feststellen, ob die übergebene Zahl n die Ziffer 3 enthält
// falls ja soll die Methode true zurückgeben, sonst false
public static boolean enthaeltDrei(int n) {

  // Durchlaufe die gesamte Zahl bis nichts mehr von ihr übrig ist
  while (n > 0) {

    // ist die letzte Ziffer eine 3?
    if (n % 10 == 3) {

      // wir haben festgestellt, dass eine 3 enthalten ist
      // ==> geben true zurück, die Methode endet sofort
      return true;
    }

    // Schneide letzte Ziffer ab
    // Merke: hier wird der Parameter n geändert, dies hat aber nur
    // lokale Auswirkungen innerhalb der Methode (call-by-value)
    n = n / 10;
  }

  // gesamte Zahl wurde geprüft, der Programmablauf ist bis hierhin gekommen
  // es kann also keine 3 enthalten gewesen sein, da die Methode sonst
  // schon verlassen worden wäre ==> keine 3 in n enthalten
  return false;
}

Weitere Beispiele

import java.util.Scanner;

public class Methoden {
  // Ausgabe(!!!) der Zeichenkette "hallo" auf der Konsole
  public static void schreibeHallo() {
    System.out.println("hallo");
  }

  // Ausgabe(!!!) von Sternen entsprechend des Wertes des Parameters n
  public static void schreibeSterne (int n) {
    // die Initialisierung könnte auch lauten i = 0 und die Bedingung wäre dann i < n
    for (int i = 1; i <= n; i++) {
      System.out.print("*");
    }
    // abschließend ein Umbruch
    System.out.println();
  }

  // Rückgabe(!!!) der Fakultät des Parameters n (n!)
  public static int fakultaet(int n) {
    int f = 1;
    for (int i = 1; i <= n; i++) {
      f = f * i;
    }
    return f;
  }

  // Rückgabe des größten gemeinsamen Teilers der beiden Parameter
  public static int ggT(int a, int b) {
    while (a != b) {
      if (a > b) {
        a = a - b;
      } else {
        b = b - a;
      }
    }
    return a;
  }

  // Verwendung der oben notierten Methoden
  public static void main(String[] args) {

    schreibeHallo();

    schreibeSterne(3);
    schreibeSterne(17);

    int m = fakultaet(4);

    schreibeSterne(m - 10);
    schreibeSterne(fakultaet(3));

    Scanner scan = new Scanner(System.in);
    System.out.print("erste Zahl: ");
    int x = scan.nextInt();

    System.out.print("zweite Zahl: ");
    int y = scan.nextInt();

    int z = ggT(x, y);
    System.out.println("ggT(" + x + ", " + y + ") = " + z);
  }
}
// Methoden mit dem gleichen Namen, aber
// unterschiedlicher Parameterliste ==> unterschiedlicher Signatur
public class F {

  // leere Methode, funktionslos
  public static void f() {
  }

  // Rückgabe der Summe 1 + 2 + .. + n
  // Achtung! keine effiziente Implementierung
  // Besser wäre return (n * (n + 1)) / 2;
  public static int f(int n) {
    int s = 0;
    for (int i = 1; i <= n; i++) {
      s = s + i;
    }
    return s;
  }

  // Rückgabe a + b
  public static int f(int a, int b) {
    return a + b;
  }

  // Verwendung der verschiedenen Methoden
  public static void main(String[] args) {

    // Variable namens f
    int f = 7;

    f = f(f);

    f = f(f, f);

    // beliebige Schachtelung von Methodenaufrufen ist möglich
    f = f(f(f, f), f(f(f), f));

    System.out.println(f);
  }
}

Java Insel

Methoden einer Klasse