Java Übungsbuch - Elisabeth Jung - E-Book

Java Übungsbuch E-Book

Elisabeth Jung

0,0

Beschreibung

  • Trainieren Sie Ihre Java-Kenntnisse
  • Learning by Doing anhand praktischer Übungen
  • Mit vollständigen und kommentierten Lösungen

Dieses Buch ist kein Lehrbuch, sondern ein reines Übungsbuch und wendet sich an Leser, die ihre Java-Kenntnisse anhand zahlreicher praktischer Übungen durch »Learning by Doing« vertiefen und festigen möchten. Es ist ideal, um sich auf Prüfungen vorzubereiten oder das Programmieren mit Java praktisch zu üben.

Jedes Kapitel beginnt mit einer kompakten Zusammenfassung des Stoffs, der in den Übungsaufgaben dieses Kapitels verwendet wird. Anschließend haben Sie die Möglichkeit, zwischen Aufgaben in drei verschiedenen Schwierigkeitsstufen – von einfach bis anspruchsvoll – zu wählen. Anhand dieser Aufgaben können Sie Ihr Wissen praktisch testen. Am Ende der Kapitel finden Sie vollständige und kommentierte Musterlösungen.

Es werden folgende Themen abgedeckt:

Die Kapitel 1 bis 3 enthalten Aufgaben zur objektorientierten Programmierung mit Java.

In Kapitel 4 geht es im Detail um Generics und das Collection Framework, seine generischen Klassen und Interfaces sowie die Definition von Enumerationen. Kapitel 5 erläutert das Exception Handling.

Kapitel 6 beschäftigt sich mit den neuen Sprachmitteln von Java 8, Lambdas und Streams. Kapitel 7 bietet einen Einblick in die mit Java 9 vorgenommene Modularisierung der Java-Plattform.

In Kapitel 8 werden Weiterentwicklungen aus den Java-Versionen 14 bis 17 behandelt. Dazu gehören die Einführung von Records und Sealed Classes sowie das Pattern Matching (sowohl für den instanceof-Operator als auch für switch). Kapitel 9 enthält viele Aufgaben zu Tests mit JUnit 5 und Gradle sowie eine kurze Einführung in beide Systeme.

Nach dem Durcharbeiten des Buches verfügen Sie über fundierte Programmierkenntnisse und einen umfangreichen Fundus an Beispielcode.

Aus dem Inhalt:

  • Klassendefinition und Objektinstanziierung
  • Abgeleitete Klassen und Vererbung
  • Abstrakte Klassen und Interfaces
  • Die Definition von Annotationen
  • Innere Klassen
  • Generics und Reflection
  • Lambdas
  • Funktionale Interfaces
  • Switch Expressions
  • Local Variable Type Inference
  • Definition und Nutzung von Streams
  • Das Modulsystem von Java
  • Records
  • Sealed Classes und Interfaces
  • Pattern Matching für den instanceof-Operator
  • Pattern Matching für switch
  • Tests mit JUnit 5 und Gradle

Sie lesen das E-Book in den Legimi-Apps auf:

Android
iOS
von Legimi
zertifizierten E-Readern
Kindle™-E-Readern
(für ausgewählte Pakete)

Seitenzahl: 600

Veröffentlichungsjahr: 2021

Das E-Book (TTS) können Sie hören im Abo „Legimi Premium” in Legimi-Apps auf:

Android
iOS
Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



Inhaltsverzeichnis
Impressum
Einleitung
Vorkenntnisse
Aufbau des Buches
Benötigte Software
Downloads
Autorin
Kapitel 1: Klassendefinition und Objektinstanziierung
1.1 Klassen und Objekte
Aufgabe 1.1: Definition einer Klasse
Aufgabe 1.2: Objekt (Instanz) einer Klasse erzeugen
1.2 Das Überlad‌en von Methoden
Aufgabe 1.3: Eine Methode überladen
1.3 Die Datenkapselu‌ng, ein Prinzip der objektorientierten Programmierung
Aufgabe 1.4: Zugriffsmethod‌en
1.4 Das »aktuelle Objekt« und die »this-Refere‌nz«
Aufgabe 1.5: Konstruktordefinitionen
1.5 Die Wert- und Referenzübergabe in Methodenaufrufen
Aufgabe 1.6: Wertübergabe in Methoden (»call by value«)
1.6 Globa‌le und lokale Referen‌zen
Aufgabe 1.7: Der Umgang mit Referenzen
1.7 Java-Pakete‌
Aufgabe 1.8: Die package-Anweisu‌ng
Aufgabe 1.9: Die import-Anweisung
1.8 Die Modifikatoren für Felder und Methoden in Zusammenhang mit der Definition von Paket‌en
Aufgabe 1.10: Pakete und die Sichtbarkeit von Membern einer Klasse
1.9 Standard-Klassen von Java
1.10 Die Wrapper-Klass‌en von Java und das Auto(un)boxing
Aufgabe 1.11: Das Auto(un)boxing
1.11 Das Paket java.lang.reflect
1.12 Arrays (Reihungen) und die Klassen Arr‌ay und Arr‌ays
Aufgabe 1.12: Der Umgang mit Array-Objekten
1.13 Zeichenketten und die Klasse String
Aufgabe 1.13: Der Umgang mit String-Objekten
Aufgabe 1.14: Der Umgang mit Textblöcken
1.14 Sprachmerkmale, die in den weiteren Beispielen genutzt werden
Aufgabe 1.15: Methoden mit variablen Argumentenlis‌ten
1.15 Den Zugriff auf Klassen und Felder minimieren: Unveränderliche (immutable) Klassen und Objekte
1.16 Die alte und neue Date&Time-API als Beispiel für veränderliche und unveränderliche Klassen
Aufgabe 1.16: Die Methoden von Date/Time-Klassen
1.17 Private Konstruktoren‌
Aufgabe 1.17: Objekte mithilfe eines privaten Konstruktors erzeugen
1.18 Lösungen
Lösung 1.1
Lösung 1.2
Lösung 1.3
Lösung 1.4
Lösung 1.5
Lösung 1.6
Lösung 1.7
Lösung 1.8
Lösung 1.9
Lösung 1.10
Lösung 1.11
Lösung 1.12
Lösung 1.13
Lösung 1.14
Lösung 1.15
Lösung 1.16
Lösung 1.17
Kapitel 2: Abgeleitete Klassen und Vererbung
2.1 Abgeleitete Klassen
2.2 Die Konstruktoren von abgeleiteten Klassen
2.3 Abgeleitete Klassen und die Sichtbarkeit von Feldern und Methoden
Aufgabe 2.1: Test von Sichtbarkeitsebenen im Zusammenhang mit abgeleiteten Klassen
2.4 Das Verdecken von Klassenmethoden und das statische Binden von Methoden
Aufgabe 2.2: Der Aufruf von verdeckten Klassenmethoden
2.5 Das Überschreiben von Instanzmethoden und das dynamische Binden von Methoden
Aufgabe 2.3: Das dynamische Binden von Methoden
2.6 Vererbung und Komposition
Aufgabe 2.4: Die Komposition
Aufgabe 2.5: Die Vererbung
2.7 Kovariante Rückgabetypen in Methoden
Aufgabe 2.6: Die Benutzung von kovarianten Rückgabetypen
2.8 Verdeckte Felder
2.9 Vergrößernde und verkleinernde Konvertierung (»up- und down-casting«)
2.10 Der Polymorphismus, ein Prinzip der objektorientierten Programmierung
Aufgabe 2.7: Der »Subtyp-Polymorphismus« im Kontext einer Klassenhierarchie
2.11 Die Methoden der Klassen java.lang.Object‌ und java.util.Objects‌
Aufgabe 2.8: Die equals()‌- und hashCode()‌-Methoden von Object
Aufgabe 2.9: Die equals()-Methode und die Vererbung
2.12 Das Klonen und die Gleichheit von geklonten Objekten
Aufgabe 2.10: Das Klonen von Instanzen der eigenen Klasse
Aufgabe 2.11: Das Klonen von Instanzen anderer Klassen
Aufgabe 2.12: Das Klonen und der Copy-Konstrukto‌r
2.13 Der Garbage Collector und das Beseitigen von Objekten
2.14 Lösungen
Lösung 2.1
Lösung 2.2
Lösung 2.3
Lösung 2.4
Lösung 2.5
Lösung 2.6
Lösung 2.7
Lösung 2.8
Lösung 2.9
Lösung 2.10
Lösung 2.11
Lösung 2.12
Kapitel 3: Die Definition von abstrakten Klassen, Interfaces und Annotationen
3.1 Abstrakte Klassen
3.2 Abstrakte Java-Standard-Klassen und eigene Definitionen von abstrakten Klassen
Aufgabe 3.1: Die abstrakte Klasse Number und ihre Unterklassen
Aufgabe 3.2: Definition einer eigenen abstrakten Klasse
3.3 Interfaces (Schnittstelle‌n)
Aufgabe 3.3: Die Definition eines Interface
3.4 Die Entscheidung zwischen abstrakten Klassen und Interfaces
Aufgabe 3.4: Paralleler Einsatz von Interfaces und abstrakten Klassen
3.5 Implementieren mehrerer Interfaces für eine Klasse
Aufgabe 3.5: Das Ableiten von Interfaces
3.6 Die Definition von inneren Klassen
Aufgabe 3.6: Ein Beispiel mit anonymer Klasse
3.7 Erweiterungen in der Definition von Interfaces
Aufgabe 3.7: Private Interface-Methoden
3.8 Die Definition von Annotationen
3.9 Vordefinierte Annotationstypen
Aufgabe 3.8: Annotationen an Methoden und Parameter von Methoden anheften
Aufgabe 3.9: Eine Klasse annotieren
Aufgabe 3.10: Die @Overrid‌e- und @Inherited-Annotation
3.10 Lösungen
Lösung 3.1
Lösung 3.2
Lösung 3.3
Lösung 3.4
Lösung 3.5
Lösung 3.6
Lösung 3.7
Lösung 3.8
Lösung 3.9
Lösung 3.10
Kapitel 4: Generics
4.1 Die Generizitä‌t
4.2 Generische Klassen und Interfaces
Aufgabe 4.1: Generischer Datentyp als Behälter für die Instanzen vom Typ des Klassenparameters
Aufgabe 4.2: Generischer Datentyp als »Über-Typ« für die Instanzen vom Typ des Klassenparameters
4.3 Wildcardtyp‌en
Aufgabe 4.3: Ungebundene Wildcardtypen
Aufgabe 4.4: Obere und untere Schranken für Wildcardtypen
4.4 Legacy Co‌de, Eras‌ure und Raw-T‌ypen
Aufgabe 4.5: Raw-Typen am Beispiel einer generischen Klasse mit zwei Typparametern
Aufgabe 4.6: Brückenmethode‌n (»bridge metho‌ds«)
4.5 Generische Arrays
Aufgabe 4.7: Erzeugen von generischen Arrays
4.6 Generische Methoden
Aufgabe 4.8: Generische Methodendefinitionen
4.7 Generische Standard-Klassen und -Interfaces
4.8 for-each-Schleifen‌ für Collections
Aufgabe 4.9: Das Interface List‌<E> und die Klasse ‌ArrayList<E>
Aufgabe 4.10: Das Interface Collection<E‌> und die Klasse Vector‌<E>
Aufgabe 4.11: Das Interface Map<K,V>‌ und die Klasse TreeMap<K,‌V>
4.9 Factory-Methoden in Collections
Aufgabe 4.12: Factory-Methoden für List, Set und Map
4.10 Die Interfaces Enumeration<‌E>, Iterable‌<T> und Iterato‌r<E>
4.11 Enumerationen und die generische Klasse Enum<E extends Enum<E>>
Aufgabe 4.13: Die Definition von Enumerationen
4.12 Die Interfaces Comparable<T> und Comparator<T> und das Sortieren von Objekten
Aufgabe 4.14: Das Comparable<T>‌-Interface
Aufgabe 4.15: Comparable<T> versus Comparator<T>‌
4.13 Typinferenz‌ für Methoden
4.14 Typinferenz‌ beim Erzeugen von Instanzen eines generischen Typs
Aufgabe 4.16: Typinferenz beim Instanziieren von generischen Klassen
Aufgabe 4.17: Der Diamond-Operator in Java 9
4.15 Lösungen
Lösung 4.1
Lösung 4.2
Lösung 4.3
Lösung 4.4
Lösung 4.5
Lösung 4.6
Lösung 4.7
Lösung 4.8
Lösung 4.9
Lösung 4.10
Lösung 4.11
Lösung 4.12
Lösung 4.13
Lösung 4.14
Lösung 4.15
Lösung 4.16
Lösung 4.17
Kapitel 5: Exceptions und Errors
5.1 Ausnahmen auslösen
5.2 Ausnahmen abfangen oder weitergeben
Aufgabe 5.1: Unbehandelte RuntimeExceptions
Aufgabe 5.2: Behandelte RuntimeExceptions
5.3 Das Verwenden von finally in der Ausnahmebehandlung
Aufgabe 5.3: Der finally-Block
5.4 Ausnahmen manuell auslösen
5.5 Exception-Unterklassen erzeugen
Aufgabe 5.4: Benutzerdefinierte Ausnahmen manuell auslösen
5.6 Multi-catch-Klausel und verbesserte Typprüfung beim Rethrowing von Exceptions
Aufgabe 5.5: Disjunction-Typ für Exceptions
Aufgabe 5.6: Typprüfung beim Rethrowing von Exceptions
5.7 Lösungen
Lösung 5.1
Lösung 5.2
Lösung 5.3
Lösung 5.4
Lösung 5.5
Lösung 5.6
Kapitel 6: Lambdas und Streams
6.1 Mittels anonymer Klassen Code an Methoden übergeben
6.2 Funktionale Interfaces
6.3 Syntax und Deklaration von Lambda-Ausdrücken
Aufgabe 6.1: Lambda-Ausdruck ohne Parameter versus anonymer Klasse
Aufgabe 6.2: Lambda-Ausdruck mit Parameter versus anonymer Klasse‌
6.4 Scoping‌ und Variable Capture‌
Aufgabe 6.3: Die Umgebung von Lambda-Ausdrücken
6.5 Methoden‌- und Konstruktor-Referenzen‌
Aufgabe 6.4: Methoden-Referenzen‌ in Zuweisungen
Aufgabe 6.5: Konstruktor-Referenzen und die neuen funktionalen Interfaces Supplier<T>‌ und Function<T,R>‌
6.6 Default- und statische Methoden in Interfaces
6.7 Das neue Interface Stream
6.8 Die forEach-Methoden von Iterator, Iterable und Stream
Aufgabe 6.6: Die funktionalen Interfaces BiConsumer<T,U>, BiPredicate<T,U> und BiFunction<T,U,R>
Aufgabe 6.7: Die Methoden des Interface Stream und die Behandlung von Exceptions in Lambda-Ausdrücken
6.9 Das Interface Collector und die Klasse Collectors: Reduktion mittels Methoden von Streams und Kollektoren.
Aufgabe 6.8: Weitere Methoden des Interface Stream: limit(), count(), max(), min(), skip(), reduce() und collect()
Aufgabe 6.9: Das Interface Collector‌ und die Klasse Collectors‌
6.10 Parallele Streams
Aufgabe 6.10: Parallele Streams
6.11 Die map(‌)- und flatMap‌()-Methoden von Stream und Optional
Aufgabe 6.11: map() versus flatMap()
6.12 Spracherweiterungen mit Java 10, Java 11, Java 12 und Java 13
Aufgabe 6.12: Typinferenz für lokale Variablen in Java 10 und Java 11
Aufgabe 6.13: Switch-Statements‌ und Switch-Expressions‌
6.13 Lösungen
Lösung 6.1
Lösung 6.2
Lösung 6.3
Lösung 6.4
Lösung 6.5
Lösung 6.6
Lösung 6.7
Lösung 6.8
Lösung 6.9
Lösung 6.10
Lösung 6.11
Lösung 6.12
Lösung 6.13
Kapitel 7: Die Modularität von Java
7.1 Das Java-Modulsystem
Aufgabe 7.1: Eine einfache Modul-Definition
Aufgabe 7.2: Eine Applikation mit mehreren Modulen
Aufgabe 7.3: Implizites Lesen von Modulen
Aufgabe 7.4: Eine modulbasierte Service-Implementierung
7.2 Lösungen
Lösung 7.1
Lösung 7.2
Lösung 7.3
Lösung 7.4
Kapitel 8: Records, Sealed Classes und Pattern Matching
8.1 Das Pattern Matching für den instanceof‌-Operator
8.2 Der neue Java-Typ Record‌
8.3 Sealed Classes‌ in Java
8.4 Das Pattern Matching für switch‌
Aufgabe 8.1: Die Definition von record-Klassen und das Pattern Matching für den instanceof-Operator‌
Aufgabe 8.2: sealed-, final- und non-sealed-Klassen
Aufgabe 8.3: sealed-Interfaces und das Pattern Matching
Aufgabe 8.4: Algebraische Datentypen‌ (ADTs), ein weiterer Schritt in Richtung funktionale Programmierung
Aufgabe 8.5: Das Pattern Matching für switch‌
8.5 Lösungen
Lösung 8.1
Lösung 8.2
Lösung 8.3
Kapitel 9: JUnit-Tests
9.1 JUnit 5 im Überblick
9.2 Tests schreiben
9.3 Testen mit dem ConsoleLauncher‌ und der JupiterEngine
Aufgabe 9.1: Die Klassen App und AppTest
Aufgabe 9.2: Die Klasse PublishingBookmitOrderingTest
Aufgabe 9.3: Die Klassen AdditionmitMap und AdditionmitMapTest
Aufgabe 9.4: Die Klassen MyClassTest und BuchmitEqualsTest
Aufgabe 9.5: Die Klasse TestBeispiele
Aufgabe 9.6: Die Klassen RechenOperationenTest und RechenOperationenParametrisierteTests
Aufgabe 9.7: Die Klasse AssertThrowsTest
9.4 JUnit-Tests mit Gradle‌
9.5 Lösungen
Lösung 9.1
Lösung 9.3
Lösung 9.4
Lösung 9.5

Elisabeth Jung

Java Übungsbuch

Für die Versionen Java 8 bis Java 17

Aufgaben mit vollständigen Lösungen

Impressum

Bibliografische Information der Deutschen Nationalbibliothek Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über <http://dnb.d-nb.de> abrufbar.

ISBN 978-3-7475-0451-2 1. Auflage 2021

www.mitp.de E-Mail: [email protected] Telefon: +49 7953 / 7189 - 079 Telefax: +49 7953 / 7189 - 082

© 2021 mitp Verlags GmbH & Co. KG

Dieses Werk, einschließlich aller seiner Teile, ist urheberrechtlich geschützt. Jede Verwertung außerhalb der engen Grenzen des Urheberrechtsgesetzes ist ohne Zustimmung des Verlages unzulässig und strafbar. Dies gilt insbesondere für Vervielfältigungen, Übersetzungen, Mikroverfilmungen und die Einspeicherung und Verarbeitung in elektronischen Systemen.

Die Wiedergabe von Gebrauchsnamen, Handelsnamen, Warenbezeichnungen usw. in diesem Werk berechtigt auch ohne besondere Kennzeichnung nicht zu der Annahme, dass solche Namen im Sinne der Warenzeichen- und Markenschutz-Gesetzgebung als frei zu betrachten wären und daher von jedermann benutzt werden dürften.

Lektorat: Sabine Schulz Sprachkorrektorat: Petra Heubach-Erdmann Covergestaltung: © maxec / stock.adobe.comelectronic publication: III-satz, Husby, www.drei-satz.de

Dieses Ebook verwendet das ePub-Format und ist optimiert für die Nutzung mit dem iBooks-reader auf dem iPad von Apple. Bei der Verwendung anderer Reader kann es zu Darstellungsproblemen kommen.

Der Verlag räumt Ihnen mit dem Kauf des ebooks das Recht ein, die Inhalte im Rahmen des geltenden Urheberrechts zu nutzen. Dieses Werk, einschließlich aller seiner Teile, ist urheberrechtlich geschützt. Jede Verwertung außerhalb der engen Grenzen des Urheherrechtsgesetzes ist ohne Zustimmung des Verlages unzulässig und strafbar. Dies gilt insbesondere für Vervielfältigungen, Übersetzungen, Mikroverfilmungen und Einspeicherung und Verarbeitung in elektronischen Systemen.

Der Verlag schützt seine ebooks vor Missbrauch des Urheberrechts durch ein digitales Rechtemanagement. Bei Kauf im Webshop des Verlages werden die ebooks mit einem nicht sichtbaren digitalen Wasserzeichen individuell pro Nutzer signiert.

Bei Kauf in anderen ebook-Webshops erfolgt die Signatur durch die Shopbetreiber. Angaben zu diesem DRM finden Sie auf den Seiten der jeweiligen Anbieter.

Einleitung

Das Java Übungsbuch: Für die Versionen Java 8 bis Java 17 ist wie alle meine Übungsbücher aus der Erkenntnis entstanden, dass zu umfangreiche Beispiele mit komplizierten Algorithmen beim Lernen von Java am Anfang keine echte Hilfe bieten. Darum liegt der Schwerpunkt des Buches nicht auf der Umsetzung von komplizierten Vorgängen, sondern konzentriert sich stattdessen darauf, die in der Dokumentation nicht immer verständlich formulierten Erläuterungen zu Java-Klassen und -Interfaces mit einfachen Beispielen zu erklären und gleichzeitig die zugrunde liegenden Konzepte zu erörtern.

Das Java Übungsbuch: Für die Versionen Java 8 bis Java 17 wendet sich in erster Linie an Lehrer, Schüler und Studenten als Begleitliteratur zum Lernen der Programmiersprache Java, ist aber auch zum Selbststudium für alle Interessenten an dem Erlernen der Programmiersprache geeignet.

Durch die Einfachheit und Vollständigkeit der Aufgabenlösungen sowie die unterschiedlichen Lösungsmöglichkeiten erhält der Leser ein fundiertes Verständnis für die Aufgabenstellungen und deren Lösungen.

Durch das Lösen von Aufgaben soll der in Referenz- und Lehrbüchern von Java angebotene Stoff vertieft werden, und die dabei erzielten Ergebnisse können anhand der Lösungsvorschläge überprüft werden. Die Beispiele im Buch sind eher selten von zu komplexer Natur, sodass der eigentliche Zweck nicht in den Hintergrund tritt, und alle beschriebenen Themen können tiefgehend und präzise damit eingeübt werden.

Vorkenntnisse

Es ist Voraussetzung, dass der Leser zusätzlich mit einem Lehrbuch zu Java arbeitet bzw. bereits damit gearbeitet hat. Die grundlegenden Erläuterungen zu Java in diesem Buch können lediglich als Wiederholung des bereits vorhandenen Wissens dienen, reichen aber nicht aus, um die Sprache Java erst neu zu lernen.

Als weitere Voraussetzung gelten Grundlagen im Bereich der Programmierung und im Umgang mit dem Betriebssystem. Ein paralleler Zugriff auf die Java-Online-Dokumentation kann Hilfe zu den Java-Standard-Klassen bieten.

Aufbau des Buches

Jedes Kapitel beginnt mit einer kurzen und knappen Wiederholung des Stoffes, der in den Übungsaufgaben dieses Kapitels verwendet wird. Danach folgen alle Aufgabenstellungen der Übungen. Am Ende des Kapitels stehen gesammelt die Lösungen der Übungsaufgaben mit Kommentaren, Erläuterungen und Hinweisen.

Die Aufgaben haben unterschiedliche Schwierigkeitsgrade. Dieser wird im Aufgabenkopf durch ein bis drei Sternchen gekennzeichnet:

ein Sternchen für besonders einfache Aufgaben, die auch von Anfängern leicht bewältigt werden können

zwei Sternchen für etwas kompliziertere Aufgaben, die einen durchschnittlichen Aufwand benötigen

drei Sternchen für Aufgaben, die sich an geübte Programmierer richten und einen wesentlich höheren Aufwand oder die Kenntnis von speziellen Details erfordern

Die Programme aus früheren Übungen werden teilweise in späteren Übungen gebraucht und es wird auch immer wieder auf theoretische Zusammenhänge zurückgekommen oder hingewiesen.

Die Lösungsvorschläge haben umfangreiche Kommentare, sodass ein Verständnis für die durchgeführte Aufgabe auch daraus abgeleitet werden kann und dadurch jede einzelne Aufgabe im Gesamtkontext unabhängig erscheint.

In den Kapiteln 1, 2 und 3 liegt das Hauptmerkmal auf den Eigenheiten der objektorientierten Programmierung mit Java. Durch eine Vielzahl von Beispielen wird gezeigt, was die Java-Standard-Klassen und Interfaces an Funktionalitäten bieten und wie diese sinnvoll in die Definition von eigenen Klassen eingebettet werden können. Diese Kapitel enthalten zusätzlich Informationen zur Reflection-API von Java, der Definition von Annotationen und inneren Klassen sowie Neuerungen aus den Versionen 8 bis 13, die sich auf die neue Date&Time-API, Textblöcke, Compact Strings und die Weiterentwicklung von Interfaces beziehen. Mit Java 8 wurden sogenannte Default-Methoden eingeführt. Diese werden in der Literatur auch als »virtual extension«- bzw. »defender«-Methoden bezeichnet und Schnittstellen, die über derartige Methoden verfügen, als erweiterte Schnittstellen. Damit können Interfaces zusätzlich zu abstrakten Methoden konkrete Methoden in Form von Standard-Implementierungen definieren und in Java wird die Mehrfachvererbung von Funktionalität ermöglicht. Neben Default-Methoden können Interfaces in Java nun auch statische Methoden enthalten. Anders als die statischen Methoden von Klassen werden diese jedoch nicht von abgeleiteten Typen geerbt.

Kapitel 4 beschäftigt sich im Detail mit Generics und dem Collection Framework mit all seinen generischen Klassen und Interfaces sowie mit der Definition von Enumerationen. Die Typinferenz für Methoden und beim Erzeugen von generischen Typen (der Diamond-Operator) sowie das Subtyping von parametrisierten und Wildcard-parametrisierten Typen sind ebenfalls Gegenstand der Themen aus diesem Kapitel.

Kapitel 5 erläutert das Exception-Handling.

Kapitel 6 beschäftigt sich mit den neuen Sprachmitteln von Java 8, Lambdas und Streams sowie mit weiteren Neuerungen aus den Versionen 8 bis 14, wie Switch-Expressions und Local Variable Type Inference.

Mit der Java-Version 8 haben sich ganz neue Betrachtungsweisen und Programmiertechniken in der Entwicklung von Applikationen mit Java eröffnet. Eine der wichtigsten Neuerungen in Java 8 sind neue Sprachmittel, die sogenannten Lambda-Ausdrücke, eine Art anonyme Methoden, die auf funktionalen Interfaces basieren. Diese besitzen jedoch eine viel kompaktere Syntax als Methoden. Das resultiert daraus, dass in ihrer Benutzung auf Namen, Modifikatoren, Rückgabetyp, throws-Klausel und in vielen Fällen auch auf Parameter verzichtet werden kann. Mit ihnen kann Funktionalität ausgeführt, gespeichert und übergeben werden, wie dies bisher nur von Instanzen in Java bekannt war.

Damit verbundene Themen wie die Gegenüberstellung zu anonymen Klassen, Syntax und Semantik, Behandlung von Exceptions, Scoping und Variable Capture, Methoden- und Konstruktor-Referenzen werden in den ersten Unterkapiteln des 6. Kapitels dieses Buches beschrieben und anhand von vielen Beispielen erläutert.

Des Weiteren finden Sie hier die Beschreibung aller neuen funktionalen Interfaces und deren Methoden. Die nachfolgenden Unterkapitel beschäftigen sich im Detail mit der Definition und Nutzung von Streams. Ein Stream besteht aus einer Folge von Werten (in der Literatur wird auch von Sequenzen von Elementen gesprochen), die nur teilweise von mehreren in einer Pipeline dazwischenliegenden Operationen ausgewertet und durch eine abschließende Operation bereitgestellt werden. Diese Operationen werden in Java als Methodenaufrufe formuliert, die Funktionalität in Form von Lambdas und Methoden-Referenzen entgegennehmen können und diese auf alle Elemente der Folge anwenden.

Mit einer Vielzahl von Aufgaben basierend auf Lambdas, Streams und Kollektoren (in denen Stream-Elemente angesammelt und reduziert werden können) werden die neuen Techniken angewandt und alle neuen Begriffe erklärt.

Kapitel 7 präsentiert das neue Java-Modulsystem. Mit dem neuen Modulsystem wurde Java selbst modular gemacht und es können eigene Applikationen und Bibliotheken modularisiert werden.

Java 9 führt das Modul als eine neue Programmkomponente ein. Das Erzeugen von Modulen und deren Abhängigkeiten führen dazu, dass der Zugriffsschutz in Java 9 restriktiver ist. Das Anlegen der erforderlichen Verzeichnisstrukturen für modulbasierte Applikationen, das Packaging von Modul-Code sowie die Implementierung von Services werden ebenfalls im Detail erklärt. Eine Vielzahl von Applikationen mit ausführlichen .cmd-Dateien für deren Ausführung ergänzen die theoretischen Erläuterungen aus diesem Kapitel.

In Kapitel 8 werden die Weiterentwicklungen aus der Programmiersprache mit den Versionen 14 bis 17 erläutert. Dazu gehören die Einführung von Records und Sealed Classes sowie das Pattern Matching.

Records wurden in der Version 14 entworfen, um die Wiederholungen von repetitivem Code in Datenklassen zu unterdrücken. Sie überlassen dem Compiler eine korrekte Generierung der Methoden equals(), hashCode(), toString() (die in Klassen, um eine Wertegleichheit von Objekten zu ermöglichen, überschrieben werden müssen) und von Zugriffsmethoden.

record-Klassen helfen in Kombination mit den in Java 15 neu eingeführten sealed-Klassen und -Interfaces, die auch mit Java 16 im Preview-Status bleiben und mit Java 17 finalisiert werden, die funktionalen Features von Java zu erweitern, insbesondere das Pattern Matching und in naher Zukunft die Destrukturierung von Objekten.

Sealed Classes und Interfaces sind Java-Datentypen, für die die Definition von Subtypen reduziert wird. Sie können nur von den in ihrer Deklaration angegebenen Typen erweitert bzw. implementiert werden.

Auch wenn es keine direkten Abhängigkeiten zwischen den Previews aus den JEPs 395, 394, 409, 406 und 405 gibt, die die Einführung dieser neuen Java-Datentypen sowie das Pattern Matching in Java beschreiben, so sind die von diesen vorgeschlagenen neuen Java-Features, wie mit vielen Beispielen in den Kapiteln 8 und 9 illustriert wird, sehr gut zusammen einsetzbar und im weitesten Sinne auch dafür gedacht.

Das Pattern Matching wurde in Java ursprünglich für den Abgleich von regulären Ausdrücken mit einem Text eingesetzt und für einen Vergleich von Typen im Zusammenhang mit dem instanceof-Operator und switch weiterentwickelt.

Der instanceof-Operator wurde erweitert, sodass anstelle eines Typ-Tests ein Musterabgleich-Test (»type test pattern«) durchgeführt wird. Dieser prüft die Übereinstimmung eines Zielobjekts mit einem vorgegebenen Mustertyp und erweist sich als sehr nützlich beim Schreiben von equals()-Methoden.

Mit Java 17 sind rund um das Pattern Matching weitere Funktionen im Zusammenhang mit Switch Statements und Switch Expressions realisiert worden. Damit werden die Restriktionen für den Typ des Ausdrucks, der im switch übergeben wird, weitestgehend aufgehoben. Bei einem klassischen switch waren zugelassen: ganzzahlige primitive Typen (char, byte, short, int) und die dazugehörigen Wrapper-Typen (Character, Byte, Short, Integer) sowie String und enum-Konstanten. Diese Auswahl wurde nun auf ganzzahlige primitive Typen und beliebige Referenztypen erweitert, sodass class-, enum-, record- und array-Typen zugelassen sind, die zusammen mit einem null-case-Label und einem default-Label die Angaben in den switch-case-Labels ausmachen können.

Die Destrukturierung von Objekten wird zusammen mit Record Patterns und Array Patterns (JEP 405) die Entwickler von nachfolgenden Java-Versionen weiter beschäftigen.

Neu in dieser Auflage des Buches sind Tests mit JUnit 5 und Gradle, die in Kapitel 9 beispielhaft präsentiert werden.

JUnit 5 kann von der Website https://junit.org/junit5/ unter »Latest Release« (aktuelle Version zum Zeitpunkt der Redaktion dieses Buches waren: Jupiter v5.7.1, Vintage v5.7.1, Platform v1.7.1) heruntergeladen werden.

Zum Testen von Applikationen werden, wie auch in den bevorstehenden Versionen von JUnit üblich, sogenannte Testklassen geschrieben. Sie beinhalten Methoden, die Testfälle beschreiben, den Rückgabetyp void aufweisen und durch Annotationen gekennzeichnet sind.

JUnit 5 führt darüber hinaus das Konzept eines ConsoleLaunchers ein, der benutzt werden kann, um Tests zu entwickeln, zu filtern und durchzuführen.

Um Ihnen ein gutes Verständnis für Details zu ermöglichen, wähle ich in diesem Buch die Ausführung über die Kommandozeile, die der ConsoleLauncher in diesem Fall ermöglicht.

Sicherlich sind Build-Tools wie Gradle und IDEs wie Eclipse, Intellij IDEA oder Maven eine große Hilfe nicht nur bei der Ausführung von JUnit-Tests, sondern generell in der Programmierung mit Java. Die Angabe von Details in diesem Zusammenhang würde den Rahmen dieses Buches jedoch sprengen.

In einem Unterkapitel in Kapitel 9 erfolgt eine kurze Beschreibung von Gradle und der Ausführung von Tests mit diesem Tool. Weil es gerade im Zusammenhang mit JUnit-Tests dem Anwender viel Kopfzerbrechen und Arbeit erspart, präsentiere ich es als Alternative zum ConsoleLauncher für die Durchführung von JUnit-Tests für die Java-Applikationen.

Eine neue Gradle-Version kann von der Website https://gradle.org/releases/ heruntergeladen werden. Zum Zeitpunkt der Buchredaktion war die Version v7.0.1 aktuell.

Weil der Schwerpunkt des Buches nicht auf der Umsetzung von aufwendigen Algorithmen liegen soll, verwende ich einfache Beispiele mit Zahlen, Buchstaben, Wörtern, Büchern, Wochentagen, geometrischen Figuren etc. und teilweise auch mit ganz abstrakten Klassennamen wie Klasse1, Klasse2, KlasseA, KlasseB etc.

An dieser Stelle möchte ich auf das dem Buch zugrunde liegende Konzept hinweisen, dass parallel zu einfachen Aufgaben, die zu allen eingeführten Definitionen und Begriffen gebracht werden, auch Aufgaben von einem höheren Schwierigkeitsgrad präsentiert werden. Dabei werden anhand von inhaltlichen Zusammenhängen zwischen den Beispielen viele Basiskonzepte von Java erläutert.

Ich habe generell versucht, keine Begriffe, Klassen und Komponenten zu benutzen, die nicht schon in vorangehenden Beispielen und Kapiteln definiert oder erläutert wurden. In den wenigen Fällen, wo es sich nicht vermieden ließ, wird darauf hingewiesen und auf die entsprechenden Stellen verwiesen.

Das Buch soll möglichst parallel zu einer Vielzahl von Java-Lehrbüchern eingesetzt werden können und einen Beitrag dazu leisten, die große Fülle von Informationen, die auf uns über die API-Dokumentation zukommt, besser einzuordnen und korrekt anwenden zu können.

Schrecken Sie nicht davor zurück, von Anfang an (gerade bei den schwierigeren Aufgaben) die Anforderungen aus dem Aufgabentext mit den Ergebnissen aus dem Lösungsvorschlag zu vergleichen (zumindest anfangs und vielleicht auch nur teilweise). Nahe an der Programmiersprache formuliert, sollen diese Aufgaben in erster Linie dazu dienen, das Programmieren mit Java zu erlernen, ohne sich gleichzeitig auf aufwendige Algorithmen zu konzentrieren. Es ist ja auch mit ein Grund, warum die Bücher vollständige Lösungsvorschläge beinhalten. Sie sind gerade dafür gedacht, die Theorie besser zu verstehen, aber auch gleichzeitig mit Beispielen einzuüben.

Andererseits verpflichten diese Bücher nicht, selbst auf die gleiche Lösung zu kommen, und enthalten nur Vorschläge zu den Lösungen von Aufgaben.

Anhand eines umfangreichen Index können Sie beim selbstständigen Programmieren im Buch nachschlagen, wenn Sie nach einem Lösungsansatz oder Hilfe beim Beseitigen von Fehlern suchen sollten.

Benötigte Software

Das aktuelle Java Development Kit der Java Standard Edition können Sie sich kostenlos von der Java-Homepage von Oracle http://www.oracle.com/technetwork/java/javase/downloads/index.html herunterladen. Das JDK umfasst sowohl die Software zur Programmerstellung als auch das JRE (Java Runtime Environment) für die Programmausführung.

Grafische Entwicklungsoberflächen sind, wie bereits erwähnt, keine Voraussetzung und auch nicht Bestandteil dieses Buches. Die Programme lassen sich grundsätzlich mit einem Texteditor wie z.B. Notepad++ oder auch Wordpad eingeben und über die Kommandozeile durch den Aufruf der Programme javac, jar und java übersetzen, paketieren und starten. Die vollständigen Programmaufrufe sind bei jeder Aufgabe angegeben.

Sollte Ihnen beim Erlernen der Programmiersprache selbst eine Entwicklungsumgebung nicht zu aufwendig oder unübersichtlich erscheinen, steht Ihnen nichts im Wege, die zu den Aufgaben zugehörigen Programm- und Klassendateien zum Testen oder auch Ergänzen in eine solche einzubetten.

Downloads

Die Website zum Buch mit der Adresse http://www.mitp.de/0449 beinhaltet den plattformunabhängigen Quellcode der Lösungsvorschläge und die für Windows kompilierte ausführbare Version als Download-Archiv. Diese Archivdatei enthält alle Java-Quellcodes, übersetzten Klassen und Bilddateien in einer Verzeichnisstruktur, die mit der im Buch beschriebenen übereinstimmt. Zusätzlich finden Sie auf dieser Webseite die Datei Java9Migration.pdf, in der die Migration von Anwendungen nach Java 9 beschrieben wird, die durch verschiedene Kategorien von Modulen unterstützt werden kann.

Ich wünsche Ihnen viel Erfolg beim Programmieren mit Java.

Elisabeth Jung

Autorin

Elisabeth Jung ist freie Autorin und wohnhaft in Frankfurt am Main.

Nach dem Studium der Mathematik an der Universität Temeschburg in Rumänien hat Elisabeth Jung parallel zur klassischen Mathematik Grundlagen der Informatik und Fortran unterrichtet. Im Jahr 1982 hat sie bereits eine Aufgabensammlung für Fortran an der gleichen Universität veröffentlicht.

In den Jahren 1984 und 2001 hat Elisabeth Jung bei der Firma Siemens in Frankfurt in einer Vielzahl von Projektarbeiten umfangreiche Erfahrungen gesammelt in den Bereichen Programmiersprachen (Assembler, Fortran, Pascal, C, C++, Java), Datenbanken (Text-Retrieval, relationale Systeme, Client-Server-Architekturen) und der Entwicklung und dem Test von Hardware-naher Systemsoftware.

Seit 2001 beschäftigt sie sich mit ihren bevorzugten Themen, der Mathematik und den objektorientierten Programmiersprachen, insbesondere Java. Ihre Bücher sind aus der Erfahrung im Unterricht und dem Erlernen von Programmiersprachen entstanden, bei dem insbesondere das eigene Programmieren und die praktische Anwendung des Gelernten eine große Rolle spielt.

Kapitel 1: Klassendefinition und Objektinstanziierung

1.1  Klassen und Objekte

Klassen‌ und Objekte bilden die Basis in der objektorientierten Programmierung. Eine Klasse ist eine Ansammlung von Attributen, die Eigenschaften definieren und Felder genannt werden, und von Funktionen, die deren Zustände und Verhaltensweisen festlegen und als Methoden bezeichnet werden. Felder und Methoden werden auch Member der Klasse genannt.

Klassen werden mit dem Schlüsselwort class eingeleitet und definieren eine logische Abstraktion, die eine Basisstruktur für Objekte vorgibt. Sie sind als eine Erweiterung der primitiven Datentypen zu sehen. Während Klassen Vorlagen (Modelle) definieren, sind Objekte konkrete Exemplare, auch Instanzen‌ der Klasse genannt.

Eine Schnittstelle (Interface)‌ ist eine reine Spezifikation, die definiert, wie eine Klasse sich zu verhalten hat. Sie wird mit dem Schlüsselwort interface eingeleitet und konnte zu den Anfangszeiten von Java keine Implementationen von Feldern und Methoden enthalten, mit Ausnahme von Konstantendefinitionen (die als static und final deklariert werden). Mit der Weiterentwicklung von Java wurden sowohl publicstatic-Methoden als auch public default-Methoden in Interfaces zugelassen (Java 8). Um redundanten Code zu vermeiden und die Benutzung von Interfaces zu verbessern, wurden mit Java 9 zusätzlich private-Methoden zugelassen. Einige dieser Methoden können Implementierungen liefern. Wir werden im weiteren Verlauf darauf näher eingehen.

Ein Objekt einer Klasse wird in Java mit dem new-Operator‌ und einem Konstruktor‌ erzeugt. Damit werden auch seine Felder initialisiert und der erforderliche Speicherplatz für das Objekt reserviert. Ein Objekt wird über eine Referenz angesprochen. Eine Referenz‌ entspricht in Java in etwa einem Zeiger in anderen Programmiersprachen und ist einem Verweis auf das Objekt gleichzustellen, womit dieses identifiziert werden kann.

Mit Referenztypen werden Datentypen bezeichnet, die im Gegensatz zu den primitiven Datentypen vom Entwickler selbst definiert werden. Diese können vom Typ einer Klasse, einer Schnittstelle oder eines Arrays sein. Ein Arraytyp identifiziert ein Objekt, das mehrere Werte von ein und demselben Typ speichern kann.

Die mit dem Modifikator static deklarierten Felder und Methoden in einer Klasse werden als Klassenfelder‌ bzw. Klassenmethoden‌ bezeichnet. Alle anderen Felder und Methoden einer Klasse werden auch Instanzfelder‌ bzw. Instanzmethoden‌ genannt.

Die Klassen bilden in Java eine Klassenhierarchie. Jede Klasse hat eine Oberklasse, deren Felder und Methoden sie erbt. Die Oberklasse aller Klassen in Java ist die Klasse java.lang.Object (siehe dazu Kapitel 2,Abgeleitete Klassen und Vererbung).

Beim Definieren von Klassen ist zu beachten, dass eine Klasse eine in sich funktionierende Einheit darstellt, die alle benötigten Felder und Methoden definiert.

Konstruktoren

Für die Initialisierungen eines Objekts der Klasse werden Konstruktoren‌ genutzt. Diese sind eine spezielle Art von Methoden. Sie haben den gleichen Namen wie die Klasse, zu der sie gehören, und verfügen über keinen Rückgabewert. Weil der new-Operator eine Referenz auf das erzeugte Objekt zurückgibt, ist eine zusätzliche Rückgabe von Werten in Konstruktoren nicht mehr erforderlich. Jede Klasse besitzt einen impliziten Konstruktor, der auch als Standard-Konstruktor bezeichnet wird. Dieser hat eine leere Parameterliste und übernimmt das Initialisieren der Instanzfelder mit den Defaultwerten der jeweiligen Datentypen. Eine Klasse kann mehrere explizite Konstruktoren definieren, die sich durch ihre Parameterlisten unterscheiden. Der parameterlose Konstruktor wird nur dann vom Compiler generiert, wenn die Klasse keinen expliziten Konstruktor definiert. Ist dies jedoch der Fall und die Klasse möchte auch den parameterlosen Konstruktor benutzen, so muss dieser ebenfalls explizit definiert werden.

Öffentliche (public) Konstruktoren werden von Klassen angeboten, damit auch ihre Benutzer, in der Literatur häufig als Clients bezeichnet, Instanzen davon erzeugen können. In diesem Zusammenhang wird immer öfter darauf hingewiesen (insbesondere wenn es um den produktiven Einsatz von Klassen geht), dass noch eine weitere Möglichkeit besteht, Instanzen von Klassen zu erzeugen, indem die Klasse eine oder mehrere statische Factory-Methoden zur Verfügung stellt. Laut Definition sind dies Klassenmethoden, die eine Instanz der Klasse zurückgeben. Ein Beispiel dafür sind die valueOf()-Methoden der Wrapper-Klassen für primitive Datentypen, wie Integer, Double etc. Diese Methoden müssen nicht unbedingt bei jedem Aufruf ein Objekt zurückliefern. Damit wird das unnötige Erzeugen von identischen Objekten vermieden und Klassen die Möglichkeit gegeben, mit bereits konstruierten Objekten zu arbeiten, indem diese abgespeichert und von den Methoden wiederholt zurückgegeben werden.

Der große Nachteil von derartigen Methoden, der auch beim Erstellen der Beispielklassen aus diesem Buch berücksichtigt wurde, ist jedoch, dass Klassen, die über keine public oder protected Konstruktoren verfügen, nicht erweitert werden können. Auch wenn damit Programmierer aufgefordert werden, des Öfteren Komposition anstelle von Vererbung (siehe dazu Kapitel 2) zu benutzen, ist und bleibt das Vererben von Klassen ein wesentlicher Bestandteil der Programmiersprache Java. Um »nahe an der Programmiersprache« alle Einzelheiten von Java mit möglichst einfachen Beispielen zu erlernen, brauchen wir Klassen, die das Ableiten zulassen und somit ihren Unterklassen den Aufruf der Konstruktoren von Oberklassen ermöglichen.

Klassenfelder und Klassenmethoden

Klassenfelder gehören nicht zu einzelnen Objekten, sondern zu der Klasse, in der sie definiert wurden. Alle durchgeführten Änderungen ihrer Werte werden von der Klasse und allen ihren Objekten gesehen. Jedes Klassenfeld ist nur einmal vorhanden. Darum sollten Klassenfelder benutzt werden, um Informationen, die von allen Objekten der Klasse benötigt werden, zu speichern. Diese Felder können direkt über den Klassennamen angesprochen werden und stehen zur Verfügung, bevor irgendein Objekt der Klasse erzeugt wurde.

Klassenmethoden können ebenfalls über den Klassennamen angesprochen werden.

Innerhalb der eigenen Klasse können alle Klassenfelder und Klassenmethoden auch ohne Klassennamen angesprochen werden, sollten aber, um den Richtlinien der objektorientierten Programmierung zu genügen, möglichst mit diesem verwendet werden.

Instanzfelder und Instanzmethoden

Instanzfelder sind mehrfach vorhanden, da für jedes Objekt eine Kopie von allen Instanzfeldern einer Klasse erstellt wird. Die Instanzen einer Klasse unterscheiden sich voneinander durch die Werte ihrer Instanzfelder. Innerhalb einer Klasse kann der Zugriff darauf direkt über ihren Namen erfolgen oder in der Form this.name bzw. obj.name (wobei obj eine Referenz auf ein Objekt der Klasse ist).

Das Schlüsselwort this bezeichnet die Referenz auf das »aktuelle Objekt« der Klasse, auch das »aufrufende Objekt« genannt. Damit ist ein Zugriff auf Objekteigenschaften (in Instanzfeldern gespeichert) jederzeit möglich. Konstruktoren werden mit dem Schlüsselwort new aufgerufen und innerhalb eines anderen Konstruktors über das Schlüsselwort this, gefolgt von der Parameterliste.

Java-Programme

Die Java-Programmtechnologie basiert auf die Zusammenarbeit von einem Compiler und einem Interpreter. Die Programme werden zuerst kompiliert, was einer syntaktischen Prüfung und der Erstellung von Bytecode‌ entspricht. Es entsteht dadurch noch kein ausführbares Programm, sondern ein plattformunabhängiger Code, der an einen Interpreter, die virtuelle Java-Maschine (JVM), übergeben wird. Die JVM‌ ist ein plattformspezifisches Programm, das Bytecode lesen, interpretieren und ausführen kann.

Ein Java-Programm manipuliert Werte, die durch Typ und Name gekennzeichnet werden. Über die Festlegung von Name und Typ wird eine Variable definiert. Man spricht von Variablen in Zusammenhang mit einem Programm und von Feldern in Zusammenhang mit einer Klassendefinition.

Eine Variable ist im Grunde genommen ein symbolischer Name für eine Speicheradresse. Während für primitive Variablen der Typ des Werts, der an dieser Adresse gespeichert wird, gleich dem Typ des Namens der Variablen ist, wird im Falle einer Referenzvariablen nicht der Wert von einem bestimmten Objekt an dieser Adresse gespeichert, sondern die Angabe, wo das Programm den Wert (das Objekt) von diesem Typ finden kann.

Im Gegensatz zu lokalen Variablen, die keinen Standardwert haben und deswegen nicht verwendet werden können, bevor sie nicht explizit initialisiert wurden, werden alle Felder in einer Klassendefinition automatisch mit Defaultwerten initialisiert (mit 0, 0.0, false primitive Typen und mit null Referenztypen).

Die Definition einer Referenzvariablen besteht aus dem Namen der Klasse bzw. eines Interface gefolgt vom Namen der Variablen. Eine so definierte Referenzvariable kann eine Referenz auf ein beliebiges Objekt der Klasse oder einer Unterklasse oder den Defaultwert null aufnehmen. Weil Arrays als Objekte implementiert werden, müssen Arrays mit einem Array-Initialisierer oder mit dem new-Operator erzeugt werden.

Bevor ein Programm Objekte von einer Klasse bilden kann, wird diese mit dem Java-Klassenlader (Klasse java.lang.ClassLoader) geladen und mit dem Java-Bytecode-Verifier geprüft.

Nach der Art der Ausführung existieren mehrere Arten von Java-Programmen:

Ein Java-Applet‌ ist ein Java-Programm, das im Kontext eines Webbrowsers‌ mit bestimmten Sicherheitseinschränkungen abläuft. Es wird mittels einer HTML-Seite gestartet und kann im Browser oder mithilfe des Appletviewers ausgeführt werden. Applets wurden mit der Version 9 von Java als deprecated gekennzeichnet und werden nicht mehr weiterentwickelt.

Ein Servlet‌ ist ein Java-Programm, das im Kontext eines Webservers abläuft.

Eine Java-Applikation‌ ist ein eigenständiges Programm, das direkt von der JVM gestartet wird. Alle nachfolgenden Beispiele werden als Java-Applikationen präsentiert.

Jede Java-Applikation benötigt eine main()‌-Methode, die einen Eingangspunkt für die Ausführung des Programms durch die JVM definiert. Diese Methode muss für alle Klassen der JVM zugänglich sein und deshalb mit dem Modifikator public definiert werden. Sie muss auch mit dem Modifikator static als Klassenmethode deklariert werden, da ein Aufruf dieser Methode möglich sein muss, ohne dass eine Instanz der Klasse erzeugt wurde. Von hier aus werden alle anderen Programmabläufe gesteuert.

Auf die Definition der Parameterliste der main()-Methode wird in nachfolgenden Programmbeispielen eingegangen.

Gleich in den ersten Beispielen werden für Bildschirmausgaben die Methoden System.out.print(...)‌ und System.out.println(...)‌ der Klasse java.io.PrintStream‌ verwendet. Mit der Methode System.out.print(...) wird in der gleichen Bildschirmzeile weitergeschrieben, in der eine vorangehende Ausgabe erfolgte. Die Methode System.out.println() ohne Parameter schließt eine vorher ausgegebene Bildschirmzeile ab und bewirkt, dass danach in eine neue Zeile geschrieben wird. Ein Aufruf der Methode System.out.println(...) mit Parameter ist äquivalent mit dem Aufruf von System.out.print(...) gefolgt von einem Aufruf von System.out.println() ohne Parameter, das heißt, dieser Aufruf führt immer zu einem Zeilenende. Auf die Definition von diesen Methoden kommen wir, in der Beschreibung der Java-Standard-Klasse java.lang.System‌ noch einmal zurück.

Ein Programm wird als Quelltext in einer oder mehreren .java-Dateien und als übersetztes Programm in einer oder mehreren .class-Dateien abgelegt.

Aufgabe 1.1: Definition einer Klasse

Definieren Sie eine Klasse KlassenDefinition, die die main()-Methode als einzige Klassenmethode implementiert. Aus dieser soll die Bildschirmanzeige »Dies ist eine einfache Klassendefinition« erfolgen.

Hinweise für die Programmierung

Ein Erzeugen von Instanzen der Klasse ist nicht erforderlich.

Achten Sie auf den richtigen Abschluss der Ausgabezeile.

Java-Dateien: KlassenDefinition.javaProgrammaufruf: java KlassenDefinition

Aufgabe 1.2: Objekt (Instanz) einer Klasse erzeugen

Definieren Sie eine Klasse ObjektInstanziierung, die in einem parameterlosen Konstruktor die Bildschirmanzeige »Instanz einer Java-Klasse erzeugen« vornimmt und in ihrer main()-Methode eine Instanz der Klasse erzeugt.

Java-Dateien: ObjektInstanziierung.javaProgrammaufruf: java ObjektInstanziierung

1.3  Die Datenkapselu‌ng, ein Prinzip der objektorientierten Programmierung

Den Feldern und Methoden einer Klasse können über Modifikatoren verschiedene Sichtbarkeitsebenen zugeordnet werden.

Der bereits erwähnte Modifikator public sagt aus, dass der Zugriff auf Member einer Klasse von überall aus erfolgen kann, von wo aus auch die Klasse erreichbar ist.

Sind die Felder oder Methoden mit private definiert, können sie nur innerhalb der eigenen Klasse direkt angesprochen werden. Felder sollten immer als private definiert werden, wenn die Zuweisung von unzulässigen Werten verhindert werden soll. Dies ist der Fall, wenn sie von einer eigenen Methode der Klasse, die diesen Wert auch ändern kann, verwendet oder weitergegeben werden.

Definiert die Klasse keine Einschränkungen diesbezüglich oder einen zugelassenen Wertebereich für Felder innerhalb, von dem auch andere Klassen Werte setzen können, sollte sie über Zugriffsmethoden‌ (»accessor-methods«) verfügen, die die Werte dieser Felder zurückgeben und ggf. setzen können. Dies entspricht dem sogenannten Prinzip der Datenkapselung: Auf die Felder einer Klasse soll nur mithilfe von Methoden der Klasse zugegriffen werden können.

Aufgabe 1.4: Zugriffsmethod‌en

Definieren Sie eine Klasse Punkt mit zwei Instanzfeldern vom Typ double, die die Koordinaten x und y eines Punkts im zweidimensionalen kartesischen Koordinatensystem beschreiben. Sie sollen von außerhalb der Klasse nur über die von Ihnen definierten Zugriffsmethoden setX(), setY(), getX() und getY() zugänglich sein und werden im Konstruktor der Klasse übergeben. Fügen Sie der Klasse eine zusätzliche Instanzmethode anzeige() für eine Punktanzeige am Bildschirm in der Form (x,y) hinzu.

Definieren Sie zum Testen der Klasse Punkt eine zweite Klasse PunktTest, die in ihrer main()-Methode eine Instanz der Klasse Punkt erzeugt und an dieser die Methoden der Klasse aufruft.

Java-Dateien: Punkt.java, PunktTest.javaProgrammaufruf: java PunktTest

1.4  Das »aktuelle Objekt« und die »this-Refere‌nz«

In jedem Konstruktor und in jeder Instanzmethode kann das aktuelle (aufrufende) Objekt der Klasse in Form einer this-Referenz angesprochen werden. Ein Konstruktoraufruf aus einem anderen Konstruktor erfolgt über this(parameterliste) und muss der zuerst erreichte übersetzte Programmcode in diesem Konstruktor sein. Aus anderen Methoden kann ein Konstruktor nicht über this aufgerufen werden, sondern nur mit dem new-Operator.

Aufgabe 1.5: Konstruktordefinitionen

Erstellen Sie eine Java-Klasse mit dem Namen Vektor, die drei Instanzfelder x, y und z definiert, die die Komponenten eines Vektors bezeichnen. Die Klasse definiert drei Konstruktoren:

den parameterlosen Konstruktor,

einen Konstruktor, der drei Argumente vom Typ int mit den gleichen Namen wie die der Instanzfelder übergeben bekommt

und den sogenannten Copy-Konstruktor‌, der als Parameter eine Referenz vom Typ der eigenen Klasse besitzt.

Der parameterlose Konstruktor soll über den Aufruf des zweiten Konstruktors alle Instanzfelder der Klasse auf 0 setzen.

Die Klasse soll über eine Methode für die Bildschirmanzeige eines Vektor-Objekts in der Form (x,y,z) verfügen.

Definieren Sie zwei weitere Methoden, die sich überladen, zum Erzeugen eines neuen Vektor-Objekts, das als Summe der aktuellen Instanz und einer übergebenen berechnet wird und deren Rückgabewert die aktuelle Instanz ist. Die erste Methode soll drei Parameter vom Typ int besitzen, die zweite Methode einen Parameter vom Typ Vektor.

Soll das ursprüngliche Objekt nicht verloren gehen, kann eine Kopie davon erzeugt werden. Eine dritte Methode im Lösungsvorschlag der Aufgabe berechnet die gleiche Summe, ohne dass die Instanz, an der die Methode aufgerufen wird, abgeändert wird. Bei gleicher Parameterliste muss die Methode über einen neuen Namen verfügen.

Zum Testen der Klasse Vektor soll eine zweite Klasse VektorTest erstellt werden, die in ihrer main()-Methode Instanzen der Klasse mithilfe ihrer Konstruktoren erzeugt und ihre Methoden aufruft.

Java-Dateien: Vektor.java, VektorTest.javaProgrammaufruf: java VektorTest

1.5  Die Wert- und Referenzübergabe in Methodenaufrufen

In Java-Methoden werden alle Argumente, ob es Werte von primitiven Typen oder Referenzen sind, als Kopie per Wert übergeben. Der Mechanismus der Wertübergabe wird auch »call by value‌« bzw. »copy per value« genannt. Wenn ein Argument übergeben wird, wird dessen Wert an eine Speicheradresse in den Stack der Methodenaufrufe (»method call stack«) kopiert. Egal ob dieses Argument eine Variable von einem primitiven oder Referenztyp ist, wird der Inhalt der Kopie als Parameterwert übergeben und nur diese kann innerhalb der Methode abgeändert werden, nicht der Wert selbst. Das heißt, eine Parametervariable wird als lokale Variable betrachtet, die zum Zeitpunkt des Methodenaufrufs mit dem entsprechenden Argument initialisiert wird und nach dem Beenden der Methode nicht mehr existiert.

Eine Argumentübergabe per Referenz, auch »call by reference‌« genannt, wie sie in anderen Programmiersprachen verwendet wird, gibt es in Java nicht. Für die Übergabe von Objekten werden zwar Referenzen vom Typ der Objekte als Parameter für Methoden definiert, doch werden diese, wie vorher beschrieben, kopiert. Aus diesem Grund ist in der Java-Literatur oft zu lesen: »In Java werden Objekte per Referenz und Referenzen per Wert übergeben.«

Aufgabe 1.6: Wertübergabe in Methoden (»call by value«)

Die Klasse MethodenParameter definiert drei Klassenmethoden mit den Signaturen publicmethode1(int x, int[] y), public methode2(Punkt x, Punkt[] y) und public methode3(Punkt x), wobei Punkt die Klasse aus der Aufgabe 1.4 bezeichnet.

Rufen Sie aus der main()-Methode der Klasse alle drei Methoden auf und zeigen Sie die Werte der von Ihnen übergebenen primitiven, Array- und Referenz-Typen vor und nach den Methodenaufrufen am Bildschirm an.

Hinweisefür die Programmierung

Um festzustellen, wie die Übergabe in Methodenaufrufen erfolgt, soll durch Zuweisungen und den Aufruf von Zugriffsmethoden der Klasse Punkt ein Teil der im Methodenaufruf übergebenen Werte verändert werden.

Java-Dateien: MethodenParameter.javaProgrammaufruf: java MethodenParameter

1.6  Globa‌le und lokale Referen‌zen

Alle bisherigen Programme haben Referenzvariablen als lokale Referenzen in Methoden oder als deren Parametervariablen definiert. Instanz- und Klassenfelder von einem Referenztyp werden auch als globale Referenzen bezeichnet.

Aufgabe 1.7: Der Umgang mit Referenzen

Definieren Sie eine Klasse GlobaleReferenzen, die anstelle der lokalen Variablen aus den Methoden der Klasse MethodenParameter globale Programmvariablen definiert und die Methoden selbst ohne Parametervariablen.

Hinweise für die Programmierung

Referenzparameter von Methoden können im Prinzip durch globale Referenzen der Klasse ersetzt werden, nur sind die darauf durchgeführten Änderungen innerhalb von Methoden auch nach außen sichtbar. Dabei macht es kein Unterschied, ob die globalen Referenzen als Klassen- bzw. Instanzfelder definiert wurden.

Referenzparameter von Methoden können im Prinzip durch globale Referenzen der Klasse ersetzt werden, nur sind die darauf durchgeführten Änderungen innerhalb von Methoden auch nach außen sichtbar. Dabei macht es kein Unterschied, ob die globalen Referenzen als Klassen- bzw. Instanzfelder definiert wurden.

Java-Dateien: GlobaleReferenzen.javaProgrammaufruf: java GlobaleReferenzen

1.8  Die Modifikatoren für Felder und Methoden in Zusammenhang mit der Definition von Paket‌en

Auf ein Member einer Klasse, das ohne Modifikator definiert wurde, kann von außerhalb eines Pakets nicht zugegriffen werden. Nur mit public deklarierte Member sind uneingeschränkt öffentlich. Ein mit protected definiertes Member ist außerhalb eines Pakets nur für abgeleitete Klassen einer Klasse sichtbar. Weitere Ergänzungen zu diesen Aussagen können in Kapitel 2, Abgeleitete Klassen und Vererbung (Abschnitt 2.3), gelesen werden.

Kapitel 7 aus diesem Buch beschäftigt sich mit dem neuen Modulsystem von Java und kommt nochmals in Abschnitt 7.2 auf die Sichtbarkeitsebenen innerhalb von Paketen im Zusammenhang mit Modulen zurück.

Aufgabe 1.10: Pakete und die Sichtbarkeit von Membern einer Klasse

Die Klassen aus diesen Programmbeispielen sollen als Test der import-Anweisung für Pakete dienen, die Subpakete beinhalten. Definieren Sie zu diesem Zweck eine Klasse PackageTest1, deren Programmdatei im Verzeichnis kapitel1 abgelegt ist und Instanzen von zwei weiteren Klassen, Klasse1 und Klasse2 erzeugt, die in den Unterverzeichnissen paket1 und paket2 von kapitel1 in Programmdateien mit gleichem Namen abgelegt werden.

Die Klasse Klasse1 definiert drei Klassenfelder vom Typ int: privatesFeld, geschuetztesFeld, oeffentlichesFeld mit den Modifikatoren private, protected, public und ein weiteres Klassenfeld feld ohne Modifikator. Sie soll die Zeichenkette "Instanz der Klasse1" am Bildschirm anzeigen und den Paketnamen über die Anweisung: package paket1; angeben. Die Klasse Klasse2 soll die Zeichenkette "Instanz der Klasse2" am Bildschirm anzeigen und die Anweisung: package paket2; für die Angabe des Paketnamens definieren.

In der Klasse PackageTest1 sollen beide Paketnamen über eine import-Anweisung bekannt gegeben werden und soweit möglich die Werte der in Klasse1 definierten Felder am Bildschirm angezeigt werden.

Das Java-Programm PackageTest1.java soll im Verzeichnis kapitel1 übersetzt und ausgeführt werden.

Java-Dateien: kapitel1\PackageTest1.java, kapitel1\paket1\Klasse1.java, kapitel1\paket1\paket2\Klasse2.javaProgrammaufrufe im Verzeichnis kapitel1: javac PackageTest1.java und java PackageTest1

1.9  Standard-Klassen von Java

Von großer Bedeutung in der Programmierung mit Java sind seine Standard-Klassen, die die sogenannte Java-API‌ bilden. Die Standard-Klassen von Java sind in Pakete gebündelt, wie z.B. java.lang, java.io, java.util etc. Das Java-Paket java.lang beinhaltet die Klassen, die die Basis der Java-Programmiersprache bilden, wie Object‌, System‌, Process‌, ProcessBuilder‌, Runtime‌, Math‌, Class<T>‌ etc. Diese Klassen werden automatisch vom Compiler importiert, dafür ist keine import-Anweisung nötig.

Zur Identifikation einer Klasse muss bei allen Paketen außer java.lang‌ der Paketname dem Klassennamen vorangestellt werden, wie z.B. mit import java.util.List. Sollen alle Klassen eines Pakets importiert werden, geschieht dies über einen Stern statt über den Klassennamen, wie z.B. import java.util.*.

Mit der Version 9 von Java wurde die Modularisierung‌ als Spracherweiterung eingeführt, mit der Pakete in Module zusammengefasst werden. Gleichzeitig wurden Aktivitäten rund um die Modularisierung der Java-Plattform selbst gestartet und die JDK und JRE modularisiert. Wie bereits erwähnt, können die Neuerungen, die das Modulsystem von Java mit sich bringt, in Kapitel 7 gelesen und eingeübt werden.

Die Klasse System

Die Klasse System kann nicht instanziiert werden, da sie keinen Konstruktor besitzt. Sie besitzt aber eine Vielzahl von nützlichen Klassenfeldern und Klassenmethoden. Dazu zählen Felder, die den Zugriff auf die Standardein‌- und Standardausgabe‌ erlauben, und andere, die Systemeigenschaften und Umgebungsvariablen definieren.

Diese Klasse definiert als Klassenfelder die globalen Referenzen in, out und err, die auf Objekte vom Typ der Klassen InputStream‌ und PrintStream‌ aus dem Paket java.io‌ verweisen, die Ein- und Ausgaben zu den Standardgeräten (in der Regel die Konsole‌) leiten. Das Objekt, auf das die Referenz err der Klasse PrintStream zeigt, wird für die Ausgabe von Fehlermeldungen benutzt. Alle drei Instanzen werden beim Programmaufruf erzeugt, mit den Standardgeräten verbunden und stehen jederzeit dem Programmierer zur Verfügung.

Die Instanz, auf die die Referenz out zeigt,wird auch mit den Methoden System.out.print(...) und System.out.println(...) der Klasse PrintStream genutzt, um Bildschirmausgaben zu realisieren. Wir haben diese bereits in den vorangegangenen Aufgaben verwendet.

Eine Liste mit allen Systemeigenschaften‌ kann mit der Methode getProperties()‌und die Liste der im System gesetzten Umgebungsvariablen mit der Methode getenv()‌ der Klasse System abgerufen werden.

Die Klasse File‌

Die Verwaltung von Dateien und deren Verzeichnissen wird in Java u.a. von der Klasse File aus dem Paket java.io übernommen. Ein File-Objekt ist eine abstrakte Repräsentation einer Datei oder eines Verzeichnisses. Die Klasse java.nio.Files (Java 7) stellt zusätzliche Methoden zur Verfügung, mit denen Informationen über Dateien und Verzeichnisse geholt werden können.

Mit der Methode write() der java.io.FileWriter‌-Klasse kann ein Text (als Stream von Characters) in eine Datei geschrieben werden. Dabei werden die Characters in Bytes decodiert. Um direkt Bytes in eine Datei zu schreiben, kann ein Stream vom Typ FileOutputStream benutzt werden.

Die Klassen Runtime, ProcessBuilder und Process

Beim Ausführen eines Programms mithilfe des java-Kommandos wird eine JVM gestartet und vom Betriebssystem dazu ein eigener Prozess erzeugt. Es können auch mehrere JVMs gestartet werden, die entsprechenden Prozesse laufen dann parallel, wobei jeder Prozess seinen eigenen Adressraum besitzt.

Eine Instanz der Klasse Runtime‌repräsentiert die Laufzeitumgebung einer Java-Anwendung und kann über den Aufruf der Methode getRuntime()‌ der Klasse ermittelt werden. Über dieses Objekt kann die aktuell laufende JVM mit der Methode exit()‌ beendet werden. Dies ist auch über die gleichnamige Methode der Klasse System möglich und wird über deren Aufruf System.exit()‌ eingeleitet.

Über den Aufruf der Methode Process exec(String name) der Klasse, wobei name den Namen einer .exe-Datei spezifiziert und der Rückgabewert vom Typ Process ist, kann eine andere Anwendung gestartet werden.

Die Klasse ProcessBuilder‌ wurde mit Java 5.0 eingeführt und kann alternativ zur Klasse Runtime zum Starten eines Prozesses des Betriebssystems genutzt werden.

Ein Objekt der Klasse Process kann durch einen der Methodenaufrufe Runtime.exec bzw. ProcessBuilder.start() erzeugt werden und repräsentiert einen Prozess des Betriebssystems.

Die Klassen Exception‌ und Error‌

Exceptions sind Ausnahmesituationen‌, die zur Laufzeit eines Programms auftreten und seinen Ablauf unterbrechen können. Diese können behandelt werden, sodass ein Programmabbruch dabei vermieden wird. Errors sind schwerwiegende Fehler, eine weitere Ausführung des Programms ist bei deren Auftreten meistens nicht mehr gerechtfertigt.

Exceptions werden über das Schlüsselwort‌throw ausgelöst und können von einem catch-Block‌ aufgefangen werden, in dem deren Verarbeitung erfolgen kann. Dazu werden die Anweisungen, die Exceptions auslösen, zu einem try-Block‌ zusammengefasst und diesem wird ein catch-Block nachgestellt. Exceptions kann man zur Behandlung auch weitergeben, indem sie mit dem Schlüsselwort throws im Methodendefinitionskopf durch Komma getrennt aufgelistet werden. Wird eine Exception auch von der main()-Methode mit throws weitergeworfen, so wird diese nicht mehr von der Applikation behandelt und die Applikation wird mit einer entsprechenden Fehlermeldung beendet.

Diese Klassen und ihre Unterklassen befinden sich auch im Paket java.lang und werden in Kapitel 5, Exceptions und Errors, ausführlicher behandelt.