- Lernen
- Informatik & Informationswissenschaften
- Programmierung
- Java-Programmierung – Klassen, Vererbung, Collections und Streams
- Java-Methoden und Problemzerlegung
Java-Methoden und Problemzerlegung
return zurück. Finde heraus, wo du welche Variable verwenden kannst, warum eine Methode mit Kopien ihrer Argumente arbeitet und wie der Aufrufstapel laufende Methoden nachverfolgt. Überlade Methoden und sage voraus, welche Version Java aufruft. Zerlege schließlich ein Problem in kleine Methoden, die jeweils eine Aufgabe erfüllen, entferne wiederholten Code und entwickle und teste ein Programm Methode für Methode.AnfängerEinheit 3 von 11 in Kurs: Java-Programmierung – Klassen, Vererbung, Collections und Streams
Was du lernst
- Definiere statische Methoden, rufe sie aus
mainund gegenseitig auf und übergib ihnen die benötigten Informationen durch Parameter.- Definiere eine Methode und rufe sie aufSchreibe Methodenkopf und Methodenrumpf an die richtige Stelle, rufe die Methode über ihren Namen auf und verfolge, wie die Ausführung in die Methode und zurück springt.
- Übergib Informationen über Parameter an eine MethodeDeklariere Parameter mit Typ und Namen, rufe die Methode mit passenden Argumenten auf und verwende mehrere Parameter in der richtigen Reihenfolge.
- Definiere eine Methode und rufe sie auf
- Schreibe Methoden, die einen Wert berechnen und mit
returnzurückgeben, und verwende die zurückgegebenen Werte in Variablen, Ausdrücken und Bedingungen.- Gib einen Wert zurück und verwende ihnDeklariere einen Rückgabetyp, gib ein Ergebnis mit einer return-Anweisung zurück und entscheide im aufrufenden Code, was mit dem Rückgabewert geschieht.
- Kehre innerhalb von Verzweigungen und Schleifen aus einer Methode zurückVerwende return-Anweisungen in if-Anweisungen und Schleifen, stelle sicher, dass jeder Pfad einen Wert zurückgibt, und schreibe Methoden, die eine Ja-Nein-Frage beantworten.
- Gib einen Wert zurück und verwende ihn
- Erkenne, wo du welche Variable verwenden kannst, was eine Methode im aufrufenden Code ändern kann und was nicht und wie der Aufrufstapel laufende Methoden nachverfolgt.
- Bestimme, wo du welche Variable verwenden kannstWende die Regeln für die Gültigkeitsbereiche lokaler Variablen, Parameter und in Blöcken deklarierter Variablen an und behebe die Kompilierfehler, die durch Fehler beim Gültigkeitsbereich entstehen.
- Verstehe, dass Argumentwerte als Kopien übergeben werdenSage voraus, was eine Methode im aufrufenden Code ändern kann: Die Methode arbeitet mit Kopien der Argumentwerte, sodass eine Neuzuweisung an einen Parameter die Variable des Aufrufers nicht ändert.
- Verfolge Methodenaufrufe auf dem AufrufstapelVerfolge, wie Java laufende Methoden in Stapelrahmen erfasst, vollziehe Programme nach, in denen Methoden andere Methoden aufrufen, und lies einen Stacktrace.
- Bestimme, wo du welche Variable verwenden kannst
- Gib mehreren Methoden denselben Namen mit unterschiedlichen Parameterlisten und sage voraus, welche Version Java für bestimmte Argumente aufruft.
- Schreibe überladene MethodenDeklariere Methoden mit demselben Namen, erkenne, welche Unterschiede eine gültige Überladung ergeben, und überlade Methoden, wenn sie dieselbe Aufgabe erfüllen.
- Sage voraus, welche überladene Methode ausgeführt wirdErmittle anhand der Argumenttypen, welche Version Java auswählt, berücksichtige dabei automatische erweiternde Typumwandlungen und erkenne Aufrufe, die sich nicht kompilieren lassen.
- Schreibe überladene Methoden
- Zerlege ein Problem in kleine Methoden, die jeweils eine Aufgabe erfüllen, ersetze wiederholten Code durch parametrisierte Methoden und entwickle und teste ein Programm Methode für Methode.
- Gib jeder Methode eine klar abgegrenzte AufgabeErkenne ein gut aufgeteiltes Programm, teile Code auf, der zu viel tut, und trenne Berechnung von Ein- und Ausgabe.
- Ersetze wiederholten Code durch eine parametrisierte MethodeFinde Code, der mit kleinen Unterschieden wiederholt wird, forme ihn zu einer Methode um, deren Parameter die Unterschiede aufnehmen, und prüfe, ob das Programm sich weiterhin gleich verhält.
- Entwirf ein Programm nach dem Top-down-Prinzip und teste jede Methode einzelnPlane ein Programm als Folge von Methodenaufrufen, schreibe es schrittweise mit vorläufigen Stubs und teste jede Methode mit Eingaben, deren Ergebnisse du kennst.
- Gib jeder Methode eine klar abgegrenzte Aufgabe
Java-Methoden und Problemzerlegung einfach erklärt
1Schreibe eigene Methoden und rufe sie auf
Ein Programm lässt sich leichter nachvollziehen, wenn sinnvolle Schritte Namen haben. Eine Methode gibt einer Gruppe von Anweisungen einen Namen, und ein Aufruf führt diese Anweisungen dort aus, wo sie gebraucht werden. Beginne mit einer Methode, die immer dieselbe Zeile ausgibt, und ergänze dann Parameter, damit eine Definition unterschiedliche Eingaben verarbeiten kann. Wenn du einen Methodenkopf liest und ihn mit einem Aufruf abgleichst, erkennst du, welche Informationen jeder Schritt benötigt, bevor du seinen Rumpf untersuchst.
1.1 Definiere eine Methode und rufe sie auf
Eine Methode ist ein benannter Block von Anweisungen, der eine Aufgabe erfüllt. Die Definition einer Methode stellt die Aufgabe bereit; ihr Aufruf führt sie aus.
Lies Kopf und Rumpf
public static void printLine() {
System.out.println("----------");
}
Die erste Zeile ist der Methodenkopf. Lies seine Teile von links nach rechts:
publicerlaubt Code in anderen Klassen, die Methode aufzurufen.staticordnet die Methode der Klasse zu. Für ihren Aufruf ist kein Objekt nötig.voidbesagt, dass die Methode keinen Wert zurückgibt.printLinebenennt die Aufgabe.()ist die leere Parameterliste: Diese Methode benötigt keine Eingabe.
Die geschweiften Klammern umschließen den Methodenrumpf, also die Anweisungen, die bei einem Aufruf ausgeführt werden. Zwischen diesem Kopf und seiner öffnenden Klammer steht kein Semikolon.
Setze die Definition in die Klasse
public class Main {
public static void main(String[] args) {
System.out.println("Start");
printLine();
System.out.println("Middle");
printLine();
}
public static void printLine() {
System.out.println("----------");
}
}
main und printLine sind getrennte Methoden innerhalb von Main. Die schließende Klammer von main steht vor der Definition von printLine. Eine Methode kann nicht innerhalb einer anderen Methode definiert werden; eine falsch gesetzte Klammer kann „illegal start of expression“ verursachen.
Die Definitionen können in beliebiger Reihenfolge stehen. Wenn du printLine über main verschiebst, ändert das nicht, wann die Methode ausgeführt wird. Eine Methode, die nie aufgerufen wird, wird nicht allein deshalb ausgeführt, weil ihre Definition in der Datei steht.
Verfolge einen Aufruf und die Rückkehr zum Aufrufer
Der Aufruf printLine(); besteht aus dem Namen, Klammern und einem Semikolon, weil der Aufruf als eigene Anweisung steht. Die Ausführung beginnt in main:
- Gib „Start“ aus.
- Wechsle zu
printLine, gib zehn Bindestriche aus und fahre dann nach dem Aufruf fort. - Gib „Middle“ aus.
- Wechsle erneut zu
printLineund gib erneut zehn Bindestriche aus.
Die Ausgabe lautet:
Start
----------
Middle
----------
Beide Aufrufe verwenden dieselbe Definition. Änderst du die ausgegebene Zeile, ändert sie sich an beiden Stellen, die sie verwenden.
Benenne Methoden genau und rufe sie korrekt auf
Verwende ein Verb oder eine Verbgruppe in camelCase mit kleinem Anfangsbuchstaben: printReceipt, calculateAverage oder isValidCode. Java unterscheidet Groß- und Kleinschreibung: PrintLine() findet keine Methode namens printLine. Auch printLine; schlägt fehl; die Klammern gehören zum Aufruf.
Schreibe bei einer statischen Methode in einer anderen Klasse zuerst den Klassennamen. Math.max(3, 8) ergibt 8, Math.abs(-4) ergibt 4 und Math.sqrt(16.0) ergibt 4.0. Deine eigenen Methoden in derselben Klasse kannst du allein über ihren Namen aufrufen.
Diese Beispiele verwenden die klassische klassenbasierte Form mit public static void main(String[] args). Java 25 unterstützt auch kompakte Quelldateien mit void main() und ohne umgebende Klasse. Behalte beim Ausführen dieser Beispiele die klassische Form bei; ihre Hilfsmethoden werden mit static deklariert. Methoden wie System.out.println, die du aus der Java-Bibliothek aufrufst, müssen nicht selbst statisch sein.
Wichtige Erkenntnisse
- Setze Methodendefinitionen innerhalb der Klasse und außerhalb anderer Methoden.
- Eine Definition stellt eine Aufgabe bereit; ein Aufruf führt sie aus und setzt danach beim Aufrufer fort.
- Schreibe bei einem Aufruf Klammern und ein Semikolon, wenn der Aufruf eine Anweisung ist.
- Verwende einen aussagekräftigen Namen in camelCase und beachte seine genaue Groß- und Kleinschreibung.
- Rufe eine statische Methode einer anderen Klasse mit deren Klassennamen auf, zum Beispiel
Math.max(3, 8).
1.2 Übergib Informationen über Parameter an eine Methode
Ein Parameter ermöglicht es einer Methode, Werte zu verarbeiten, die sich von Aufruf zu Aufruf ändern. Ein Argument ist der Wert, der beim Aufruf der Methode für diesen Parameter übergeben wird.
Gib jeder Eingabe einen Typ und einen Namen
public static void printStars(int count) {
for (int i = 0; i < count; i++) {
System.out.print("*");
}
System.out.println();
}
int count deklariert einen Parameter. In printStars(3); wird das Argument 3 zum Anfangswert von count. Die Schleife läuft dreimal, gibt also *** aus; anschließend beendet println() die Zeile. printStars(8); verwendet denselben Rumpf mit dem Wert 8 für count.
Der Typ steht in der Definition, nicht im Aufruf. Schreibe printStars(5);, nicht printStars(int 5);.
Werte Argumente vor dem Eintritt in den Rumpf aus
Ein Argument kann ein Literal, eine Variable oder ein Ausdruck sein:
int n = 4;
printStars(n * 2);
Java wertet zuerst n * 2 aus. Es übergibt den resultierenden Wert 8, sodass der Rumpf mit dem Wert 8 für count beginnt. Der Variablenname beim Aufrufer muss nicht mit dem Parameternamen übereinstimmen.
Ordne mehrere Parameter nach Position zu
Jeder Parameter braucht seinen eigenen Typ. Eine Methode für Rechtecke kann mit diesem Kopf beginnen:
public static void printRectangle(int width, int height)
Der Aufruf printRectangle(5, 2); gibt width den Wert 5 und height den Wert 2. Vertauschst du die Argumente, erhältst du ein Rechteck mit Breite 2 und Höhe 5. Beide Aufrufe lassen sich kompilieren, weil beide Argumente int-Werte sind; nur die beabsichtigte Form zeigt dir, welche Reihenfolge richtig ist. (int width, height) im Kopf schlägt fehl, weil für height kein Typ deklariert ist.
Unterschiedliche Typen erleichtern es, manche Fehler in der Reihenfolge zu erkennen:
public static void printRepeated(String text, int times) {
for (int i = 0; i < times; i++) {
System.out.print(text);
}
System.out.println();
}
printRepeated("ab", 3); gibt „ababab“ aus. printRepeated(3, "ab"); passt nicht zu einem String gefolgt von einem int-Wert. Auch die Anzahl der Argumente muss passen: Bei printRectangle(5); erhält ein Parameter keinen Wert.
Prüfe kompatible numerische Typen
Java erweitert einen int-Wert automatisch zu einem double-Wert. Eine Methode mit den Parametern (double km, double hours) akzeptiert die Argumente (150, 2) und erhält dafür 150.0 und 2.0. Jeder int-Wert kann als double genau dargestellt werden.
Java wandelt ein double-Argument nicht implizit in einen int-Parameter um. printStars(2.9); schlägt fehl, weil bei der Umwandlung der Nachkommateil verloren ginge. Der explizite Cast in printStars((int) 2.9); ergibt 2, sodass zwei Sterne ausgegeben werden. Verwende einen Cast nur, wenn das Abschneiden dieses Teils beabsichtigt ist.
Wähle für jede Information, die die Aufgabe benötigt, einen Parameter. Da printRepeated Text und eine Anzahl erhält, lässt sich die Methode mit Tastatureingaben, einem festen Testaufruf oder aus einer anderen Methode verwenden. Ihr Rumpf muss nicht entscheiden, woher diese Werte stammen.
Wichtige Erkenntnisse
- Ein Parameter wird im Kopf deklariert; ein Argument liefert seinen Wert beim Aufruf.
- Java wertet Argumentausdrücke aus, bevor die aufgerufene Methode ausgeführt wird.
- Argumente müssen nach Position, Anzahl und kompatiblem Typ zu den Parametern passen.
- Schreibe für jeden Parameter einen Typ, aber keine Typen im Aufruf.
- Ein int-Wert kann zu double erweitert werden; ein double-Wert benötigt einen expliziten Cast, um zu int zu werden.
- Das Vertauschen von Argumenten desselben Typs kann sich kompilieren lassen und trotzdem das Ergebnis ändern.
2Gib Ergebnisse aus Methoden zurück
Eine Methode kann mehr als nur etwas ausgeben: Sie kann einen Wert an den aufrufenden Code zurückgeben. Dadurch lässt sich eine Berechnung in einer weiteren Berechnung, einer Bedingung oder einer späteren Auswertung verwenden. Verfolge, wie Rückgabetypen, Ausdrücke und Zuweisungen zusammenwirken, und verwende dann return-Anweisungen in Verzweigungen und Suchen. Die Position einer return-Anweisung ist entscheidend: Sie bestimmt sowohl, welche Antwort den Aufrufer erreicht, als auch, wie viel von der Methode ausgeführt wird.
2.1 Gib einen Wert zurück und verwende ihn
Ein Rückgabewert ist ein Ergebnis, das eine Methode an ihren Aufrufer zurückgibt. Sobald ein Aufruf endet, kann er als Ausdruck mit dem Typ dieses Ergebnisses verwendet werden.
Deklariere, was zurückgegeben wird
public static int sum(int a, int b) {
return a + b;
}
int ersetzt void im Kopf, weil diese Methode eine Ganzzahl zurückgibt. Die return-Anweisung wertet a + b aus und gibt den Wert an die Aufrufstelle zurück. Für sum(4, 7) ist dieser Wert 11.
Der Aufrufer entscheidet, was als Nächstes geschieht:
int total = sum(4, 7);
System.out.println(sum(4, 7));
int doubled = sum(2, 3) * 2;
Die erste Zeile speichert 11, die zweite gibt 11 aus und die dritte speichert 10. Ein Rückgabewert kann auch in einer Bedingung verwendet werden, etwa if (sum(a, b) > 10).
Ein kompatibles int-Ergebnis kann bei der Rückgabe aus einer double-Methode erweitert werden. Ein double-Ergebnis kann ohne passende explizite Umwandlung nicht aus einer int-Methode zurückgegeben werden.
Berechne den Ausdruck vor der Umwandlung des Ergebnisses
public static double average(int a, int b) {
return (a + b) / 2.0;
}
Für average(3, 4) ist der Zähler 7. Die Division durch das double-Literal 2.0 ergibt 3.5. Lautete der Ausdruck (a + b) / 2, würde Java zuerst zwei int-Werte dividieren und 3 erhalten. Erst danach würde es diesen Wert zum Rückgabewert 3.0 erweitern. Ein double-Rückgabetyp macht eine bereits im Rumpf ausgeführte Ganzzahldivision nicht rückgängig.
Unterscheide Rückgabe und Ausgabe
| Verhalten der Methode | Was der Aufrufer erhält | Was auf dem Bildschirm erscheint |
|---|---|---|
return a + b; in einer int-Methode | Die Summe | Nichts durch diese Anweisung |
System.out.println(a + b); in einer void-Methode | Kein Wert | Die Summe |
Ein void-Aufruf kann keinen Wert in einer Zuweisung oder einem anderen Aufruf liefern. Ist printSum eine void-Methode, schlägt int total = printSum(1, 2); fehl, obwohl printSum eine Zahl ausgibt. Verwende für Berechnungen eine Methode mit Rückgabewert und überlasse dem Aufrufer die Entscheidung über die Ausgabe.
Gib Text und Ja-Nein-Ergebnisse zurück
Zu den Rückgabetypen gehören auch String und boolean. Eine Begrüßungsmethode kann "Hello, " + name + "!" zurückgeben, was für das Argument "Ana" „Hello, Ana!“ ergibt. Eine Methode isEven(int n) kann n % 2 == 0 zurückgeben.
Eine Formatierungsmethode wandelt eine Zahl in wiederverwendbaren Text um:
public static String formatDuration(int minutes) {
return minutes / 60 + " h " + minutes % 60 + " min";
}
Für 135 ergibt die Ganzzahldivision 2 volle Stunden und der Divisionsrest 15 Minuten. Das Ergebnis ist „2 h 15 min“; für 60 ist es „1 h 0 min“.
Behalte oder kombiniere das Ergebnis
Ein Aufruf wie sum(4, 7); lässt sich kompilieren, verwirft aber sein Ergebnis. Gibt square(x) einen neuen Wert zurück, verwende x = square(x);, um ihn in x zu speichern.
Ein Ergebnis kann auch unmittelbar zum Argument werden. In Math.max(sum(1, 2), 4) ergibt sum(1, 2) zuerst 3, danach ergibt Math.max(3, 4) den Wert 4. Werte den inneren Aufruf aus, bevor du sein Ergebnis im äußeren verwendest.
Wichtige Erkenntnisse
- Der deklarierte Rückgabetyp sagt dem Aufrufer, welche Art von Ergebnis der Aufruf liefert.
- Eine Rückgabe liefert einen Wert; eine Ausgabe zeigt einen Wert an.
- Der Ausdruck wird berechnet, bevor sein Ergebnis in den Rückgabetyp umgewandelt wird.
- Speichere einen Rückgabewert, wenn späterer Code ihn benötigt; ein allein stehender Aufruf verwirft ihn.
- Ein Rückgabewert kann als Argument an eine andere Methode übergeben werden.
2.2 Kehre innerhalb von Verzweigungen und Schleifen aus einer Methode zurück
Eine return-Anweisung beendet den aktuellen Methodenaufruf sofort. Auch innerhalb einer Bedingung oder Schleife verlässt sie die ganze Methode, nicht nur diesen Block.
Gib jedem Pfad eine Antwort
public static int larger(int a, int b) {
if (a > b) {
return a;
}
return b;
}
larger(9, 4) gibt innerhalb des if-Blocks 9 zurück. Bei larger(2, 6) wird der if-Rumpf übersprungen und das abschließende return gibt 6 zurück. Gleiche Eingaben wie (5, 5) erreichen ebenfalls das abschließende return, das 5 zurückgibt. Nach dem ersten return ist kein else nötig, weil dieser Pfad die Methode bereits verlassen hat.
In einer gewöhnlichen Methode mit Rückgabewert darf kein möglicher Pfad die schließende Klammer erreichen, ohne einen Wert zu liefern. Eine Vorzeichenmethode, die nur für positive und negative Eingaben zurückkehrt, lässt null ohne Antwort:
public static String sign(int n) {
if (n > 0) {
return "positive";
} else if (n < 0) {
return "negative";
}
return "zero";
}
Das abschließende return vervollständigt die Methode. Zwei getrennte if-Anweisungen, von denen eine n > 0 und die andere n <= 0 prüft, benötigen weiterhin ein abschließendes return oder eine eindeutige if/else-Struktur. Javas Rückgabeprüfung leitet nicht ab, dass diese beiden Vergleiche alle Ganzzahlen abdecken. Eine Anweisung direkt nach einem unbedingten return im selben Block ist unerreichbar und wird abgelehnt.
Beende eine Suche im richtigen Moment
public static int firstMultipleOf7(int start, int end) {
for (int i = start; i <= end; i++) {
if (i % 7 == 0) {
return i;
}
}
return -1;
}
Für (10, 20) verwirft die Schleife 10, 11, 12 und 13 und gibt dann 14 zurück. Der Aufruf endet, ohne 15 bis 20 zu prüfen. Für (15, 20) endet die Schleife ohne Treffer, sodass das return danach -1 zurückgibt, das vereinbarte Signal „nicht gefunden“.
Ein else { return -1; } innerhalb der Schleife würde nach dem ersten erfolglosen Kandidaten abbrechen. Die Suche ab 10 würde dann einen Misserfolg melden, bevor sie 14 erreicht. Die Rückgabe für einen Misserfolg gehört hinter die Schleife, nachdem alle Kandidaten geprüft wurden.
Gib die Bedingung selbst zurück
Ein Vergleich liefert bereits einen boolean-Wert. Schreibe return n % 2 == 0; statt eines if/else, das true und false getrennt zurückgibt. Der Name einer booleschen Methode beginnt oft mit is oder has, sodass sich ein Aufruf innerhalb eines if natürlich liest.
public static boolean isLeapYear(int year) {
return (year % 4 == 0 && year % 100 != 0)
|| year % 400 == 0;
}
Dies gibt für 2024 und 2000 den Wert true, für 1900 und 2023 aber false zurück. Verwende if (isLeapYear(y)) direkt.
Verlasse eine void-Methode vorzeitig
Eine void-Methode verwendet return; ohne Wert. Zum Beispiel kann printSquareRoot(double x) bei einer negativen Eingabe „No real square root“ ausgeben und sofort zurückkehren. Für andere Eingaben kann die Methode Math.sqrt(x) ausgeben, das für 2.25 den Wert 1.5 ergibt. return 5; schlägt in einer void-Methode fehl, weil ihr Kopf keinen Rückgabewert verspricht.
Wichtige Erkenntnisse
returnbeendet den aktuellen Aufruf sofort, auch innerhalb einer Schleife.- Eine Methode mit Rückgabewert darf ihre schließende Klammer nicht ohne Antwort erreichen.
- Setze die Rückgabe für eine erfolglose Suche hinter die Schleife, damit spätere Kandidaten geprüft werden können.
- Ein Vergleich kann direkt als boolean-Wert zurückgegeben werden.
- Verwende
return;ohne Wert, um eine void-Methode vorzeitig zu verlassen. - Code direkt nach einem unbedingten return im selben Block ist unerreichbar.
3Verfolge Variablenwerte und Aufrufe im Programmablauf
Ein Methodenaufruf gibt einer Hilfsmethode keinen Zugriff auf jede Variable im Programm. Jeder Aufruf hat seine eigenen Parameter und lokalen Variablen, und Java hält diese Werte in einem eigenen Stapelrahmen. Nutze den Gültigkeitsbereich, um zu entscheiden, wo ein Name zulässig ist, die Wertübergabe, um vorherzusagen, was Zuweisungen ändern können, und den Aufrufstapel, um noch nicht abgeschlossene Aufgaben zu verfolgen. Diese drei Ideen erklären, warum zwei Variablen denselben Namen haben und dabei unterschiedliche Werte enthalten können.
3.1 Bestimme, wo du welche Variable verwenden kannst
Der Gültigkeitsbereich einer Variablen ist der Bereich des Codes, in dem ihr Name verwendet werden kann. Betrachte zuerst die geschweiften Klammern um ihre Deklaration: Ein lokaler Name gehört zu einer bestimmten Methode oder einem bestimmten Block.
Lokale Variablen des Aufrufers bleiben beim Aufrufer
public static void main(String[] args) {
int price = 20;
printDouble(price);
}
public static void printDouble(int price) {
System.out.println(price * 2);
}
Das price in main und der Parameter price in printDouble sind getrennte Variablen. Das Argument liefert den Wert, den die Hilfsmethode benötigt, sodass sie 40 ausgibt.
Wenn du den Parameter entfernst und printDouble() aufrufst, kann die Hilfsmethode trotzdem nicht das lokale price von main lesen. Ihr Rumpf würde mit „cannot find symbol“ fehlschlagen. Der Aufruf einer Methode aus einer anderen verbindet ihre Gültigkeitsbereiche nicht. Verschiedene Methoden dürfen jeweils den Namen count verwenden, ohne ihre Variablen zu verbinden.
Ein verschachtelter Block schafft einen kleineren Gültigkeitsbereich
Eine lokale Variable kann von ihrer Deklaration bis zur schließenden Klammer ihres Blocks verwendet werden. Ein Parameter ist im gesamten Rumpf seiner Methode verfügbar. Ein in for (int i = 0; i < 3; i++) deklarierter Zähler gehört zu dieser Schleife; i kann danach nicht gelesen werden.
Um ein Ergebnis nach einer Schleife zu behalten, deklariere es vor der Schleife:
int sum = 0;
for (int i = 1; i <= 5; i++) {
sum += i;
}
System.out.println(sum);
Durch die Additionen nimmt sum nacheinander die Werte 1, 3, 6, 10 und 15 an. Die abschließende Ausgabe kann sum lesen, weil der umgebende Block noch nicht beendet ist. i wird nur für die Schleife benötigt und bleibt innerhalb ihres Gültigkeitsbereichs.
Wenn du int sum = 0; in den Schleifenrumpf verschiebst, entsteht bei jedem Durchlauf ein neues sum. Jedes beginnt bei null und verschwindet am Ende dieses Durchlaufs. Es kann weder eine laufende Summe speichern noch nach der Schleife ausgegeben werden.
Prüfe Namen und Anfangswerte getrennt
Zwei lokale Variablen mit demselben Namen dürfen innerhalb einer Methode keine überlappenden Gültigkeitsbereiche haben. Eine Methode mit einem Parameter x kann keine weitere lokale Variable x deklarieren; eine Schleife, die i deklariert, kann in ihrem Rumpf kein weiteres i deklarieren. Benenne die zweite Variable um oder verwende die vorhandene.
Zwei aufeinanderfolgende Schleifen dürfen jeweils int i deklarieren. Der Gültigkeitsbereich der ersten Schleife ist vor der zweiten Deklaration beendet. Die geschweiften Klammern, nicht die ganze Datei, bestimmen diese Wiederverwendung.
Auch ein zulässiger Name braucht einen Wert, bevor er gelesen wird. int total; total++; schlägt fehl, weil ++ den alten Wert lesen muss und eine lokale Variable keinen automatischen Anfangswert erhält. Verwende int total = 0; für eine laufende Summe.
Halte eine lokale Variable im kleinsten Block, der sie benötigt. Benötigt eine Methode Informationen von ihrem Aufrufer, mache diese Informationen zu einem Parameter. Benötigt der Aufrufer ein Ergebnis, gib es zurück. Dann zeigen Kopf und Rumpf der Methode, woher ihre Daten stammen.
Wichtige Erkenntnisse
- Ein lokaler Name ist von seiner Deklaration bis zum Ende seines Blocks verfügbar.
- Eine aufgerufene Methode kann die lokalen Variablen des Aufrufers nicht über deren Namen lesen.
- Deklariere einen Akkumulator vor einer Schleife, wenn er eine Summe behalten oder danach gelesen werden muss.
- Gültigkeitsbereiche lokaler Namen dürfen sich innerhalb einer Methode nicht überlappen, und lokale Variablen brauchen vor ihrer Verwendung einen Wert.
3.2 Verstehe, dass Argumentwerte als Kopien übergeben werden
Java übergibt Argumente als Wert. Zu Beginn eines Aufrufs erhält jeder Parameter eine Kopie des Werts seines Arguments. Eine Zuweisung an den Parameter ändert diese lokale Variable.
Verfolge die Kopie getrennt
public static void main(String[] args) {
int score = 10;
addBonus(score);
System.out.println(score);
}
public static void addBonus(int score) {
score = score + 5;
System.out.println(score);
}
Es gibt zwei Variablen namens score. Verfolge ihre Werte zu jedem Zeitpunkt:
| Zeitpunkt | score in main | score in addBonus |
|---|---|---|
| Vor dem Aufruf | 10 | Noch kein Aufruf |
Beim Eintritt in addBonus | 10 | 10 |
| Nach der Zuweisung in der Hilfsmethode | 10 | 15 |
| Nach der Rückkehr der Hilfsmethode | 10 | Aufruf beendet |
Die Hilfsmethode gibt 15 aus, dann gibt main den Wert 10 aus. Ein gemeinsamer Name bedeutet nicht, dass die beiden Variablen denselben Speicherplatz nutzen. Eine Umbenennung des Parameters würde dieses Verhalten nicht ändern.
Gib den neuen Wert zurück und speichere ihn
public static int addBonus(int score) {
return score + 5;
}
Schreibe bei dieser Version score = addBonus(score);. Java kopiert den alten Wert in den Parameter, berechnet 15, gibt diesen Wert zurück und weist ihn der Variablen des Aufrufers zu. Die Zuweisung beim Aufrufer ist der Schritt, der diese Variable ändert.
Ein Ergebnis kann auch an anderer Stelle gespeichert werden. Multipliziert triple(int n) sein lokales n mit 3 und gibt es zurück, bleibt bei int a = 4; int b = triple(a); der Wert von a gleich 4 und b erhält den Wert 12. Die Ausgabe von a + " " + b zeigt „4 12“.
Wende dieselbe Regel auf String-Referenzen an
Eine String-Variable enthält eine Referenz auf ein String-Objekt. Java kopiert diese Referenz in einen String-Parameter:
public static void exclaim(String text) {
text = text + "!";
}
Verweist word beim Aufrufer auf „hi“, verweist word nach exclaim(word); weiterhin auf „hi“. Die Konkatenation erzeugt einen neuen String und die Zuweisung ändert nur, worauf der Parameter verweist. Der vorhandene Inhalt von Strings kann nicht geändert werden.
Um den neuen Text zu behalten, gib text + "!" aus einer String-Methode wie exclaimed zurück und schreibe dann word = exclaimed(word);.
Bei veränderlichen Objekten wie Listen kann eine kopierte Referenz weiterhin auf dasselbe Objekt zeigen. Eine Methode kann den Inhalt dieses Objekts über die kopierte Referenz ändern. Eine Neuzuweisung an den Parameter kann weiterhin nicht bewirken, dass die Variable des Aufrufers auf ein anderes Objekt verweist. Sowohl für kopierte Referenzen als auch für kopierte Zahlen gilt die Wertübergabe.
Du darfst einem Parameter einen neuen Wert zuweisen, aber eine separate lokale Variable wie result kann eine längere Berechnung leichter lesbar machen: Sie hält die ursprüngliche Eingabe verfügbar und zeigt zugleich, welcher Wert zurückgegeben wird.
Wichtige Erkenntnisse
- Jeder Parameter erhält eine Kopie des Werts seines Arguments.
- Eine Neuzuweisung an einen Parameter lässt die Variable des Aufrufers unverändert.
- Um einen berechneten Ersatzwert zu behalten, gib ihn zurück und weise ihn beim Aufrufer zu.
- Eine kopierte Objektreferenz kann auf dasselbe veränderliche Objekt zugreifen, aber eine Neuzuweisung an den Parameter bleibt lokal.
3.3 Verfolge Methodenaufrufe auf dem Aufrufstapel
Der Aufrufstapel erfasst Aufrufe, die begonnen haben, aber noch nicht beendet sind. Jeder Aufruf erhält einen Stapelrahmen mit seinen Parametern, lokalen Variablen und der Stelle, an der sein Aufrufer fortsetzt.
Lege einen Rahmen auf den Stapel und entferne ihn nach der Ausführung
Der Stapel folgt dem LIFO-Prinzip: Was zuletzt hineinkommt, wird zuerst entfernt. Der neueste Aufruf liegt oben und wird ausgeführt, während sein Aufrufer darunter wartet. Wenn er zurückkehrt, entfernt Java seinen Rahmen und setzt beim Aufrufer fort.
public static void main(String[] args) {
int total = totalSeconds(2, 30);
System.out.println(total);
}
public static int totalSeconds(int minutes, int seconds) {
int fromMinutes = toSeconds(minutes);
return fromMinutes + seconds;
}
public static int toSeconds(int minutes) {
return minutes * 60;
}
Verfolge die noch nicht abgeschlossenen Aufgaben:
mainbeginnt und rufttotalSeconds(2, 30)auf. Die Zuweisung antotalmuss warten.- Der neue Rahmen enthält
minutes = 2undseconds = 30. Die Methode rufttoSeconds(minutes)auf und wartet auffromMinutes. toSecondserhält ein eigenesminutes = 2. Die Methode gibt120zurück und ihr Rahmen wird entfernt.totalSecondsspeichertfromMinutes = 120, addiert30und gibt150zurück. Sein Rahmen wird entfernt.mainspeicherttotal = 150und gibt150aus.
An der tiefsten Stelle liegen die Rahmen von unten nach oben in der Reihenfolge main, totalSeconds und toSeconds. Beide Hilfsmethoden haben einen Parameter namens minutes, aber diese Parameter liegen in verschiedenen Rahmen. Die lokalen Variablen des wartenden Aufrufers werden für die Methode darüber nicht sichtbar.
Ein späterer Aufruf erhält einen neuen Rahmen. Ein lokaler Zähler merkt sich seinen vorherigen Wert nicht, nachdem sein Aufruf beendet wurde; der Rückgabewert kann ein Ergebnis zurückübermitteln.
Lies einen Stacktrace vom Fehler nach außen
Bei einer unbehandelten Ausnahme wird ein Stacktrace ausgegeben, der zeigt, wo der Fehler auftrat und welche Aufrufe dorthin führten. Hier ist eine vollständige Datei:
public class Main {
public static void main(String[] args) {
report(300, 0);
}
public static void report(int meters, int minutes) {
int speed = perMinute(meters, minutes);
System.out.println(speed + " m per minute");
}
public static int perMinute(int meters, int minutes) {
return meters / minutes;
}
}
Die erste Ausgabezeile lautet Exception in thread "main" java.lang.ArithmeticException: / by zero. Die Rahmen darunter sind:
at Main.perMinute(Main.java:12)
at Main.report(Main.java:7)
at Main.main(Main.java:3)
Der oberste Rahmen weist auf die Ganzzahldivision durch null hin. Die nächsten beiden zeigen die Aufrufe, die dorthin führten. Die Zeilennummern gehören genau zu diesem Aufbau der Datei; wenn du eine Zeile hinzufügst, ändern sie sich.
Der Stapel hat begrenzten Platz. Eine Kette, die fortlaufend Aufrufe hinzufügt, ohne sie enden zu lassen, kann einen StackOverflowError verursachen. In der Stapelansicht eines Debuggers kannst du dieselbe Kette untersuchen, während das Programm an einem Haltepunkt angehalten ist, einschließlich der eigenen Variablen jedes wartenden Aufrufs.
Wichtige Erkenntnisse
- Jeder Aufruf legt einen neuen Rahmen auf den Stapel; eine Rückkehr entfernt diesen Rahmen und setzt beim Aufrufer fort.
- Der neueste noch nicht beendete Aufruf wird zuerst ausgeführt: Der Stapel folgt dem LIFO-Prinzip.
- Gleichnamige lokale Variablen in verschiedenen Rahmen sind getrennte Variablen.
- Verfolge einen Rückgabewert zurück in den wartenden Ausdruck oder die wartende Zuweisung.
- Lies einen Stacktrace vom Fehlerrahmen oben zu den aufrufenden Methoden darunter.
- Eine Kette, die fortlaufend Rahmen hinzufügt, kann den Stapel erschöpfen.
4Überlade Methoden
Eine nützliche Operation kann mehrere Arten von Eingaben akzeptieren. Durch Überladen können diese Formen einen Namen teilen und jeweils eine eigene Parameterliste behalten. Prüfe zuerst, ob zwei Deklarationen wirklich unterschiedliche Signaturen haben. Verfolge dann die Wahl des Compilers bei einem Aufruf: Zähle die Argumente, bestimme ihre Typen und vergleiche die kompatiblen Versionen. Der Typ einer aufnehmenden Variablen kann einen Aufruf nicht retten, dessen Argumente keine geeignete Überladung bestimmen.
4.1 Schreibe überladene Methoden
Überladen gibt Methoden denselben Namen mit unterschiedlichen Parameterlisten. Das ist nützlich, wenn die Methoden dieselbe Aufgabe für eine unterschiedliche Anzahl oder unterschiedliche Typen von Eingaben erfüllen.
Vergleiche Signaturen
Für diese Methoden besteht eine Signatur aus dem Namen und den Parametertypen in ihrer Reihenfolge. Die Anzahl der Parameter ergibt sich aus dieser Liste.
public static int max(int a, int b) {
if (a > b) {
return a;
}
return b;
}
public static int max(int a, int b, int c) {
return max(max(a, b), c);
}
public static double max(double a, double b) {
if (a > b) {
return a;
}
return b;
}
Die Signaturen lauten max(int, int), max(int, int, int) und max(double, double). max(3, 9) verwendet die erste und gibt 9 zurück. max(3, 9, 5) ruft die zweite auf: Der innere Aufruf mit zwei int-Werten gibt 9 zurück, dann vergleicht der äußere Aufruf mit zwei int-Werten 9 und 5 und gibt ebenfalls 9 zurück. max(2.5, 1.0) verwendet die double-Version und gibt 2.5 zurück.
Erkenne, welche Änderungen nicht zählen
Eine Änderung allein des Rückgabetyps erzeugt keine Überladung. Diese Köpfe stehen im Konflikt:
public static int area(int w, int h)
public static double area(int w, int h)
Beide haben die Signatur area(int, int). Java lehnt die doppelte Deklaration ab. Auch das Umbenennen der Parameter in width und height lässt die Signatur gleich: Aufrufer übergeben Werte nach Position, nicht nach Parameternamen.
Die Reihenfolge unterschiedlicher Typen zählt dagegen. describe(String s, int n) und describe(int n, String s) sind unterschiedliche Signaturen. Sie lassen sich zusammen kompilieren, aber ein Aufrufer muss sich eine leicht zu verwechselnde Reihenfolge merken; verwende ein solches Paar daher nur, wenn es dem Entwurf hilft.
Lass eine kürzere Überladung einen Standardwert liefern
Java bietet in Methodendeklarationen keine Standardwerte für Parameter. Eine kürzere Überladung kann eine längere mit einem festen Argument aufrufen:
public static void printLine(int length, char symbol) {
for (int i = 0; i < length; i++) {
System.out.print(symbol);
}
System.out.println();
}
public static void printLine(int length) {
printLine(length, '-');
}
printLine(5, '*'); gibt ***** aus. printLine(4); führt die kürzere Methode aus, die die längere mit 4 und '-' aufruft und ---- ausgibt. Die Schleife steht an einer Stelle, sodass Änderungen an der Ausgabeaufgabe für beide Formen gelten.
Auch die Java-Bibliothek verwendet Überladungen. System.out.println akzeptiert über verschiedene Versionen einen int-, double-, char-, boolean- oder String-Wert. Math.abs und Math.max bieten Versionen für int, long, float und double an.
Gib dem gemeinsamen Namen eine einzige Bedeutung. Das Ausgeben eines Belegs und das Ausgeben eines Etiketts sind unterschiedliche Aufgaben; klare Namen wie printReceipt und printLabel sagen dem Aufrufer, welche Aufgabe ausgeführt wird.
Wichtige Erkenntnisse
- Überladungen haben denselben Namen und unterscheiden sich in Anzahl, Typen oder Reihenfolge der Parametertypen.
- Rückgabetypen und Parameternamen unterscheiden Signaturen nicht.
- Eine kürzere Überladung kann eine längere mit einem festen Standardargument aufrufen.
- Verwende einen überladenen Namen für eine Aufgabe mit unterschiedlichen Eingabeformen.
4.2 Sage voraus, welche überladene Methode ausgeführt wird
Der Compiler wählt eine Überladung anhand der Anzahl und der Typen der Argumente. Ermittle diese Wahl, bevor du betrachtest, wie der Rückgabewert verwendet wird.
Beginne mit exakten Typübereinstimmungen
Angenommen, eine Klasse definiert show(int x) und show(double x). show(5) verwendet die int-Version, weil das Literal 5 ein int-Wert ist. show(5.0) verwendet die double-Version. Ein als 5.0 geschriebener ganzzahliger Wert ist weiterhin ein double-Ausdruck.
Vergleiche bei einem Aufruf mit mehreren Argumenten den Typ an jeder Position. Ein Kandidat mit der falschen Anzahl von Argumenten kann nicht passen.
Erlaube bei Bedarf eine erweiternde Typumwandlung
Existiert nur show(double x), ist show(5) gültig: Der int-Wert wird zu 5.0 erweitert. Zu den möglichen erweiternden Umwandlungen primitiver Typen gehören:
byte → short → int → long → float → double
char → int → long → float → double
Bei show(long x) und show(double x) wählt ein int-Argument long. Das ist der spezifischere kompatible Parametertyp: Ein long-Wert kann zu double erweitert werden, ein double-Wert aber nicht zurück zu long.
Eine erweiternde Typumwandlung ist eine zulässige numerische Umwandlung; sie garantiert nicht, dass bei jeder solchen Umwandlung alle Stellen erhalten bleiben. Insbesondere kann bei der Umwandlung eines ausreichend großen long-Werts in double sein Wert gerundet werden. Die in den vorherigen Beispielen verwendete Umwandlung von int zu double erhält alle int-Werte exakt.
Ein char-Wert hat ebenfalls einen numerischen Wert. Bei letter(int code) und letter(String text) ruft letter('A') die int-Version mit 65 auf. Ein Zeichen wird nicht in einen String umgewandelt, damit es zu einem Parameter passt. Doppelte Anführungszeichen in "A" würden stattdessen ein String-Argument erzeugen.
Schließe implizite einengende Typumwandlungen aus
Gibt es nur show(int x), schlägt show(5.0) fehl. Java wandelt ein double-Argument nicht einengend um, nur weil sein Wert zufällig ganzzahlig ist. show((int) 5.0) übergibt nach einem expliziten Cast einen int-Wert.
Ebenso können Versionen von max für zwei und drei int-Werte max(1) nicht akzeptieren. Kein Kandidat hat die erforderliche Anzahl von Parametern, daher meldet der Compiler „no suitable method found“. Vergleiche die Kandidatenliste mit den übergebenen Argumenten.
Trenne die Auswahl der Überladung von der Ergebnisumwandlung
Betrachte int twice(int n) und double twice(double x). In double d = twice(3); wählt das Argument die int-Überladung. Dieser Aufruf gibt 6 zurück, dann erweitert die Zuweisung den Wert zu 6.0. Die Zielvariable d wählt nicht die double-Überladung.
Erkenne einen mehrdeutigen Aufruf
public static void combine(int a, double b) { }
public static void combine(double a, int b) { }
combine(1, 2) passt zur ersten Version nach Erweiterung des zweiten Arguments und zur zweiten nach Erweiterung des ersten. Keine der beiden Signaturen ist über beide Positionen hinweg spezifischer. Java meldet „reference to combine is ambiguous“; es wählt nicht die zuerst geschriebene Definition.
combine(1, 2.0) passt nur zu (int, double), weil die andere Signatur eine einengende Umwandlung von 2.0 benötigen würde. Ein eindeutiger Argumenttyp oder eine bessere Auswahl an Überladungen beseitigt die Mehrdeutigkeit.
Wichtige Erkenntnisse
- Wähle eine Überladung zur Kompilierzeit anhand der Anzahl und der Typen der Argumente.
- Eine exakte Typübereinstimmung hat Vorrang; eine erweiternde Typumwandlung kann eine andere Version kompatibel machen.
- Java wandelt numerische Methodenargumente nicht implizit einengend um.
- Die Variable, die das Ergebnis aufnimmt, wählt die Überladung nicht aus.
- Mehrdeutigkeit bedeutet, dass mehrere Versionen passen und keine über den ganzen Aufruf hinweg spezifischer ist.
5Zerlege ein Programm in Methoden
Die Methoden eines Programms sollten seinen Plan erkennen lassen. Gib jeder Methode eine sinnvolle Aufgabe, übergib ihr die benötigten Werte und verwende ihr Ergebnis im nächsten Schritt. Wiederholter Code deutet oft auf eine fehlende Methode hin; eine lange Methode zeigt oft, dass mehrere Aufgaben vermischt sind. Plane zuerst die Methodenköpfe, entwickle kleine Teile mit bekannten Ergebnissen und verbinde sie nach und nach. So lassen sich sowohl der Datenfluss als auch die Stelle eines neuen Fehlers leichter erkennen.
5.1 Gib jeder Methode eine klar abgegrenzte Aufgabe
Problemzerlegung bedeutet, ein Problem in kleinere Aufgaben zu teilen und jede Aufgabe in einer eigenen Methode zu lösen. Wähle Aufgaben, deren Namen helfen, den Plan des Programms zu verstehen.
Trenne Berechnung und Ein- und Ausgabe
Ein Programm für eine Radtour benötigt eine Geschwindigkeitsberechnung und einen Bericht. Beide verwenden dieselben Daten, haben aber unterschiedliche Aufgaben:
public static double kilometersPerHour(double km, double hours) {
return km / hours;
}
public static void printRideReport(double speed) {
System.out.println("Speed: " + speed + " km/h");
}
Für eine Fahrt von 150 km in 2 Stunden gibt die Berechnung 75.0 zurück. Die Berichtsmethode erhält diesen Wert und zeigt ihn an. Ein Aufrufer verbindet die Schritte:
double speed = kilometersPerHour(150, 2);
printRideReport(speed);
Die Berechnung liest keine Tastatureingaben und gibt keinen Bericht aus. Ein anderes Programm kann sie verwenden, um Fahrten zu vergleichen oder Geschwindigkeiten zu speichern, ohne ihren Rumpf zu ändern. Um sie zu testen, rufe sie mit bekannten Eingaben auf und vergleiche die zurückgegebene Zahl mit der erwarteten.
Erkenne eine Methode mit zu vielen Aufgaben
Ein Name wie readAndCheckAndPrint deutet auf mehrere Tätigkeiten hin. Weitere Hinweise sind durch Kommentare gekennzeichnete Abschnitte, ein Rumpf, der länger als eine Bildschirmseite ist, und Gruppen von Variablen, die unabhängigen Zwecken dienen. Das sind Gründe, die Methode zu untersuchen, keine starren Grenzen für ihre Zeilenanzahl.
Teile an einer sinnvollen Stelle. Eingabecode kann einen Wert einlesen, eine boolesche Hilfsmethode kann ihn prüfen, eine Berechnung kann ein Ergebnis zurückgeben und eine Berichtsmethode kann es anzeigen. Dann liest sich main wie eine kurze Beschreibung, wie diese Aufgaben zusammenhängen.
Auf eine Aufgabe konzentrierte Methoden lassen sich einzeln leichter lesen, wiederverwenden und testen. Ändert sich das Format eines Berichts, bearbeite die Berichtsmethode. Ist eine Berechnung falsch, teste sie, ohne eine ganze Folge von Eingaben eintippen zu müssen.
Lass den Kopf den Datenfluss erklären
In kilometersPerHour(double km, double hours) nennt der Name das Ergebnis, die Parameternamen nennen die Größen und der Rückgabetyp double nennt die Art des Ergebnisses. Ein kurzer Javadoc-Kommentar kann die Bedeutung der Eingaben und des Ergebnisses ergänzen:
/**
* Calculates a ride's average speed.
* @param km distance in kilometers
* @param hours elapsed time in hours, greater than zero
* @return speed in kilometers per hour
*/
Setze diesen Kommentar direkt über den Methodenkopf. Ein nützlicher Kommentar ergänzt Informationen wie Einheiten oder eine Anforderung an die Eingabe; er muss nicht jede Zeile des Rumpfs beschreiben.
Eine Hilfsmethode, die nur ihre eigene Klasse benötigt, kann private static sein. private verhindert, dass andere Klassen sie aufrufen, während Code in derselben Klasse sie weiterhin verwenden kann. public static stellt die Hilfsmethode anderen Klassen zur Verfügung.
Auch wenn du jede offensichtliche Anweisung in eine eigene umhüllende Methode auslagerst, kann das den Plan verschleiern. Behalte eine Methode, wenn ihr Name für einen sinnvollen Schritt oder eine wiederverwendbare Berechnung steht. Eine Kette von Hilfsmethoden, die denselben Aufruf nur weiterreichen, schafft mehr Stellen zum Untersuchen, ohne mehr über die Aufgabe zu erklären.
Wichtige Erkenntnisse
- Zerlege ein Programm in sinnvolle Aufgaben mit klaren Namen.
- Berechnungsmethoden nehmen Parameter entgegen und geben Ergebnisse zurück; Berichtsmethoden zeigen sie an.
- Hilfsmethoden mit klarer Aufgabe lassen sich einzeln wiederverwenden und testen.
- Ein Methodenkopf und ein kurzer Javadoc-Kommentar können Eingaben, Einheiten und Ergebnis angeben.
- Verwende
private staticfür Hilfsmethoden, die nur ihre eigene Klasse aufrufen soll. - Teile dort auf, wo ein Name die Aufgabe verdeutlicht, nicht bei jeder einzelnen Anweisung.
5.2 Ersetze wiederholten Code durch eine parametrisierte Methode
Codeduplizierung wiederholt dieselbe Aufgabe an mehreren Stellen. Wenn sich nur die Eingabewerte unterscheiden, kann eine parametrisierte Methode die gemeinsame Arbeit zusammenhalten.
Finde die gemeinsame Berechnung
Angenommen, ein Programm summiert von 1 bis 10 und dann von 1 bis 50:
int total1 = 0;
for (int i = 1; i <= 10; i++) {
total1 += i;
}
System.out.println("Sum 1 to 10: " + total1);
int total2 = 0;
for (int i = 1; i <= 50; i++) {
total2 += i;
}
System.out.println("Sum 1 to 50: " + total2);
Beide Schleifen beginnen bei 1, addieren zu einer Summe und enden bei einer oberen Grenze. Nur diese Grenze ändert sich. Ein Parameter n kann sie an eine gemeinsame Berechnung übergeben.
Extrahiere die Methode
public static int sumUpTo(int n) {
int total = 0;
for (int i = 1; i <= n; i++) {
total += i;
}
return total;
}
n ist die veränderliche Eingabe. total und i sind Arbeitsvariablen, die in die Hilfsmethode gehören. Der Aufrufer benötigt die fertige Summe, also gibt die Hilfsmethode einen int-Wert zurück.
Ersetze beide wiederholten Abschnitte:
System.out.println("Sum 1 to 10: " + sumUpTo(10));
System.out.println("Sum 1 to 50: " + sumUpTo(50));
sumUpTo(10) gibt 55 zurück und sumUpTo(50) gibt 1275 zurück. Die ausgegebenen Zeilen lauten wie zuvor „Sum 1 to 10: 55“ und „Sum 1 to 50: 1275“. Die Beschreibungen bleiben beim Aufrufer; die Schleife führt nur die Berechnung aus.
Prüfe die Extraktion in einer festen Reihenfolge
- Markiere die Anweisungen, die die wiederholte Aufgabe erfüllen.
- Bestimme jeden Wert, der sich zwischen den Kopien unterscheidet.
- Mache diese Werte zu Parametern mit passenden Typen und gib jedes außerhalb benötigte Ergebnis zurück.
- Ersetze jede Kopie durch einen Aufruf mit ihren ursprünglichen Eingaben.
- Führe das Programm vorher und nachher mit denselben Eingaben aus und vergleiche das Verhalten.
Die Struktur von Code zu ändern und dabei sein Verhalten zu erhalten, heißt Refactoring. Bleibt eine ursprüngliche Kopie zurück, muss eine spätere Korrektur weiterhin an zwei Stellen vorgenommen werden. Verschiebst du eine Ausgabe in eine Schleife oder änderst einen Schleifenendpunkt, ändert sich das Verhalten, auch wenn der Code nun kürzer ist.
Haben zwei Unterschiede verschiedene Typen, verwende passende Parameter. Soll unterschiedlicher Text unterschiedlich oft wiederholt werden, bietet sich printRepeated(String text, int times) an; das Wort ist ein String und die Anzahl ein int-Wert.
Ähnlich aussehender Code erfüllt nicht immer dieselbe Aufgabe. Ein boolescher Parameter, der eine Methode zwischen unabhängigen Berechnungen umschaltet, kann ihre Aufrufe schwer verständlich machen. Gib unterschiedlichen Aufgaben unterschiedliche Namen. Extrahiere die wiederholte Operation nur, wenn sie beim Ändern ihrer Eingaben eine klare Bedeutung behält.
Wichtige Erkenntnisse
- Bestimme die gemeinsame Aufgabe und mache ihre veränderlichen Eingaben zu Parametern.
- Halte Arbeitsvariablen lokal und gib jedes Ergebnis zurück, das der Aufrufer benötigt.
- Überarbeite jede Kopie und vergleiche das Verhalten mit denselben Eingaben.
- Führe Kopien derselben Aufgabe zusammen, nicht unabhängige Aufgaben, die nur ähnlich aussehen.
5.3 Entwirf ein Programm nach dem Top-down-Prinzip und teste jede Methode einzeln
Der Top-down-Entwurf beginnt mit der gesamten Aufgabe und verfeinert sie zu kleineren Methodenaufrufen. Entscheide, was jeder Aufruf benötigt und zurückgibt, bevor du alle Einzelheiten ausarbeitest.
Plane die Schritte und Köpfe
Ein Programm für die wöchentliche Bewegung liest 7 Tagesdauern ein, addiert sie und gibt einen Bericht aus. Seine Hilfsmethoden lassen sich so planen:
readMinutes(Scanner scanner, int day)gibt die gültige Minutenzahl eines Tages zurück.isValidMinutes(int minutes)beantwortet, ob die Eingabe zwischen 0 und 1440 liegt.formatDuration(int minutes)gibt Text mit vollen Stunden und verbleibenden Minuten zurück.averagePerDay(int total, int days)gibt einen Durchschnitt als double-Wert zurück.printSummary(int totalMinutes, int days)gibt den Bericht aus.
Derselbe Scanner wird an jeden Eingabeaufruf übergeben. Eine Hilfsmethode wie isValidMinutes muss selbst keine Eingabe lesen.
Verwende beim Entwickeln Stubs
Ein Stub hat seinen endgültigen Kopf, aber einen vorläufigen minimalen Rumpf, etwa return 0;. Er ermöglicht das Kompilieren des Programms, bevor eine Hilfsmethode implementiert ist. Ersetze einen Stub, kompiliere und prüfe diese Methode, bevor du den nächsten ersetzt. Ein kompilierbarer Stub ist weiterhin unvollendete Arbeit; seine feste Antwort kann keine echte Berechnung überprüfen.
Hier ist das vollständige Programm:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int total = 0;
for (int day = 1; day <= 7; day++) {
total += readMinutes(scanner, day);
}
printSummary(total, 7);
}
public static int readMinutes(Scanner scanner, int day) {
System.out.println("Minutes for day " + day + ":");
int minutes = scanner.nextInt();
while (!isValidMinutes(minutes)) {
System.out.println("Enter minutes from 0 to 1440:");
minutes = scanner.nextInt();
}
return minutes;
}
public static boolean isValidMinutes(int minutes) {
return minutes >= 0 && minutes <= 1440;
}
public static String formatDuration(int minutes) {
return minutes / 60 + " h " + minutes % 60 + " min";
}
public static double averagePerDay(int total, int days) {
return (double) total / days;
}
public static void printSummary(int totalMinutes, int days) {
System.out.println("Total: " + formatDuration(totalMinutes));
System.out.println("Daily average: "
+ averagePerDay(totalMinutes, days) + " min");
if (totalMinutes >= 150) {
System.out.println("Weekly goal reached");
}
}
}
Bei ganzzahligen Eingaben wiederholt readMinutes seine Wertebereichsprüfung, bis die Zahl gültig ist. Bei den täglichen Eingaben 30, 45, 0, 60, 90, 45 und 45 beträgt die Summe 315. Die Formatierung ergibt „5 h 15 min“, der Durchschnitt beträgt 45.0 Minuten und das Programm meldet, dass sein Wochenziel von 150 Minuten erreicht ist.
Prüfe kleine Methoden und ihre Grenzwerte
Rufe jede Berechnung mit bekannten Ergebnissen auf. formatDuration(0) sollte „0 h 0 min“ ergeben, 59 ergibt „0 h 59 min“, 60 ergibt „1 h 0 min“ und 135 ergibt „2 h 15 min“. Die Eingaben knapp unter und bei 60 prüfen den Wechsel von Minuten zu einer vollen Stunde.
Teste für isValidMinutes die Werte -1, 0, 1440 und 1441. Die erwarteten Ergebnisse sind false, true, true und false. Wenn du erwartete und tatsächliche Werte nebeneinander ausgibst, wird ein Fehler sichtbar. JUnit automatisiert diese Art von Prüfung.
Die Bottom-up-Entwicklung beginnt damit, kleine Hilfsmethoden zu schreiben und zu testen, und verbindet sie dann zu größeren Schritten. Du kannst nach dem Top-down-Prinzip planen und nach dem Bottom-up-Prinzip entwickeln: Lege zuerst den Datenfluss fest, beweise dann, dass die kleinen Berechnungen funktionieren, und prüfe anschließend das verbundene Programm.
Wichtige Erkenntnisse
- Der Top-down-Entwurf macht aus einer Gesamtaufgabe Aufrufe und verfeinert dann jede Aufgabe.
- Lege Eingaben und Rückgabetyp jeder Hilfsmethode vor ihrem Rumpf fest.
- Ein Stub behält einen geplanten Kopf bei, während sein Rumpf noch vorläufig ist.
- Teste bekannte Ergebnisse und Werte an und um Grenzwerte.
- Entwickle und prüfe jeweils eine Methode und teste dann das verbundene Programm.
- Die Bottom-up-Entwicklung erstellt kleine Hilfsmethoden, bevor sie diese kombiniert.
Java-Methoden und Problemzerlegung: Beispiele
1Entwickle eine wiederverwendbare Methode zur Linienausgabe
11 Schritte
- 1EinführungMache aus einer wiederholten Aktion einen Aufruf
Ein Bericht benötigt an mehreren Stellen eine kurze Trennlinie. Wenn diese Aktion einen Methodennamen erhält, kann der Bericht eine Trennlinie anfordern, ohne die Ausgabeanweisungen zu wiederholen. Diese benannte Aktion ist eine Methode, und eine Definition dient jedem Aufruf.
Beginne mit einer festen Trennlinie. Lass dann den Aufrufer ihre Breite und ihr Zeichen über Parameter wählen. Verfolge Definition, Aufruf und übergebene Werte als drei verschiedene Teile der Aufgabe.
- 2ErklärungLies einen Methodenkopf
public static void printDivider() { System.out.println("===="); }printDividerist der Name.publicerlaubt Aufrufe aus anderen Klassen;staticbedeutet, dass kein Objekt benötigt wird.voidbesagt, dass kein Wert zurückkommt, und()besagt, dass keine Eingabe erforderlich ist.Die Anweisung innerhalb der geschweiften Klammern ist der Rumpf. Nur weil diese Definition existiert, wird nichts ausgegeben.
- 3TippSetze die Hilfsmethode neben main
In einer klassischen Klasse sind
mainundprintDividergetrennte Methoden innerhalb derselben Klassenklammern. Schließe den Rumpf vonmain, bevor du mit dem Kopf der Hilfsmethode beginnst.Eine Hilfsmethode kann über oder unter
mainstehen; ihre Position legt nicht fest, wann sie ausgeführt wird. Eine Definition innerhalb vonmainwird abgelehnt. Prüfe zuerst die Klammern, wenn ein Methodenkopf „illegal start of expression“ verursacht. - 4BeispielVerfolge den ersten Aufruf
Angenommen,
mainenthält:System.out.println("Items"); printDivider(); System.out.println("End");Die Ausführung gibt zuerst „Items“ aus. Der Aufruf führt zu
printDivider, das „====“ ausgibt. Wenn die Hilfsmethode endet, fährtmainbei seiner nächsten Anweisung fort und gibt „End“ aus.Der Rumpf der Hilfsmethode wird zwischen den beiden Anweisungen des Aufrufers ausgeführt, genau dort, wo der Aufruf steht.
- 5HerausforderungSetze eine zweite Trennlinie
mainruftprintDivider();auf, gibt"A"aus und ruft erneutprintDivider();auf. Welche drei Zeilen erscheinen?---reveal---
Die Zeilen lauten „====“, „A“ und „====“. Jeder Aufruf führt denselben Rumpf der Hilfsmethode aus, gibt einmal etwas aus und kehrt zu seiner eigenen Stelle in
mainzurück.printDivider;wäre kein Aufruf. Auch ohne Argumente sind die Klammern erforderlich. - 6ErklärungMache die Breite zur Eingabe
Ersetze den festen Rumpf durch eine Schleife:
public static void printDivider(int length) { for (int i = 0; i < length; i++) { System.out.print("="); } System.out.println(); }lengthist ein im Kopf deklarierter Parameter. InprintDivider(6);wird das Argument6zu seinem Anfangswert. Sechs Schleifendurchläufe geben sechs Zeichen aus, bevor die Zeile endet. - 7BeispielWerte einen Ausdruck vor dem Aufruf aus
int sections = 3; printDivider(sections + 2);Werte zuerst
sections + 2aus und erhalte5. Führe dann die Hilfsmethode mit ihrem Parameterlength = 5aus. Ihre Schleife gibt „=====“ aus.sectionsundlengthmüssen nicht denselben Namen haben. Ein Parameter erhält den ausgewerteten Argumentwert; der Aufrufer schreibt den Typ nicht in den Aufruf. - 8BeispielErgänze eine zweite veränderliche Eingabe
Um auch das Zeichen zu wählen, verwende:
public static void printDivider(int length, char symbol) { for (int i = 0; i < length; i++) { System.out.print(symbol); } System.out.println(); }Der Aufruf
printDivider(3, '+');übergibt3anlengthund'+'ansymbolund gibt „+++“ aus. Jeder Parameter benötigt im Kopf seinen eigenen Typ. - 9TippPrüfe Anzahl, Reihenfolge und Typ
Ein Aufruf muss für jeden Parameter einen kompatiblen Wert liefern. Bei
printDivider(3);fehlt nun das Zeichen, währendprintDivider("3", '+');Text übergibt, wo ein int-Wert benötigt wird.Java kann einen int-Wert für einen double-Parameter zu double erweitern. Es kann
3.7nicht implizit einengend umwandeln, damit der Wert zu diesem int-Parameter passt. Der explizite Cast(int) 3.7liefert3, indem er den Nachkommateil abschneidet. - 10HerausforderungSage einen parametrisierten Aufruf voraus
Welche Zeile gibt dies mit der Hilfsmethode mit zwei Parametern aus?
int width = 4; printDivider(width - 1, '#');---reveal---
Es wird „###“ ausgegeben. Der Ausdruck wird zu
3ausgewertet, dann erhält der erste Parameter diese Anzahl und der zweite'#'. Die Schleife läuft dreimal.Der Aufruf ist eine Anweisung und endet daher mit einem Semikolon. Seine Argumentausdrücke enthalten Werte, keine Parameterdeklarationen.
- 11ZusammenfassungLies die Aktion und ihre Eingaben
Eine Definition gibt einer Aufgabe einen Kopf und einen Rumpf; ein Aufruf führt sie aus und setzt dann beim Aufrufer fort. Halte Definitionen innerhalb der Klasse und außerhalb anderer Methoden.
Parameter beschreiben die benötigten Eingaben. Aufrufe liefern ausgewertete Argumente in der passenden Reihenfolge, in der richtigen Anzahl und mit kompatiblen Typen.
2Gib Antworten zurück und beende Suchen
13 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungGib ein Ergebnis an den Aufrufer zurück
- 2ErklärungLass einen Aufruf für einen Wert stehen
- 3KernaussageEine Ausgabe liefert keinen Wert für eine Zuweisung
- 4BeispielBehalte den Nachkommateil
- 5HerausforderungVerwende das Ergebnis in einem anderen Ausdruck
- 6BeispielGib formatierten Text zurück
- 7ErklärungEin return beendet diesen Aufruf
- 8TippPrüfe den Pfad, der den if-Block auslässt
- 9BeispielSchreibe eine Suche mit einem Ergebnis für Misserfolg
- 10BeispielVerfolge die Suche bis zu ihrem Ende
- 11HerausforderungFinde einen vorzeitig gemeldeten Misserfolg
- 12HerausforderungVerbinde ein Suchergebnis mit einer Bedingung
- 13ZusammenfassungVerfolge Antwort und Abbruchstelle
3Halte die Variablen jedes Aufrufs getrennt
15 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungOrdne jede Variable ihrem Aufruf zu
- 2ErklärungEine Hilfsmethode sieht ihre eigenen lokalen Variablen
- 3BeispielHalte eine Summe außerhalb der Schleife
- 4HerausforderungUnterscheide Gültigkeitsbereich und Initialisierung
- 5BeispielKopiere eine Zahl in einen Parameter
- 6BeispielWeise den zurückgegebenen Ersatzwert zu
- 7HerausforderungSage einen zweiten Aufruf voraus
- 8VerbindungFür kopierte Referenzen gilt dieselbe Regel
- 9BeispielBeginne eine äußere Berechnung
- 10BeispielHalte den Aufrufer an und lege einen weiteren Rahmen auf den Stapel
- 11BeispielÜbertrage Ergebnisse auf dem Stapel nach unten
- 12TippLies einen Fehler aus dem obersten Rahmen
- 13HerausforderungFinde die wartende Aufgabe
- 14HerausforderungVerbinde Kopien mit Ergebnissen auf dem Stapel
- 15ZusammenfassungHalte Namen, Werte und Rahmen auseinander
4Wähle die richtige überladene Methode
12 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungEine Aufgabe, mehrere Eingabeformen
- 2ErklärungBestimme eine Signatur
- 3KernaussageEin Rückgabetyp kann keine Überladung erzeugen
- 4BeispielDelegiere an die vollständige Version
- 5HerausforderungPrüfe eine vorgeschlagene dritte Deklaration
- 6ErklärungWähle eine exakte numerische Typübereinstimmung
- 7BeispielVerwende erweiternde ohne einengende Typumwandlung
- 8BeispielTrenne den Aufruf von seiner Zuweisung
- 9BeispielVergleiche jede Argumentposition
- 10TippNutze die Kandidatenliste zur Untersuchung eines Aufrufs
- 11HerausforderungWähle eine Version und verwende dann ihr Ergebnis
- 12ZusammenfassungPrüfe Definitionen vor Aufrufen
5Erstelle einen kleinen Bericht aus getesteten Hilfsmethoden
16 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungTeile einen Bericht in kleine Aufgaben auf
- 2ErklärungBenenne die Aufgabe, bevor du den Rumpf schreibst
- 3BeispielMache die Berechnung wiederverwendbar
- 4BeispielGib der Ausgabe eine eigene Methode
- 5HerausforderungVerwende eine Berechnung ohne Bericht wieder
- 6ErklärungFinde, was sich im wiederholten Code ändert
- 7BeispielWähle den extrahierten Methodenkopf
- 8BeispielSetze die gemeinsame Schleife an eine Stelle
- 9HerausforderungErhalte die letzte Wiederholung
- 10ErklärungPlane das Programm von oben
- 11TippVerwende einen Stub nur während der Entwicklung
- 12BeispielÜbergib denselben Eingabeleser
- 13BeispielPrüfe die Berechnung vor dem Verbinden
- 14BeispielPrüfe eine Eingabefolge im verbundenen Programm
- 15HerausforderungFinde einen Fehler beim Verbinden
- 16ZusammenfassungEntwickle sinnvolle Teile und verbinde sie
Java-Methoden und Problemzerlegung: Übungsaufgaben
Set 1: Aufrufe, Werte und kleine Programmschritte10 Übungsfragen
Frage 1 von 10
main-Methode und benötigt eine neue Methode printMenu. Wohin gehört die neue Definition?Antwort zeigenAntwort ausblenden
main macht ihre Definition nicht zu einem Teil von main; dort würde eine verschachtelte Methodendefinition entstehen, die Java ablehnt.Frage 2 von 10
Welche Zeile gibt dieser Aufruf aus?
public static void showPair(int rows, int columns) {
System.out.println(rows + ":" + columns);
}
int width = 7;
showPair(3, width - 2);
Antwort zeigenAntwort ausblenden
rows = 3. Der Ausdruck width - 2 wird vor dem Aufruf ausgewertet, sodass der zweite Parameter 5 erhält und die Ausgabe „3:5“ lautet. Der Name width beim Aufrufer ändert die Zuordnung der Argumente nach Position nicht.Frage 3 von 10
Welchen Wert gibt halfTotal(8, 5) zurück?
public static double halfTotal(int a, int b) {
return (a + b) / 2;
}
Antwort zeigenAntwort ausblenden
13, und die Division durch den int-Wert 2 ergibt 6. Dieses Ergebnis wird zum zurückgegebenen double-Wert 6.0 erweitert. Ein Rückgabetyp double sorgt nicht dafür, dass die frühere Division mit Dezimalzahlen rechnet; um 6.5 zu erhalten, wäre ein Divisor 2.0 nötig.Frage 4 von 10
Warum lässt sich diese Methode nicht kompilieren?
public static String label(int n) {
if (n >= 0) {
return "ready";
}
}
Antwort zeigenAntwort ausblenden
Frage 5 von 10
Eine Schleife soll die Ganzzahlen 2 bis 4 addieren und danach die Summe ausgeben. Welche Deklaration soll den Kommentar ersetzen?
// declaration here
for (int i = 2; i <= 4; i++) {
total += i;
}
System.out.println(total);
Antwort zeigenAntwort ausblenden
total vor der Schleife mit 0, damit alle drei Additionen dieselbe Variable aktualisieren und sie für die abschließende Ausgabe sichtbar bleibt. Die alleinige Deklaration int total; weist keinen Anfangswert zu, sodass total += i eine nicht initialisierte lokale Variable lesen würde.Frage 6 von 10
Welche zwei Zahlen werden in welcher Reihenfolge ausgegeben?
public static void lower(int level) {
level -= 3;
System.out.println(level);
}
int level = 14;
lower(level);
System.out.println(level);
Antwort zeigenAntwort ausblenden
14 initialisiertes level, verringert diese Kopie auf 11 und gibt sie aus. Das level des Aufrufers bleibt 14 und wird als Nächstes ausgegeben. Derselbe Name in beiden Methoden führt nicht dazu, dass eine Zuweisung an den Parameter die Variable des Aufrufers aktualisiert.Frage 7 von 10
public static int scale(int value). Welcher zusätzliche Kopf würde mit ihrer Signatur in Konflikt stehen?Antwort zeigenAntwort ausblenden
scale(int). Sein double-Rückgabetyp und der geänderte Parametername unterscheiden ihn nicht von der vorhandenen Methode. Eine Änderung des Parametertyps oder ein zusätzlicher Parameter würde eine andere Signatur erzeugen.Frage 8 von 10
show(int n) und show(double n). Welche Methode wird für show(12.0) ausgeführt?Antwort zeigenAntwort ausblenden
12.0 hat den Typ double, sodass die double-Überladung exakt passt. Sein ganzzahliger Wert macht den Ausdruck nicht zu einem int-Wert. Die int-Überladung würde eine einengende Typumwandlung benötigen, die Java für dieses Argument nicht implizit durchführt.Frage 9 von 10
Antwort zeigenAntwort ausblenden
Frage 10 von 10
Ein Programm soll Aufgabendauern von 0 bis einschließlich 90 Minuten akzeptieren und ihren Durchschnitt ausgeben. Zurzeit verwendet es diese Hilfsmethoden. Welche zwei Aufrufe mit Eingabewerten können beide unabhängigen Fehler aufdecken?
public static boolean valid(int m) {
return m > 0 && m <= 90;
}
public static double mean(int total, int count) {
return total / count;
}
Antwort zeigenAntwort ausblenden
valid(0) gibt fälschlich false zurück, obwohl null erlaubt ist. mean(7, 2) gibt 3.0 statt 3.5 zurück, weil vor der erweiternden Typumwandlung eine Ganzzahldivision stattfindet. Der Test mit valid(90) und diesem Mittelwert entdeckt nur den Divisionsfehler, da der obere Endpunkt bereits funktioniert.Set 2: Verfolge Hilfsmethoden und korrigiere Methodengrenzen10 Übungsfragen
Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.
Frage 1 von 10
In welcher Reihenfolge werden die Zeilen ausgegeben?
public static void frame() {
System.out.println("Open");
mark();
System.out.println("Close");
}
public static void mark() {
System.out.println("Mark");
}
main ruft frame(); auf und gibt dann "Done" aus.
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 2 von 10
public static void repeat(int count). Welcher Wert erreicht count bei repeat((int) 3.9 + 2);?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 3 von 10
Welcher Wert wird result zugewiesen?
public static int addTwo(int n) {
return n + 2;
}
public static int product(int a, int b) {
return a * b;
}
int result = product(addTwo(4), addTwo(addTwo(1)));
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 4 von 10
Die Methode soll den ersten geraden Wert in einem Bereich einschließlich seiner Grenzen zurückgeben oder -1, falls keiner existiert. Für (9, 12) gibt sie fälschlich -1 zurück. Welche Änderung korrigiert die Suche?
public static int firstEven(int start, int end) {
for (int n = start; n <= end; n++) {
if (n % 2 == 0) {
return n;
} else {
return -1;
}
}
return -1;
}
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
pages von main steht. Wie sollen die Daten zur Hilfsmethode gelangen?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
Welcher Wert wird von outer(4) an main zurückgegeben?
public static int outer(int n) {
int saved = n;
int changed = inner(n + 1);
return saved + changed;
}
public static int inner(int n) {
n *= 2;
return n - 1;
}
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
printBorder(int length, char symbol) mit zwei Argumenten enthält bereits die Ausgabeschleife. Wie soll printBorder(int length) ein vorgegebenes '#' bereitstellen, ohne die Schleife zu duplizieren?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
Diese Überladungen stehen in derselben Klasse. Welcher Wert wird in answer gespeichert?
public static int adjust(int n) { return n + 1; }
public static double adjust(double n) { return n + 0.5; }
double answer = adjust(6);
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
8 * 3 und einer mit 11 * 4. Beide Ergebnisse werden für spätere Vergleiche benötigt. Welcher Kopf einer Hilfsmethode passt zur Extraktion dieser gemeinsamen Berechnung?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
public static int countItems(int limit) { return 0; } zu einem nützlichen vorläufigen Stub?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Set 3: Verbinde Argumente, Rückgabepfade und Entwurf10 Übungsfragen
Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.
Frage 1 von 10
Ein Programmierer schreibt dies innerhalb einer Klasse. Warum wird die Definition der Hilfsmethode abgelehnt?
public static void main(String[] args) {
public static void printTitle() {
System.out.println("Schedule");
}
}
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 2 von 10
Die Methode gibt die Anzahl vollständiger Gruppen zurück. Was gibt groups(5, 18) zurück?
public static int groups(int groupSize, int items) {
return items / groupSize;
}
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 3 von 10
Eine Kalenderhilfsmethode ist wie unten geschrieben. Welches Ergebnispaar geben zuerst leap(2100) und dann leap(2400) zurück?
public static boolean leap(int year) {
return (year % 4 == 0 && year % 100 != 0)
|| year % 400 == 0;
}
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 4 von 10
Welchen Text gibt der Aufrufer aus?
public static void tag(String name) {
name = name + "-checked";
}
String label = "parcel";
tag(label);
System.out.println(label);
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
Ein Fehlerbericht beginnt mit ArithmeticException: / by zero und diesen Rahmen. Wo trat die fehlgeschlagene Division auf?
at Main.unitCount(Main.java:23)
at Main.printLoad(Main.java:15)
at Main.main(Main.java:5)
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
public static void describe(String name, int count). Welcher Kopf fügt durch Änderung der Reihenfolge der Parametertypen eine gültige Überladung hinzu?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
Welche Zeile wird durch diesen Aufruf ausgegeben? Das Zeichen 'B' hat den numerischen Wert 66.
public static int code(int n) { return n + 1; }
public static String code(String s) { return s + "!"; }
double result = code('B');
System.out.println(result);
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
Das ursprüngliche Programm gibt die Summen 20 und 30 in dieser Reihenfolge aus. Nach der Extraktion verwendet es diese Hilfsmethode. Wie sollen die beiden Aufrufe geschrieben werden, damit diese Summen erhalten bleiben?
public static int sumMultiples(int factor, int count) {
int total = 0;
for (int i = 1; i <= count; i++) {
total += factor * i;
}
return total;
}
Die ursprüngliche erste Schleife addierte 2, 4, 6 und 8. Die zweite addierte 5, 10 und 15.
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
Ein Programm liest fünf tägliche Anzahlen ein, addiert sie und gibt einen Durchschnitt aus. Die geplanten Köpfe der Hilfsmethoden stehen unten. Welche Verbindung in main ermöglicht es dem Berichtsschritt, alle benötigten Daten zu erhalten?
public static int readCount(Scanner scanner, int day)
public static double average(int total, int days)
public static void printReport(int total, int days)
Der Scanner wurde bereits erstellt.
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Set 4: Untersuche vollständige Methodeninteraktionen10 Übungsfragen
Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.
Frage 1 von 10
Welche Zeile wird ausgegeben?
public static int next(int n) { return n + 1; }
public static double half(int n) { return n / 2.0; }
int n = 5;
next(n);
double result = half(next(n)) + n;
System.out.println(result);
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 2 von 10
Welchen Wert gibt locate(5) zurück?
public static int locate(int limit) {
int total = 0;
for (int n = 1; n <= limit; n++) {
total += n;
if (total > 8) {
return n;
}
}
return -1;
}
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 3 von 10
Der Code soll die Gewichte addieren und ihre Summe nach der Schleife ausgeben. Zurzeit lässt er sich nicht kompilieren. Welche Änderung behebt das Problem mit dem Gültigkeitsbereich und erhält zugleich eine laufende Summe?
for (int box = 1; box <= 3; box++) {
int total = 0;
total += box * 2;
}
System.out.println(total);
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 4 von 10
Welches Wertepaar wird als a:b ausgegeben?
public static int shift(int n) {
n += 4;
return n;
}
int a = 2;
int b = shift(a);
a = shift(b);
System.out.println(a + ":" + b);
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
Welche Werte haben an der markierten Stelle die beiden lokalen Variablen namens saved, zuerst im äußeren und dann im inneren Rahmen?
public static int outer(int n) {
int saved = n + 2;
return inner(saved) + saved;
}
public static int inner(int n) {
int saved = n * 3;
// program pauses here
return saved;
}
main hat outer(5) aufgerufen.
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
Die folgenden Methoden sind in einer Klasse deklariert. Was geschieht bei double result = merge(4, 9);?
public static int merge(int a, double b) { return a; }
public static double merge(double a, int b) { return b; }
public static double merge(double a, double b) { return a + b; }
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
"Crate" mit den Elementnummern 1 bis 2 aus; der zweite gibt "Bag" mit den Elementnummern 1 bis 4 aus. Welcher extrahierte Rumpf erhält dieses Format für printItems(String label, int count)?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
Eine Hilfsmethode soll einen Tagesdurchschnitt als Dezimalzahl zurückgeben:
public static double dailyAverage(int total, int days) {
return total / days;
}
Warum deckt die Prüfung nur mit (24, 6) ihren Rechenfehler nicht auf?
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Hängst du bei deiner eigenen Aufgabe fest? Hausaufgabenhilfe
Schlüsselbegriffe
A
Akkumulator
sum vor einer Schleife, damit jeder Durchlauf zu der aus dem vorherigen Durchlauf erhaltenen Summe addiert.Argument
printRepeated("go", 4) liefern die Argumente den Text und die Anzahl der Wiederholungen.Aufrufstapel
main eine Hilfsmethode aufruft, die eine weitere Hilfsmethode aufruft, können drei Rahmen gleichzeitig auf dem Stapel liegen.B
Block
Boolesche Methode
true oder false zurück, oft als Antwort auf eine Frage, die der Aufrufer direkt als Bedingung verwendet.isEven(count) kann direkt innerhalb der Klammern einer if-Anweisung stehen.Bottom-up-Entwicklung
C
Codeduplizierung
D
Delegierende Methodenüberladung
E
Einengende Typumwandlung
9.0 an einen int-Parameter schlägt ohne Cast fehl, obwohl der Wert keinen Nachkommateil hat.Erweiternde Typumwandlung
12 an einen double-Parameter übergeben, erhält dieser 12.0, ohne dass ein expliziter Cast nötig ist.Exakte Typübereinstimmung
show(int) und show(double) stimmt show(9) exakt mit der int-Version überein.Explizite Typumwandlung (Cast)
(int) 4.9 liefert einem int-Parameter den Wert 4, indem er den Nachkommateil abschneidet.G
Grenzwertfall
-1, 0, 1440 und 1441 als getrennte Eingaben.Gültigkeitsbereich
H
Hilfsmethode
I
Initialisierung
int total = 0; vor einer Summenschleife gibt der ersten Addition eine vorhandene Summe zur Aktualisierung.Inkrementelle Entwicklung
J
Javadoc
/** ... */-Kommentar über einer Deklaration, mit Tags zur Beschreibung von Parametern und Ergebnissen.@param minutes, um eine Eingabe zu erklären, und @return, um den erzeugten formatierten Text zu beschreiben.L
LIFO-Prinzip
Lokale Variable
total einer Hilfsmethode ist von einer ebenfalls total genannten lokalen Variablen einer anderen Methode getrennt.M
Mehrdeutiger Aufruf
(int, double) und (double, int) machen einen Aufruf mit zwei int-Argumenten mehrdeutig.Methode
printHeader() kann an mehreren Stellen eines Programms dieselbe Berichtsüberschrift ausgeben.Methodenaufruf
printHeader(); führt die Methode zur Ausgabe der Überschrift an dieser Stelle beim Aufrufer aus.Methodenextraktion
sumUpTo(int n) extrahiert, bleibt der Aufrufer für seinen eigenen Berichtstext verantwortlich.Methodenkopf
public static void greet() beschreibt eine aufrufbare Aktion ohne Eingabe und Rückgabewert.Methodenrumpf
Methodensignatur
width in w lässt area(int, int) unverändert und kann keine neue Überladung erzeugen.P
Parameter
printRepeated(String text, int times) sind text und times die beiden deklarierten Parameter.Parameterliste
(int width, int height) erfordert zwei Argumente, die zuerst width und dann height zugeordnet werden.Private Methode
private static deklariert, kann ihre eigene Klasse sie verwenden, ohne einen externen Aufruf anzubieten.Problemzerlegung
R
Refactoring
Referenz
return-Anweisung
return minutes * 60; beendet die Berechnung und liefert die Anzahl der Sekunden.Rückgabetyp
void deklariert, dass sie keinen Wert liefert.double average(int a, int b) deklarierte Methode verspricht ihrem Aufrufer ein double-Ergebnis.Rückgabewert
int total = sum(6, 8); ist die zurückgegebene 14 in der Variablen total des Aufrufers gespeichert.S
Sentinelwert
-1 nach einer Suche kann „nicht gefunden“ signalisieren, wenn -1 kein gültiger Treffer sein kann.Stacktrace
Stapelrahmen
minutes haben, weil die Parameter in getrennten Rahmen liegen.Stapelüberlauf
StackOverflowError.Statische Methode
Math.abs(-6) verwendet den Klassennamen Math und gibt den positiven Wert 6 zurück.Stub
return 0; kann als Platzhalter dienen, während die eigentliche Berechnung implementiert und geprüft wird.T
Top-down-Entwurf
main die Aufrufe für Eingabe, Berechnung und Bericht, bevor du die Rümpfe ihrer Hilfsmethoden ausarbeitest.U
Unerreichbare Anweisung
Unveränderlicher String
"!" und die Zuweisung des Ergebnisses an einen String-Parameter lassen die Referenz des Aufrufers unverändert.Ü
Überladen
max können zwei und drei int-Werte akzeptieren und dabei eine Bedeutung für den Namen beibehalten.Überladungsauflösung
V
Verschachtelter Aufruf
Math.max(sum(2, 6), 5) wird das Ergebnis 8 zum ersten Argument von Math.max.void
printTotal kann nicht die rechte Seite einer int-Zuweisung füllen, nur weil er eine Zahl ausgibt.Vorzeitige Rückkehr
return candidate; ausgeführt wird, selbst wenn die Schleife noch weitere Kandidaten hat.W
Wertübergabe (Pass-by-Value)
- 1Java-Programme kompilieren und starten
- 2Bedingungen und Schleifen in Java
- 3Java-Methoden und Problemzerlegung
- 4Listen, Arrays und Strings in Java
- 5Java-Klassen und Objekte
- 6Objektreferenzen in Java
- 7HashMaps und Collections in Java
- 8Vererbung in Java
- 9Ausnahmen und Dateien in Java
- 10Modultests in Java
- 11Lambda-Ausdrücke und Streams in Java
Über diese Einheit
Erstelle deine eigenen Lernmaterialien
Mach aus deinen Notizen, Folien und Dokumenten Lernleitfäden, Übungsfragen, Karteikarten und 6 weitere Tools.