diff --git a/docs/additional-material/tools/debugging.mdx b/docs/additional-material/tools/debugging.mdx index 3d5c9d8e79..2024db4f9e 100644 --- a/docs/additional-material/tools/debugging.mdx +++ b/docs/additional-material/tools/debugging.mdx @@ -18,17 +18,15 @@ Laufzeit- und logische Fehler den Einsatz eines _Debuggers_. Ein Debugger ermöglicht es, ein Programm kontrolliert auszuführen, an beliebigen Stellen anzuhalten und den Zustand von Variablen sowie den Programmfluss zu -inspizieren. Moderne IDEs wie [Eclipse](https://eclipseide.org/) oder -[IntelliJ IDEA](https://www.jetbrains.com/idea/) verfügen über einen -integrierten Debugger. +inspizieren. [Eclipse](https://eclipseide.org/) verfügt über einen integrierten +Debugger. ## Weiterführende Links Die folgenden Ressourcen bieten eine vertiefte Einführung in das Debugging mit -den gängigsten Java-IDEs. +Eclipse. - [Eclipse Debugging Guide](https://www.eclipse.org/community/eclipse_newsletter/2017/june/article1.php) -- [IntelliJ IDEA – Debug Code](https://www.jetbrains.com/help/idea/debugging-code.html) - [Visualizing Execution with Java Visualizer](https://pythontutor.com/java.html) ## Breakpoints @@ -74,10 +72,10 @@ zu verfolgen. -**Step Over** (`F6` in Eclipse / `F8` in IntelliJ) führt die aktuelle Zeile -vollständig aus und hält in der nächsten Zeile derselben Methode an. -Methodenaufrufe werden dabei als ein Schritt behandelt – der Debugger springt -nicht in die aufgerufene Methode hinein. +**Step Over** (`F6`) führt die aktuelle Zeile vollständig aus und hält in der +nächsten Zeile derselben Methode an. Methodenaufrufe werden dabei als ein +Schritt behandelt – der Debugger springt nicht in die aufgerufene Methode +hinein. ```java title="Beispiel" showLineNumbers int a = 5; // <- Debugger hält hier an @@ -88,9 +86,8 @@ int c = b * 2; // <- Debugger hält hier an nach Step Over -**Step Into** (`F5` in Eclipse / `F7` in IntelliJ) springt in den Rumpf des -Methodenaufrufs in der aktuellen Zeile hinein, sodass dessen Ausführung Schritt -für Schritt verfolgt werden kann. +**Step Into** (`F5`) springt in den Rumpf des Methodenaufrufs in der aktuellen +Zeile hinein, sodass dessen Ausführung Schritt für Schritt verfolgt werden kann. ```java title="Beispiel" showLineNumbers int a = 5; // <- Debugger hält hier an @@ -101,16 +98,16 @@ int c = b * 2; -**Step Return** (`F7` in Eclipse / `Shift+F8` in IntelliJ) führt die restliche -aktuelle Methode vollständig aus und hält nach der Rückkehr in der aufrufenden -Methode an. Dieser Befehl wird typischerweise eingesetzt, wenn man versehentlich -in eine Methode hineingesprungen ist. +**Step Return** (`F7`) führt die restliche aktuelle Methode vollständig aus und +hält nach der Rückkehr in der aufrufenden Methode an. Dieser Befehl wird +typischerweise eingesetzt, wenn man versehentlich in eine Methode +hineingesprungen ist. -**Resume** (`F8` in Eclipse / `F9` in IntelliJ) setzt die Programmausführung -fort, bis der nächste Breakpoint erreicht wird oder das Programm regulär endet. +**Resume** (`F8`) setzt die Programmausführung fort, bis der nächste Breakpoint +erreicht wird oder das Programm regulär endet. @@ -122,15 +119,14 @@ angegebene Bedingung wahr ist. Das ist besonders nützlich, wenn ein Fehler nur unter bestimmten Umständen auftritt – etwa bei einem konkreten Schleifendurchlauf oder einem bestimmten Parameterwert. -```java title="Example: Fehler tritt nur bei i == 42 auf" showLineNumbers +```java title="Beispiel: Fehler tritt nur bei i == 42 auf" showLineNumbers for (int i = 0; i < 100; i++) { process(i); // <- bedingter Breakpoint: i == 42 } ``` In Eclipse wird ein bedingter Breakpoint über **Rechtsklick auf den Breakpoint → -Breakpoint Properties → Enable Condition** gesetzt. In IntelliJ IDEA über -**Rechtsklick auf den Breakpoint → More → Condition**. +Breakpoint Properties → Enable Condition** gesetzt. ## Häufige Fehler und deren Ursachen diff --git a/docs/documentation/algorithms.mdx b/docs/documentation/algorithms.mdx index aec2dd0c9f..7c69b4d321 100644 --- a/docs/documentation/algorithms.mdx +++ b/docs/documentation/algorithms.mdx @@ -134,15 +134,9 @@ Im nachfolgenden Beispiel wird die Zahlenfolge :::info -l = linke Grenze, r = rechte Grenze, m = Mitte - -::: - -:::info - -Bei der Ermittlung der Mitte wird i.d.R. die Abrundungsfunktion verwendet, d.h. -zu einer reellen Zahl wird die größte ganze Zahl, die kleiner als die reelle -Zahl ist, verwendet. +l = linke Grenze, r = rechte Grenze, m = Mitte. Bei der Ermittlung der Mitte +wird die Abrundungsfunktion verwendet, d.h. es wird die größte ganze Zahl +kleiner als die reelle Zahl verwendet. ::: @@ -290,15 +284,9 @@ auf beide Teile angewendet (Teile-und-Herrsche-Prinzip). :::info l = linke Grenze, r = rechte Grenze, m = Mitte, d = Datensammlung, i = linker -Index, j = rechter Index - -::: - -:::info - -Bei der Ermittlung der Mitte wird i.d.R. die Abrundungsfunktion verwendet, d.h. -zu einer reellen Zahl wird die größte ganze Zahl, die kleiner als die reelle -Zahl ist, verwendet. +Index, j = rechter Index. Bei der Ermittlung der Mitte wird die +Abrundungsfunktion verwendet, d.h. es wird die größte ganze Zahl kleiner als die +reelle Zahl verwendet. ::: diff --git a/docs/documentation/arrays.md b/docs/documentation/arrays.md index 78c6411737..7764d462af 100644 --- a/docs/documentation/arrays.md +++ b/docs/documentation/arrays.md @@ -61,16 +61,10 @@ public class MainClass { } ``` -:::info - -Der Index beginnt in Java bei 0. - -::: - ## Der Parameter _String[] args_ Der Parameter `String[] args` der main-Methode ermöglicht es, der Anwendung beim -Aufruf über die Kommandozeile Argumente zu übergeben. +Aufruf über die [Kommandozeile](java) Argumente zu übergeben. ```java title="MainClass.java" showLineNumbers public class MainClass { @@ -113,3 +107,32 @@ public class MainClass { Technisch gesehen handelt es sich bei einer variablen Argumentliste um ein Feld. ::: + +## Mehrdimensionale Felder + +Mehrdimensionale Felder sind Felder, deren Elemente selbst wieder Felder sind. +Der häufigste Anwendungsfall ist das zweidimensionale Feld, das sich als Matrix +mit Zeilen und Spalten vorstellen lässt. Der Zugriff auf ein Element erfolgt +über zwei Indizes: zuerst die Zeile, dann die Spalte. + +```java title="MainClass.java" showLineNumbers +public class MainClass { + + public static void main(String[] args) { + int[][] matrix = { + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9} + }; + + // alle Elemente zeilenweise ausgeben + for (int row = 0; row < matrix.length; row++) { + for (int col = 0; col < matrix[row].length; col++) { + System.out.print(matrix[row][col] + " "); + } + System.out.println(); + } + } + +} +``` diff --git a/docs/documentation/binary-numbers.mdx b/docs/documentation/binary-numbers.mdx index 9f48687097..bea5fa7d3e 100644 --- a/docs/documentation/binary-numbers.mdx +++ b/docs/documentation/binary-numbers.mdx @@ -12,7 +12,7 @@ Informationen wie Zahlen und Zeichen werden im Computer in Form von sogenannten _Bits_ (Binary Digits) gespeichert. Ein Bit ist die kleinstmögliche Informationseinheit und kann zwei Zustände annehmen: 0 und 1 (bzw. „an" und „aus", „wahr" und „falsch", „Strom fließt" und „Strom fließt nicht"). Zahlen, -die mit Hilfe von Bits dargestellt werden, nennt man _Binärzahlen_, das +die mithilfe von Bits dargestellt werden, nennt man _Binärzahlen_, das dazugehörige Zahlensystem _Binärsystem_. Zahlensysteme legen fest, wie Zahlen dargestellt werden — der Name verrät dabei jeweils, wie viele Ziffern zur Verfügung stehen. Um Verwechslungen zu vermeiden, können Zahlen mit einem nach- diff --git a/docs/documentation/class-structure.mdx b/docs/documentation/class-structure.mdx index 2f5b17c459..a5da936d64 100644 --- a/docs/documentation/class-structure.mdx +++ b/docs/documentation/class-structure.mdx @@ -8,9 +8,9 @@ tags: [class-structure] import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -Klassen stellen den grundlegenden Rahmen für Programme dar. Jede Klasse kann -Daten (_Attribute_) und Routinen (_Methoden_) besitzen. Routinen bestehen aus -Folgen von verzweigten und sich wiederholenden Anweisungen, wobei Anweisungen +Klassen sind der grundlegende Rahmen für Programme. Jede Klasse kann Daten +(_Attribute_) und Routinen (_Methoden_) besitzen. Routinen bestehen aus Folgen +von verzweigten und sich wiederholenden Anweisungen, wobei Anweisungen wohldefinierte Befehle darstellen, die der Interpreter zur Laufzeit ausführt. Anweisungen müssen in Java mit einem Semikolon abgeschlossen werden und können zu Anweisungsblöcken zusammengefasst werden, die durch geschweifte Klammern @@ -103,10 +103,10 @@ bezeichnet. ## Die main-Methode Die Methode `void main(args: String[])` ist eine spezielle Methode in Java und -stellt Startpunkt sowie Endpunkt einer Anwendung bzw. eines Programms dar. Nur -Klassen mit einer main-Methode können von der Laufzeitumgebung ausgeführt -werden. Aus diesem Grund werden Klassen mit einer main-Methode auch als -_ausführbare Klassen_ oder als _Startklassen_ bezeichnet. +ist Startpunkt sowie Endpunkt einer Anwendung. Nur Klassen mit einer +main-Methode können von der Laufzeitumgebung ausgeführt werden. Aus diesem Grund +werden Klassen mit einer main-Methode auch als _ausführbare Klassen_ oder als +_Startklassen_ bezeichnet. ```java title="MainClass.java" showLineNumbers public class MainClass { @@ -162,8 +162,8 @@ Einsatz von Quellcode-Kommentaren i.d.R. verzichtet werden sollte. ## Entwicklungspakete Entwicklungspakete ermöglichen das hierarchische Strukturieren von Klassen. Um -die Klassen eines Entwicklungspaketes verwenden zu können, müssen die jeweiligen -Klassen explizit mit Hilfe des Schlüsselworts `import` importiert werden. +die Klassen eines Entwicklungspaketes verwenden zu können, müssen sie mit dem +Schlüsselwort `import` importiert werden. ```mermaid flowchart diff --git a/docs/documentation/data-objects.md b/docs/documentation/data-objects.md index 80da16846b..288b50a17d 100644 --- a/docs/documentation/data-objects.md +++ b/docs/documentation/data-objects.md @@ -135,8 +135,8 @@ public class MainClass { Der Cast-Operator `()` erlaubt die explizite Umwandlung eines Datentyps in einen anderen. Bei Wertzuweisungen findet außerdem eine implizite Typumwandlung vom -niederwertigen zum höherwertigen Datentyp statt. Zu beachten ist, dass bei einer -Typumwandlung ein Genauigkeitsverlust auftreten kann. +niederwertigen zum höherwertigen Datentyp statt. Bei einer Typumwandlung kann +ein Genauigkeitsverlust auftreten. ```java title="MainClass.java" showLineNumbers public class MainClass { diff --git a/docs/documentation/exceptions.mdx b/docs/documentation/exceptions.mdx index 8db8f535f1..7ab9b8af76 100644 --- a/docs/documentation/exceptions.mdx +++ b/docs/documentation/exceptions.mdx @@ -59,6 +59,9 @@ classDiagram RunTimeException <|-- ArithmeticException ``` +Eine Übersicht wichtiger Ausnahmenklassen findet sich in der +[Java API](java-api). + ## Definition von Ausnahmenklassen Eigene Ausnahmenklassen werden durch Ableitung von einer bestehenden @@ -133,3 +136,45 @@ public class MainClass { } ``` + +## Der finally-Block + +Der optionale `finally`-Block wird nach dem `try`- und allen `catch`-Blöcken +immer ausgeführt — unabhängig davon, ob eine Ausnahme aufgetreten ist oder +nicht. Er eignet sich daher für Aufräumarbeiten wie das Schließen von +Ressourcen. + +```java title="MainClass.java" showLineNumbers +public class MainClass { + + public static void main(String[] args) { + try { + Notebook notebook = new Notebook("Mein Gaming Laptop", new Cpu(4.7, 8), 32, 16); + } catch (InvalidValueException e) { + System.err.println(e.getMessage()); + } finally { + System.out.println("wird immer ausgeführt"); + } + } + +} +``` + +Werden Ressourcen wie Dateiströme geöffnet, empfiehlt sich statt `finally` die +_Try-with-Resources_-Anweisung. Klassen, die `AutoCloseable` implementieren, +werden dabei am Ende des `try`-Blocks automatisch geschlossen — auch im +Fehlerfall. + +```java title="MainClass.java" showLineNumbers +public class MainClass { + + public static void main(String[] args) { + try (FileReader reader = new FileReader("data.txt")) { + // Datei lesen + } catch (IOException e) { + System.err.println(e.getMessage()); + } + } + +} +``` diff --git a/docs/documentation/hashing.mdx b/docs/documentation/hashing.mdx index 37fa66276b..c78db3ec27 100644 --- a/docs/documentation/hashing.mdx +++ b/docs/documentation/hashing.mdx @@ -130,3 +130,49 @@ einer Suche wird zuerst der Bucket bestimmt, dann der Bucket durchsucht. + +## Hashing in Java + +Java verwendet Hashing intern in den Klassen [`HashMap`](maps) und +[`HashSet`](java-collections-framework). Der Schlüssel eines Eintrags +bestimmt über seinen Hashcode den Bucket, in dem der Eintrag abgelegt wird. Bei +einer Suche wird zunächst der Bucket über den Hashcode gefunden, anschließend +wird innerhalb des Buckets mit `equals()` verglichen. + +Damit diese Mechanismen korrekt funktionieren, müssen die Methoden `hashCode()` +und `equals()` der [Klasse `Object`](object) konsistent überschrieben werden: + +- Zwei inhaltlich gleiche Objekte (`equals()` gibt `true` zurück) müssen + **denselben Hashcode** liefern. +- Zwei Objekte mit demselben Hashcode müssen **nicht** inhaltlich gleich sein + (eine Kollision ist erlaubt, aber ineffizient). + +```java title="Person.java (Auszug)" showLineNumbers +public class Person { + + private String name; + private int age; + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + Person other = (Person) object; + return age == other.age && Objects.equals(name, other.name); + } + + @Override + public int hashCode() { + return Objects.hash(name, age); + } + +} +``` + +:::warning + +Wird `equals()` überschrieben, ohne `hashCode()` anzupassen, verhält sich die +Klasse in `HashMap` und `HashSet` falsch: Zwei inhaltlich gleiche Objekte landen +in unterschiedlichen Buckets und werden nicht als gleich erkannt. + +::: diff --git a/docs/documentation/interfaces.mdx b/docs/documentation/interfaces.mdx index d6950b328f..b4b09d08ed 100644 --- a/docs/documentation/interfaces.mdx +++ b/docs/documentation/interfaces.mdx @@ -147,7 +147,7 @@ public class Smartphone implements MobileDevice { @Override public int getScreenSizeInInches() { - return Math.sqrt(Math.pow(widthInInches) + Math.pow(heightInInches)); + return (int) Math.sqrt(Math.pow(widthInInches, 2) + Math.pow(heightInInches, 2)); } ... } @@ -178,3 +178,11 @@ public class MainClass { + +:::tip + +Eine Schnittstelle eignet sich, wenn mehrere, voneinander unabhängige Klassen +dasselbe Verhalten teilen sollen. Eine [abstrakte Klasse](abstract-and-final) +eignet sich, wenn gemeinsame Implementierungen geerbt werden sollen. + +::: diff --git a/docs/documentation/java-stream-api.md b/docs/documentation/java-stream-api.md index 67c36ee893..eb6dd3ce49 100644 --- a/docs/documentation/java-stream-api.md +++ b/docs/documentation/java-stream-api.md @@ -100,7 +100,7 @@ intermediäre Operationen sind Filtern, Abbilden und Sortieren. | Operation | Methode | Schnittstellen-Methode | | ------------- | ---------------------------------------------------------- | -------------------------------- | | Filtern | `Stream filter(predicate: Predicate)` | `boolean test(t: T)` | -| Abbilden | `Stream map(mapper: Function)` | `R apply(t: T)` | +| Abbilden | `Stream map(mapper: Function)` | `R apply(t: T)` | | Abbilden | `DoubleStream mapToDouble(mapper: ToDoubleFunction)` | `double applyAsDouble(value: T)` | | Abbilden | `IntStream mapToInt(mapper: ToIntFunction)` | `int applyAsInt(value: T)` | | Abbilden | `LongStream mapToLong(mapper: ToLongFunction)` | `long applyAsLong(value: T)` | diff --git a/docs/documentation/lambdas.md b/docs/documentation/lambdas.md index d2a66aee60..25ba9949db 100644 --- a/docs/documentation/lambdas.md +++ b/docs/documentation/lambdas.md @@ -8,6 +8,28 @@ tags: [inner-classes, lambdas] Lambda-Ausdrücke sind anonyme Funktionen, die einer Variablen zugewiesen oder direkt als Parameter übergeben werden können. +## Funktionale Schnittstellen + +Voraussetzung für einen Lambda-Ausdruck ist eine _funktionale Schnittstelle_ — +eine Schnittstelle mit genau einer abstrakten Methode. Die Annotation +`@FunctionalInterface` kennzeichnet eine Schnittstelle als funktional und lässt +den Compiler prüfen, dass sie tatsächlich nur eine abstrakte Methode enthält. + +Das Paket `java.util.function` enthält häufig benötigte funktionale +Schnittstellen: + +| Schnittstelle | Abstrakte Methode | Beschreibung | +| ------------------ | --------------------------- | -------------------------------------- | +| `Predicate` | `boolean test(t: T)` | Prüft eine Bedingung | +| `Function` | `R apply(t: T)` | Bildet einen Wert auf einen anderen ab | +| `Consumer` | `void accept(t: T)` | Verarbeitet einen Wert ohne Rückgabe | +| `Supplier` | `T get()` | Liefert einen Wert ohne Eingabe | +| `UnaryOperator` | `T apply(t: T)` | Bildet einen Wert auf denselben Typ ab | +| `Comparator` | `int compare(o1: T, o2: T)` | Vergleicht zwei Werte | + +Diese Schnittstellen werden unter anderem von der +[Java Stream API](java-stream-api) verwendet. + ## Implementierung von Lambda-Ausdrücken Die Parameterliste und der Methodenkörper eines Lambda-Ausdrucks werden durch @@ -29,13 +51,6 @@ public class MainClass { } ``` -:::info - -Voraussetzung für Lambda-Ausdrücke ist eine _funktionale Schnittstelle_ — eine -Schnittstelle mit genau einer abstrakten Methode. - -::: - ## Syntaxvarianten Die Syntax von Lambda-Ausdrücken erlaubt verschiedene Kurzformen: diff --git a/docs/documentation/object.md b/docs/documentation/object.md index 3eae6ce980..b0b7ae5811 100644 --- a/docs/documentation/object.md +++ b/docs/documentation/object.md @@ -65,6 +65,15 @@ eine einfache Möglichkeit zur Implementierung von `hashCode()`. ::: +:::warning + +`equals()` und `hashCode()` müssen immer gemeinsam überschrieben werden. Gilt +für zwei Objekte `a.equals(b) == true`, muss auch `a.hashCode() == b.hashCode()` +gelten. Andernfalls verhalten sich hash-basierte Datenstrukturen wie `HashMap` +und `HashSet` falsch (siehe [Schlüsseltransformationen](hashing)). + +::: + ## Die Methode _String toString()_ Die Methode `String toString()` liefert eine eindeutige Kennung des Objekts in diff --git a/docs/documentation/trees.md b/docs/documentation/trees.md index 9100396a69..1c410b2aec 100644 --- a/docs/documentation/trees.md +++ b/docs/documentation/trees.md @@ -117,3 +117,33 @@ flowchart TD depth[Tiefendurchlauf: 6, 2, 1, 4, 3, 5, 8, 7, 9 Breitendurchlauf: 6, 2, 8, 1, 4, 7, 9, 3, 5] ``` + +## Binäre Suchbäume in Java + +Java verwendet binäre Suchbäume intern in den Klassen +[`TreeSet`](java-collections-framework) und [`TreeMap`](maps). Ein +binärer Suchbaum hält seine Elemente stets sortiert: Jeder Knoten ist größer als +alle Knoten im linken Teilbaum und kleiner als alle Knoten im rechten Teilbaum. +Dadurch ist die Suche, das Einfügen und das Löschen in 𝒪(log 𝑛) möglich. + +Damit `TreeSet` und `TreeMap` Elemente vergleichen können, muss die gespeicherte +Klasse entweder `Comparable` implementieren oder beim Erzeugen der Sammlung +ein `Comparator` übergeben werden (siehe [Komparatoren](comparators)). + +```java title="MainClass.java" showLineNumbers +public class MainClass { + + public static void main(String[] args) { + TreeSet numbers = new TreeSet<>(); + numbers.add(6); + numbers.add(2); + numbers.add(8); + numbers.add(1); + numbers.add(4); + + // Ausgabe erfolgt aufsteigend sortiert: 1 2 4 6 8 + numbers.forEach(System.out::println); + } + +} +```