Tech Archive Help

09 Java – Methoden

Methoden bestimmen das Verhalten von Objekten und des Programms. Sie werden innerhalb einer Klasse angelegt. Wird derselbe Programmcode an mehreren Stellen verwendet, kann dieser in eine Methode ausgelagert werden und stattdessen die Methode aufgerufen werden.

Klassen- & Objektmethoden

Grundsätzlich kann zwischen zwei Methodenarten unterschieden werden.

  • Objektmethoden (auch Instanzmethoden)

  • Klassenmethoden (auch statische Methoden)

Objektmethoden können nur über konkret instanziierte Objekte aufgerufen werden. Klassenmethoden brauchen kein instanziiertes Objekt. Sie können über die Klasse direkt aufgerufen werden. In diesem Kapitel fokussieren wir uns auf die Klassenmethoden, da wir diese aus der main-Methode aufrufen können, ohne dass ein Objekt instanziiert werden muss.

Methodenstruktur

Allgemein wird eine Methode in zwei Bereiche unterteilt. Zum einen den Kopf - dieser beinhaltet alles vom Access Modifier bis zur geschlossenen runden Klammer. Zum anderen den Rumpf - dieser ist der eigentliche Code-Block mit den geschweiften Klammern {}.

// Class methodf access-modifier static return-type methodName(parameter) { // Code... }
// Object method access-modifier return-type methodName(parameter) { // Code... }

Teil des Methodenkopfes

Beschreibung

access-modifier

public, private, protected oder implizit package-private.

static

Keyword für den statischen Aufruf der Methode über den Klassennamen.

type

Der Rückgabetyp der angibt, von welchem Datentyp das Ergebnis der Methode ist, sofern etwas zurückgegeben wird.

methodName

Ein Identifier für den Namen der Methode.

parameter

Ein oder mehrere Werte die optional an diese Methode übergeben werden können.

Methodensignatur

Die Signatur einer Methode besteht aus ihrem Namen und ihrer Parameterliste. Der Rückgabetyp und die Zugriffsmodifizierer sind nicht Teil der Signatur. Die Signatur ist wichtig, um Methoden innerhalb einer Klasse zu unterscheiden, insbesondere bei der Methodenüberladung (engl. method overloading), wo mehrere Methoden denselben Namen, aber unterschiedliche Parameterlisten haben können.

main-Methode

Eine Methode sollte bereits bekannt sein – die main()-Methode.

public static void main(String[] args) { // Code... }

Diese ist die Wichtigste, da sie als zentraler Einstiegspunkt in unsere eigenen Programme fungiert und vorhanden sein muss. Die Methode erwartet ein String[] als Parameter, welches optionale Argumente beinhaltet, die beim Programmstart mit übergeben werden können und innerhalb des Programms verarbeitet werden.

public und static sind Keywords und werden im Kapitel 12 – Modifizierer & Zugriffsrechte genauer erklärt.

An der main()-Methode darf nichts verändert werden, außer den Identifier des Parameters anders zu nennen oder das Array in ein Vararg zu ändern.

public static void start(String[] args) { // Code... }

Rückgabetyp

Eine Methode hat die Möglichkeit ein Ergebnis an den Methodenaufrufer (engl. method caller) zurückzugeben – hier findet auch das Keyword return Anwendung. Der Rückgabewert der zurückgegeben wird, hängt vom Rückgabetyp ab.

Methoden können sowohl primitive Datentypen als auch Objekte zurückgeben. Für die primitiven Datentypen werden deren Keywords angegeben, für Referenztypen deren Klassennamen. Falls die Methode nichts zurückgibt, steht im Methodenkopf das Keyword void.

Zur direkten Weitergabe kann der Rückgabewert z.B. an eine andere Methode oder eine andere Berechnung übermittelt werden. Alternativ kann dieser auch zwischengespeichert werden, anstatt den Wert erneut zu berechnen.

public static void main(String[] args) { System.out.println(isEven(3)); boolean b = isEven(6); System.out.println(b); } public static boolean isEven(int n) { if (n % 2 == 0) { return true; } else { return false; } }

Durch die return-Anweisung ist es möglich eine Methode auch früher zu verlassen. Im folgenden Beispiel erhält die Methode eine Trennzeichenfolge und einen Text. Der Text soll überall dort gesplittet werden, an dessen Stelle dieses Zeichen bzw. diese Zeichenfolge – also der delimeter – vorkommt und gibt die getrennten Wörter in einem String[] zurück.

Die Methode kann jedoch früher verlassen werden und gibt ein leeres Array zurück, wenn der Text null, also nicht vorhanden, oder die Textlänge kleiner oder gleich 0 ist.

public static String[] splitTextBy(String delimeter, String text) { if (text == null || text.length == 0) return new String[0]; String[] words = text.split(delimeter); return words; }

Folgende Methode bekommt einen String mit einem Namen übergeben. Die Methode gibt jedoch nichts zurück, sondern gibt stattdessen den Namen mit einem Begrüßungstext auf der Konsole aus.

public static void printWelcomeMessage(String name) { System.out.println("Hello " + name + "!"); }

Folgende Methode bekommt eine Zahl übergeben und überprüft, ob diese Zahl gerade ist. Wenn ja, wird true zurückgegeben, ansonsten false.

public static boolean isEven(int n) { if (n % 2 == 0) { return true; } else { return false; } }

Die Methode isEven() kann sogar noch vereinfacht werden.

public static boolean isEven(int n) { if (n % 2 == 0) { return true; } else { return false; } }
public static boolean isEven(int n) { if (n % 2 == 0) { return true; } return false; }

Kann die Methode isEven() noch weiter vereinfacht werden?

public static boolean isEven(int n) { return n % 2 == 0; }

Statischer Methodenaufruf

ClassName.methodName(parameter);

Um die beiden Methoden aus dem vorherigen Abschnitt aufzurufen, muss folgendes geschrieben werden:

Main.isEven(4); printWelcomeMessage("Kesares");

Beide Methoden befinden sich in derselben Klasse wie die main()-Methode, aus der wir die beiden Methoden aufrufen. Daher kann auf den vorangestellten Klassennamen Main verzichtet werden.

Methodenüberladung

Es ist möglich mehr als eine Methode mit demselben Methodennamen zu schreiben. Jedoch müssen sich die Methoden entweder in der Anzahl der übergebenen Parameter oder – bei gleicher Anzahl – im Parametertyp unterscheiden.

public void print(int n) { System.out.println(n); } public void print(double n) { System.out.println(n); } public void print(int n1, int n2) { System.out.println(n1 + n2); }

Variable Parameteranzahl

Seit Java 5 ist es möglich eine beliebige Anzahl von Parametern vom gleichen Typ an eine Methode zu übergeben – auch Vararg genannt. Intern erzeugt Java daraus ein Array, dass für die Weiterverarbeitung verwendet werden kann.

public static void method1(int... numbers) { // Code... } public static void method2(String text, boolean b, int... numbers) { // Code... }
public static void main(String[] args) { method1(1, 2, 3, 4, 5); method2("Hallo Kesares!", true, 1, 2, 3, 4, 5); }

Finale Methoden

Finale Methoden werden mit dem Keyword final deklariert. Sie können nicht von Kindklassen überschrieben werden. Das Überschreiben von Methoden wird auch als Polymorphie oder Polymorphismus bezeichnet und findet nur bei der Vererbung Anwendung. Mehr zum Keyword final in Kapitel 12 – Modifizierer und Zugriffsrechte.

public final void print() { // Code... }

Rekursion

Eine rekursive Methode ruft sich selbst immer wieder auf, bis ein bestimmter Zustand erreicht ist – vergleichbar mit einer Schleife.

public static void foo() { foo(); }

Folgende rekursive Methode berechnet die Quersumme einer übergebenen Zahl n und gibt diesen Wert zurück.

public static int calculateChecksum(int n) { if (n <= 0) return 0; return n % 10 + calculateChecksum(n / 10); }
Recursion Meme

Die Math-Klasse

Java bringt von Haus aus eine Klasse mit, die ausschließlich statische Methoden für verschiedene Berechnungen besitzt. Die Math-Klasse enthält Methoden für beispielsweise das Runden von Kommazahlen, das Finden des Max- oder Min-Wertes, Wurzelberechnung, Zufallszahlen, Berechnung von Cosinus, Sinus und sehr viel mehr.

@IntrinsicCandidate public static int abs(int a) { return (a < 0) ? -a : a; }
@IntrinsicCandidate public static long round(double a) { long longBits = Double.doubleToRawLongBits(a); long biasedExp = (longBits & DoubleConsts.EXP_BIT_MASK) >> (DoubleConsts.SIGNIFICAND_WIDTH - 1); long shift = (DoubleConsts.SIGNIFICAND_WIDTH - 2 + DoubleConsts.EXP_BIAS) - biasedExp; if ((shift & -64) == 0) { long r = ((longBits & DoubleConsts.SIGNIF_BIT_MASK) | (DoubleConsts.SIGNIF_BIT_MASK + 1)); if (longBits < 0) { r = -r; } return ((r >> shift) + 1) >> 1; } else { return (long) a; } }
@IntrinsicCandidate public static double pow(double a, double b) { return StrictMath.pow(a, b); }

Call by Value vs. Call by Reference

Wenn eine Methode mit Call-by-Value aufgerufen wird, wird der Wert an diese Methode übergeben. Innerhalb der Methode wird eine Kopie des Werts erstellt und verwendet. Änderungen, die innerhalb der Methode am Parameter vorgenommen werden, wirken sich nicht auf die ursprüngliche Variable außerhalb der Methode aus. Dies ist der Standard für primitive Datentypen in vielen Programmiersprachen, einschließlich Java.

Beim Call-by-Reference wird eine Referenz (ein Zeiger) auf die ursprüngliche Variable an die Funktion übergeben. Das bedeutet, dass die Funktion direkt auf die ursprüngliche Variable zugreift. Änderungen, die innerhalb der Funktion am Parameter vorgenommen werden, wirken sich direkt auf die ursprüngliche Variable aus.

In Programmiersprachen wie C++ kann explizit Call-by-Reference verwendet werden. In Java gibt es kein echtes Call-by-Reference. Es gibt jedoch ein ähnliches Verhalten bei der Übergabe von Objektreferenzen.

Java verwendet ausschließlich Call-by-Value für die Übergabe von Variablen. Aber bei Objekten wird eine Kopie der Referenz (also der Speicheradresse des Objekts) übergeben, nicht das Objekt selbst. Dies führt zu einem Verhalten, das ähnlich wie Call-by-Reference erscheint, wenn mit Objekten gearbeitet wird.

Coding Conventions

  • Methoden sollten mit einem Verb beginnen (calculate, print, remove etc.).

  • Methoden, die einen Rückgabewert vom Typ boolean aufweisen, sollten nach Möglichkeit mit is beginnen. Alternativ kann auch auf has zurückgegriffen werden.

  • Methoden sollten nach der CamelCase-Notation geschrieben werden.

double getAverageOf(int[] nums) {} void printText(String text) {} void removeBuffsFromPlayer(Player player) {} boolean hasEffects() {}

Übung

Erweitert den Taschenrechner mit dem Wissen aus diesem Kapitel um eine Methode, die die Berechnung durchführt und auf der Konsole ausgibt.

public static void main(String[] args) { Scanner sc = new Scanner(System.in); boolean isRunning = true; while (isRunning) {} System.out.print("1st number: "); int x = sc.nextInt(); System.out.print("2nd number: "); int y = sc.nextInt(); System.out.print(""" ================= 1: Addition 2: Subtraction 3: Multiplication 4: Division ================= Option:\s"""); int option = sc.nextInt(); printCalculations(x, y, option); System.out.println("Should another calculation be performed?"); String keepGoing = sc.next(); if (!keepGoing.equalsIgnoreCase("y")) { isRunning = false; } } System.out.println("Bye."); sc.close(); } private static void printCalculations(int x, int y, int option) { switch (option) { case 1 -> System.out.println(x + " + " + y + " = " + (x + y)); case 2 -> System.out.println(x + " - " + y + " = " + (x - y)); case 3 -> System.out.println(x + " * " + y + " = " + (x * y)); case 4 -> System.out.println(x + " / " + y + " = " + (x / y)); default -> System.out.println("This option does not exist."); } }
Last modified: 19 August 2024