06:51 Uhr

[ab 2026/1] Aufrufe: 149, Hits: 232

Java-Programme. Lese- und Lernhilfe für [Staud 2026a] (Version 2026/7/24)

 

Copyright 2025, 2026 Prof. Dr. Josef L. Staud

 

Autor: Josef L. Staud

Umfang des gedruckten Textes: ca. 344 Seiten

Es ist eine auf langer Erfahrung beruhende Erkenntnis, dass eine Programmiersprache leichter erlernt werden kann, wenn der Lernstoff durch möglichst viele praxisnahe Beispielprogramme ergänzt wird. Dies war das erste Motiv für diesen Text. Das zweite Motiv besteht darin, Erläuterungen zu den Java-Beispielen in [Staud 2026a] bereitzustellen. Der Text stellt keine Einführung in Java dar; hierzu sei auf die im Literaturverzeichnis aufgeführten Bücher verwiesen und auf ChatGPT. Er soll vielmehr eine Beziehung zwischen den Konstrukten der UML in [Staud 2026a] und ihrer Umsetzung in einer objektorientierten Programmiersprache herstellen. Zur UML 2.5 findet sich eine umfassende Darstellung in [Staud 2019].

Diese HTML-Seiten wurden mit Hilfe eines von mir erstellten Programms erzeugt: WebGenerator2 (Version 2023). Es setzt Texte in HTML-Seiten um und wird ständig weiterentwickelt. Die "maschinelle" Erstellung erlaubt es, nach jeder Änderung des Textes diesen unmittelbar neu in HTML-Seiten umzusetzen. Da es nicht möglich ist, nach jeder Neuerstellung alle Seiten zu prüfen, ist es durchaus möglich, dass irgendwo auf einer "abgelegenen" Seite Fehler auftreten. Ich bitte dafür um Verzeihung und um Hinweise (hs@staud.info).

Die Veröffentlichung im Web erfolgt ab 2022 in zwei Versionen: Mit und ohne Frame-Technologien. Zu meinem Bedauern wird die Frame-Technologie inzwischen von den Verantwortlichen als unerwünscht angesehen und es häufen sich die Hinweise, dass bestimmte Browser Frame-basierte Seiten nicht mehr korrekt interpretieren können. Deshalb habe ich eine zweite Version meines Programms WebGenerator erstellt, die ohne Frames realisiert ist.

Urheberrecht

Dieser Text ist urheberrechtlich geschützt. Die dadurch begründeten Rechte, insbesondere die der Übersetzung, des Nachdrucks, des Vortrags, der Entnahme von Abbildungen und Tabellen oder der Vervielfältigung auf anderen Wegen und der Speicherung in Datenverarbeitungsanlagen, bleiben, auch bei nur auszugsweiser Verwertung, vorbehalten. Eine Vervielfältigung dieses Textes oder von Teilen dieses Textes ist auch im Einzelfall nur in den Grenzen der gesetzlichen Bestimmungen des Urheberrechtsgesetzes der Bundesrepublik Deutschland vom 9. September 1965 in der jeweils geltenden Fassung zulässig. Sie ist grundsätzlich vergütungspflichtig. Zuwiderhandlungen unterliegen den Strafbestimmungen des Urheberrechtsgesetzes.

Warenzeichen und Markenschutz

Alle in diesem Text genannten Gebrauchsnamen, Handelsnamen, Marken, Produktnamen, usw. unterliegen warenzeichen-, marken- oder patentrechtlichem Schutz bzw. sind Warenzeichen oder eingetragene Warenzeichen der jeweiligen Inhaber. Die Wiedergabe solcher Namen und Bezeichnungen in diesem Text berechtigt auch ohne besondere Kennzeichnung nicht zu der Annahme, dass solche Namen im Sinne der Gesetzgebung zu Warenzeichen und Markenschutz als frei zu betrachten wären und daher von jedermann benutzt werden dürften.

 

 

Prof. Dr. Josef Ludwig Staud

 

Vorwort, Inhalt, Abkürzungen

Vorwort

Der Gegensatz von Struktur und Verhalten ist in der IT (bei den Digitalisierungsbemühungen) allgegenwärtig. Denken wir nur Programme und Datenbanken. Oder an Geschäftsprozesse vs. Datenbanken. Für Struktur in diesem Sinne wurden vor Jahrzehnten ausgefeilte Datenbanktechniken entwickelt, die derzeit mit NoSQL-Datenbanken eine an die Anforderungen des Internetzeitalters angepasste Form gefunden haben. Für Verhalten in diesem Sinne wurden die Programmiersprachen entwickelt, die schon immer erlaubten, Verarbeitungsvorgänge zu erfassen (dafür wurden sie geschaffen), und die es heute erlauben, die Dynamik ganzer Geschäftsprozesse in Programmen darzustellen.

Struktur und Verhalten

In der UML 2.5 sind die Strukturaspekte betont, vgl. [Staud 2019]. Hier soll nun gezeigt werden, wie diese in Java umgesetzt werden können.

 

Inhaltsverzeichnis der Druckversion

Vorwort, Inhalt, Abkürzungen.. 3

1 Einleitung.. 17

1.1 Konzept 17

1.2 Bibliotheken. 17

1.3 Packages. 18

1.4 Formatierung und Schreibweise. 18

2 Grundlagen. 19

2.1 Datentypen. 19

2.2 Attribute. 19

2.3 Logische Operatoren. 20

2.4 Referenzen. 21

2.5 Der Begriff "Typ" in Java. 22

3 Konsolenarbeit. 24

3.1 Ausgeben auf Konsole. 24

3.2 Lesen von der Konsole. 24

4 Klassen, Objekte, Methoden. 26

4.1 Klassen einrichten. 26

4.1.1 Grundlagen. 26

4.1.2 Sichtbarkeit 27

4.2 Objekte einrichten - Konstruktoren. 27

4.3 Record-Klassen. 29

4.4 Aufruf von Methoden und Attributen. 29

5 Prozedurales. 31

5.1 Schleifen und Verzweigungen. 31

5.2 Das Try/Catch-Konstrukt 32

6 Textdateien bearbeiten. 34

6.1 Einleitung. 34

6.2 Paket java.io. 34

6.3 Zugriffe mit Streams. 34

6.4 Klasse File. 35

7 Exceptions. 38

8 Projekt HS1 - Objekterzeugung.. 39

8.1 Klasse HP (Hauptprogramm; main) 39

8.2 Klasse Stud. 41

8.3 Ergebnis Programmlauf 43

8.4 Betrachtete Themen. 44

9 Projekt HS2 - Objekte und Referenzen.. 45

9.1 Klasse HP. 45

9.2 Klasse Stud. 49

9.3 Ergebnis Programmlauf 49

9.4 Anmerkungen zum Programm.. 50

9.5 Betrachtete Themen. 51

10 Projekt HS3 - Array mit Objekten. 52

10.1 Klasse HP. 52

10.2 Klasse Stud. 56

10.3 Ergebnis Programmlauf 57

10.4 Beschreibung des Programms. 58

10.5 Betrachtete Themen. 59

11 Projekt HS4 - Aufrufe über Klassengrenzen. 60

11.1 Klasse HP. 60

11.2 Klasse Noten. 61

11.3 Klasse Stud. 62

11.4 Ergebnis Programmlauf 63

11.5 Beschreibung des Programms. 64

11.6 Betrachtete Themen. 65

12 Projekt HS5 - Eingaben.. 66

12.1 Klasse HP. 66

12.2 Klasse Stud. 68

12.3 Ergebnis Programmlauf 69

12.4 Anmerkungen zum Programm.. 69

12.5 Betrachtete Themen. 69

13 Exkurs - Nachrichtenverkehr. 71

14 Projekt HS6 - Record-Klassen. 72

14.1 Projektbeschreibung. 72

14.2 Klasse HP. 73

14.3 Klasse Stud. 75

14.4 Ergebnis Programmlauf 76

14.5 Betrachtete Themen. 76

14.6 Exkurs - Record vs. Klasse. 77

14.7 Exkurs - Methode hashCode() 78

15 Projekt HS7 - Vererbung, Attribute. 79

15.1 Klasse Fahrzeuge. 79

15.2 Klasse PKW... 80

15.3 Klasse LKW... 81

15.4 Klasse VAN.. 82

15.5 Klasse HP. 83

15.6 Ergebnis Programmlauf 86

15.7 Betrachtete Themen. 88

15.8 Projektbeschreibung. 88

15.8.1 Ziel des Projekts. 88

15.8.2 Aufbau des Projekts. 89

15.8.3 Beschreibung der einzelnen Klassen. 89

15.8.4 Ablauf des Programms. 90

16 Projekt HS7a - Override und Overload.. 92

16.1 Klasse HP. 92

16.2 Klasse Fahrzeuge. 92

16.3 Klasse PKW... 92

16.4 Ergebnis Programmlauf 93

16.5 Betrachtete Themen. 93

16.6 Exkurs: Override vs. Overload. 94

17 Projekt HS8 - Kapselung.. 95

17.1 Klasse HP. 95

17.2 Klasse Stud. 96

17.3 Ergebnis Programmlauf 98

17.4 Projektbeschreibung. 98

17.5 Betrachtete Themen. 99

17.6 Anmerkung. 100

18 Projekt HS9 - Assoziationen. 101

18.1 Klasse HP. 101

18.2 Klasse Stud. 101

18.3 Klasse Note. 102

18.4 Ergebnis Programmlauf 103

18.5 Erläuterungen zum Programm.. 103

18.6 Betrachtete Themen. 103

19 Projekt hsdaten - Dateien.. 105

19.1 Klasse Erstellen. 105

19.1.1 Das Programm.. 105

19.1.2 Anmerkung. 107

19.1.3 Betrachtete Themen. 107

19.2 Klasse Schreiben. 107

19.2.1 Das Programm.. 107

19.2.2 Anmerkung. 109

19.2.3 Betrachtete Themen. 109

19.3 Klasse Schreiben2. 109

19.3.1 Das Programm.. 109

19.3.2 Anmerkung. 111

19.3.3 Betrachtete Themen. 111

19.4 Klasse Lesen. 112

19.4.1 Das Programm.. 112

19.4.2 Anmerkung. 112

19.4.3 Betrachtete Themen. 113

19.5 Klasse Anhaengen. 113

19.5.1 Das Programm.. 113

19.5.2 Anmerkung. 115

19.5.3 Theorieaspekte. 116

19.5.4 Betrachtete Themen. 116

19.6 Klasse Loeschen. 117

19.6.1 Das Programm.. 117

19.6.2 Ergebnis Programmlauf 118

19.6.3 Erläuterungen. 118

19.6.4 Betrachtete Themen. 119

19.7 Klasse ErzeugLoesch. 120

19.7.1 Ergebnis Programmlauf 122

19.7.2 Erläuterungen. 122

19.7.3 Betrachtete Themen. 123

19.8 Klasse EinlesenCSV.. 124

19.8.1 Das Programm.. 124

19.8.2 Erläuterungen. 126

19.8.3 Betrachtete Themen. 127

20 Übungsaufgaben Teil 1. 128

20.1 Aufgabe 1 - Klasse und Objekt 128

20.1.1 Aufgabenstellung. 128

20.1.2 Lernziele. 128

20.1.3 Lösung. 129

20.1.4 Ergebnis Programmlauf 129

20.1.5 Betrachtete Themen. 129

20.1.6 Erläuterung. 129

20.2 Aufgabe 2 - Referenzen. 131

20.2.1 Aufgabenstellung. 131

20.2.2 Lernziele. 131

20.2.3 Lösung. 132

20.2.4 Ergebnis Programmlauf 132

20.2.5 Betrachtete Themen. 133

20.2.6 Erläuterung. 133

20.3 Aufgabe 3a - Arrays Einführung. 134

20.3.1 Aufgabenstellung. 134

20.3.2 Lernziele. 134

20.3.3 Lösung. 134

20.3.4 Betrachtete Themen. 135

20.3.5 Erläuterung. 135

20.3.6 Hinweis - Arrays von Objekten. 136

20.4 Aufgabe 3b - Arrays mit Objekten. 138

20.4.1 Aufgabenstellung. 138

20.4.2 Lernziele. 138

20.4.3 Lösung. 139

20.4.4 Ergebnis Programmlauf 139

20.4.5 Betrachtete Themen. 139

20.4.6 Erläuterung. 140

21 Übungsaufgaben Teil 2. 141

21.1 Aufgabe 4a - Klassen ohne Beziehung. 141

21.1.1 Aufgabenstellung. 141

21.1.2 Lernziele. 141

21.1.3 Ergebnis Programmlauf 141

21.1.4 Lösung. 142

21.1.5 Betrachtete Themen. 143

21.1.6 Erläuterung. 143

21.2 Aufgabe 4b -Einseitig. 144

21.2.1 Aufgabenstellung. 144

21.2.2 Lernziele. 144

21.2.3 Ergebnis Programmlauf 144

21.2.4 Lösung. 145

21.2.5 Ergebnis Programmlauf 146

21.2.6 Betrachtete Themen. 146

21.2.7 Erläuterung. 146

21.2.8 Exkurs - Von Arrays zu Listen. 147

21.3 Aufgabe 4c - Bidirektional 149

21.3.1 Aufgabenstellung. 149

21.3.2 Lernziele. 149

21.3.3 Lösung. 150

21.3.4 Ergebnis Programmlauf 152

21.3.5 Betrachtete Themen. 152

21.3.6 Theorieaspekt 152

21.4 Aufgabe 4d - n:m-Beziehung. 154

21.4.1 Aufgabe. 154

21.4.2 Lernziele. 154

21.4.3 Lösung. 154

21.4.4 Ergebnis Programmlauf 156

21.4.5 Betrachtete Themen. 156

21.4.6 Anmerkung. 156

21.5 Aufgabe 4e - Assoziationsklasse. 157

21.5.1 Aufgabe. 157

21.5.2 Lernziele. 157

21.5.3 Lösung. 158

21.5.4 Betrachtete Themen. 159

21.5.5 Anmerkung. 159

22 Übungsaufgaben Teil 3. 161

22.1 Aufgabe 5 - Eingaben. 161

22.1.1 Aufgabenstellung. 161

22.1.2 Gewünschte Ausgabe. 161

22.1.3 Lernziele. 161

22.1.4 Lösung. 162

22.1.5 Betrachtete Themen. 163

22.2 Aufgabe 6 - Record. 164

22.2.1 Aufgabenstellung. 164

22.2.2 Lernziele. 164

22.2.3 Lösung. 165

22.2.4 Ergebnis Programmlauf 166

22.2.5 Betrachtete Themen. 167

22.2.6 Exkurs: Wiederholung zu records. 167

22.3 Aufgabe 7 - Vererbung. 169

22.3.1 Aufgabenstellung. 169

22.3.2 Lernziele. 169

22.3.3 Lösung. 171

22.3.4 Ergebnis Programmlauf 172

22.3.5 Betrachtete Themen. 172

22.3.6 Erläuterung. 172

22.4 Aufgabe 8 - Kapselung. 174

22.4.1 Aufgabenstellung. 174

22.4.2 Lernziele. 174

22.4.3 Lösung. 175

22.4.4 Betrachtete Themen. 176

22.4.5 Erläuterung. 176

22.5 Aufgabe 9 - Overload. 177

22.5.1 Aufgabenstellung. 177

22.5.2 Ergebnis Programmlauf: 177

22.5.3 Lernziele. 177

22.5.4 Lösung. 177

22.5.5 Betrachtete Themen. 179

22.5.6 Erläuterung. 179

23 Übungsaufgaben Teil 4. 180

23.1 Aufgabe 10 - Einstieg. 180

23.1.1 Aufgabenstellung. 180

23.1.2 Lernziele. 180

23.1.3 Lösung. 181

23.1.4 Betrachtete Themen. 181

23.1.5 Ergebnis Programmlauf 181

23.2 Aufgabe 11 - Konstruktoren. 182

23.2.1 Aufgabenstellung. 182

23.2.2 Lernziele. 182

23.2.3 Lösung. 183

23.2.4 Ergebnis Programmlauf 183

23.2.5 Betrachtete Themen. 183

23.3 Aufgabe 12 - Array. 184

23.3.1 Aufgabenstellung. 184

23.3.2 Lernziele. 184

23.3.3 Ergebnis Programmlauf 184

23.3.4 Lösung. 185

23.3.5 Betrachtete Themen. 185

23.3.6 Erläuterung. 185

23.4 Aufgabe 13 - Kapselung. 187

23.4.1 Aufgabenstellung. 187

23.4.2 Lernziele. 187

23.4.3 Lösung. 188

23.4.4 Ergebnis Programmlauf 188

23.4.5 Betrachtete Themen. 188

23.4.6 Erläuterung. 188

23.5 Aufgabe 14 - Vererbung. 190

23.5.1 Aufgabenstellung. 190

23.5.2 Lernziele. 190

23.5.3 Lösung. 191

23.5.4 Ergebnis Programmlauf 191

23.5.5 Betrachtete Themen. 191

23.5.6 Erläuterung. 191

23.6 Aufgabe 15 - Override. 193

23.6.1 Aufgabenstellung. 193

23.6.2 Lernziele. 193

23.6.3 Lösung. 194

23.6.4 Ergebnis Programmlauf 194

23.6.5 Betrachtete Themen. 194

23.6.6 Erläuterung. 194

23.7 Aufgabe 16 - Polymorphie. 196

23.7.1 Aufgabenstellung. 196

23.7.2 Ergebnis Programmlauf 196

23.7.3 Lernziele. 196

23.7.4 Lösung. 197

23.7.5 Betrachtete Themen. 197

23.7.6 Erläuterung. 197

23.7.7 Exkurs Polymorphie. 198

24 Übungsaufgaben Teil 5. 201

24.1 Aufgabe 17 - Abstrakte Klasse. 201

24.1.1 Aufgabenstellung. 201

24.1.2 Lernziele. 201

24.1.3 Lösung. 202

24.1.4 Ergebnis Programmlauf 202

24.1.5 Betrachtete Themen. 202

24.1.6 Exkurs - Abstrakte Klasse. 202

24.2 Aufgabe 18 - Interface. 204

24.2.1 Aufgabenstellung. 204

24.2.2 Lernziele. 204

24.2.3 Lösung. 205

24.2.4 Betrachtete Themen. 205

24.2.5 Erläuterung. 205

24.2.6 Exkurs - Interface. 206

24.2.7 Interface vs. abstrakte Klasse. 207

24.3 Aufgabe 19 - 1:1. 210

24.3.1 Aufgabenstellung. 210

24.3.2 Lernziele. 210

24.3.3 Lösung. 211

24.3.4 Ergebnis Programmlauf 212

24.3.5 Betrachtete Themen. 212

24.3.6 Erläuterung. 212

24.4 Aufgabe 19b - Bidirektional 214

24.4.1 Aufgabenstellung. 214

24.4.2 Lernziele. 214

24.4.3 Lösung. 214

24.4.4 Ergebnis Programmlauf 216

24.4.5 Betrachtete Themen. 216

24.4.6 Erläuterungen. 216

24.5 Aufgabe 20 - n:m.. 217

24.5.1 Aufgabenstellung. 217

24.5.2 Lernziele. 217

24.5.3 Lösung. 218

24.5.4 Betrachtete Themen. 219

24.5.5 Erläuterung. 220

24.6 Aufgabe 21 - Klassendiagramm HS. 221

24.6.1 Aufgabe. 221

24.6.2 Lernziele. 222

24.6.3 Lösung. 222

24.6.4 Ergebnis Programmlauf 225

24.6.5 Betrachtete Themen. 225

25 Übungsaufgaben Teil 6. 226

25.1 Aufgabe 22 - Liste. 226

25.1.1 Aufgabenstellung. 226

25.1.2 Lernziele. 227

25.1.3 Lösung. 227

25.1.4 Betrachtete Themen. 231

25.1.5 Anmerkungen. 231

25.1.6 Exkurs - Verkettete Listen. 232

25.2 Aufgabe 23 - Unternehmen. 234

25.2.1 Aufgabenstellung. 234

25.2.2 Lernziele. 235

25.2.3 Lösung. 235

25.2.4 Ergebnis Programmlauf 239

25.2.5 Betrachtete Themen. 240

25.2.6 Anmerkung. 240

25.2.7 Aufgabenstellung. 241

25.2.8 Lernziele. 241

25.2.9 Lösung. 242

25.2.10 Ergebnis Programmlauf 242

25.2.11 Betrachtete Themen. 243

25.2.12 Erläuterung. 243

25.3 Aufgabe 25 - Abteilungszugehörigkeit 244

25.3.1 Aufgabenstellung. 244

25.3.2 Ergebnis Programmlauf 244

25.3.3 Lernziele. 244

25.3.4 Lösung. 245

25.3.5 Ergebnis Programmlauf 246

25.3.6 Betrachtete Themen. 246

25.3.7 Erläuterung. 246

25.4 Aufgabe 26 - PC.. 248

25.4.1 Aufgabenstellung. 248

25.4.2 Lernziele. 248

25.4.3 Lösung. 249

25.4.4 Ergebnis Programmlauf 250

25.4.5 Betrachtete Themen. 250

25.4.6 Erläuterung. 250

25.5 Aufgabe 27 - Projekte. 252

25.5.1 Aufgabenstellung. 252

25.5.2 Lernziele. 252

25.5.3 Lösung. 253

25.5.4 Ergebnis Programmlauf 254

25.5.5 Betrachtete Themen. 254

26 Übungsaufgaben Teil 7. 255

26.1 Aufgabe 28 - n:m-Beziehung. 255

26.1.1 Aufgabenstellung. 255

26.1.2 Lernziele. 255

26.1.3 Lösung. 256

26.1.4 Ergebnis Programmlauf 257

26.1.5 Betrachtete Themen. 257

26.2 Aufgabe 29 - Rekursive Beziehung. 259

26.2.1 Aufgabenstellung. 259

26.2.2 Lernziele. 259

26.2.3 Ergebnis Programmlauf 259

26.2.4 Lösung. 260

26.2.5 Betrachtete Themen. 261

26.2.6 Erläuterung. 261

26.3 Aufgabe 30 - Gehaltsauswertung. 262

26.3.1 Aufgabenstellung. 262

26.3.2 Lernziele. 262

26.3.3 Ergebnis Programmlauf 262

26.3.4 Lösung. 263

26.3.5 Betrachtete Themen. 264

26.4 Aufgabe 31 - Projektstatistik. 265

26.4.1 Aufgabenstellung. 265

26.4.2 Lernziele. 265

26.4.3 Ergebnis Programmlauf 265

26.4.4 Lösung. 266

26.4.5 Betrachtete Themen. 267

26.4.6 Erläuterung. 267

26.5 Aufgabe 32 - Mini-Personalverwaltung. 269

26.5.1 Aufgabenstellung. 269

26.5.2 Lernziele. 269

26.5.3 Lösung. 270

26.5.4 Ergebnis Programmlauf 274

26.5.5 Betrachtete Themen. 275

27 Übungsaufgaben Teil 8. 276

27.1 Aufgabe 33 - Vererbung. 276

27.1.1 Aufgabenstellung. 276

27.1.2 Lernziele. 276

27.1.3 Ergebnis Programmlauf 276

27.1.4 Lösung. 277

27.1.5 Betrachtete Themen. 277

27.1.6 Erläuterung. 278

27.2 Aufgabe 34 - Override. 279

27.2.1 Aufgabenstellung. 279

27.2.2 Lernziele. 279

27.2.3 Ergebnis Programmlauf 279

27.2.4 Lösung. 280

27.2.5 Betrachtete Themen. 281

27.2.6 Erläuterung. 281

27.3 Aufgabe 35 - Overload. 282

27.3.1 Aufgabenstellung. 282

27.3.2 Lernziele. 282

27.3.3 Ergebnis Programmlauf 282

27.3.4 Lösung. 283

27.3.5 Betrachtete Themen. 283

27.3.6 Erläuterung. 284

27.4 Aufgabe 36 - Abstraktes. 285

27.4.1 Aufgabenstellung. 285

27.4.2 Lernziele. 285

27.4.3 Ergebnis Programmlauf 285

27.4.4 Lösung. 286

27.4.5 Betrachtete Themen. 287

27.4.6 Erläuterung. 287

28 Übungsaufgaben Teil 9. 288

28.1 Aufgabe 37 - Interface. 288

28.1.1 Aufgabenstellung. 288

28.1.2 Lernziele. 288

28.1.3 Ergebnis Programmlauf 288

28.1.4 Lösung. 289

28.1.5 Betrachtete Themen. 289

28.1.6 Erläuterung. 290

28.2 Aufgabe 38 - Mehrere Interfaces. 291

28.2.1 Aufgabenstellung. 291

28.2.2 Lernziele. 291

28.2.3 Ergebnis Programmlauf 291

28.2.4 Lösung. 292

28.2.5 Betrachtete Themen. 292

28.2.6 Erläuterung. 293

28.3 Aufgabe 39 - Polymorphie. 294

28.3.1 Aufgabenstellung. 294

28.3.2 Lernziele. 294

28.3.3 Ergebnis Programmlauf 294

28.3.4 Lösung. 295

28.3.5 Betrachtete Themen. 296

28.3.6 Ziel der Aufgabe, vertieft 296

28.4 Aufgabe 40 - Laufzeittyp. 297

28.4.1 Aufgabenstellung. 297

28.4.2 Lernziele. 297

28.4.3 Ergebnis Programmlauf 297

28.4.4 Lösung. 298

28.4.5 Betrachtete Themen. 299

28.4.6 Erläuterung. 299

28.5 Aufgabe 40a - Angestellte. 300

28.5.1 Aufgabenstellung. 300

28.5.2 Lernziele. 300

28.5.3 Ergebnis Programmlauf 301

28.5.4 Exkurs - Explizit vs, implizit 301

28.5.5 Lösung. 302

28.5.6 Erläuterungen. 305

28.5.7 Betrachtete Themen. 305

29 Übungsaufgaben Teil 10. 306

29.1 Aufgabe 41 - Records. 306

29.1.1 Aufgabenstellung. 306

29.1.2 Lernziele. 306

29.1.3 Lösung. 307

29.1.4 Betrachtete Themen. 307

29.1.5 Erläuterung. 307

29.2 Aufgabe 42 - Projektmitarbeit 309

29.2.1 Aufgabenstellung. 309

29.2.2 Lernziele. 310

29.2.3 Lösung. 311

29.2.4 Ergebnis Programmlauf 314

29.2.5 Betrachtete Themen. 314

29.2.6 Erläuterung. 315

29.3 Aufgabe 43 - Exception Handling. 316

29.3.1 Aufgabenstellung. 316

29.3.2 Lernziele. 316

29.3.3 Ergebnis Programmlauf 316

29.3.4 Lösung. 317

29.3.5 Betrachtete Themen. 317

29.3.6 Erläuterung. 317

29.4 Aufgabe 44 - Eigene Exception. 318

29.4.1 Aufgabenstellung. 318

29.4.2 Lernziele. 318

29.4.3 Ergebnis Programmlauf 318

29.4.4 Lösung. 319

29.4.5 Betrachtete Themen. 319

29.4.6 Erläuterung. 320

29.5 Aufgabe 45 - Generics. 321

29.5.1 Aufgabenstellung. 321

29.5.2 Lernziele. 321

29.5.3 Ergebnis Programmlauf 321

29.5.4 Lösung. 322

29.5.5 Betrachtete Themen. 323

29.5.6 Erläuterung. 323

30 Übungsaufgaben Teil 11. 325

30.1 Aufgabe 46 - HashSet 325

30.1.1 Aufgabenstellung. 325

30.1.2 Lernziele. 325

30.1.3 Ergebnis Programmlauf 325

30.1.4 Lösung. 326

30.1.5 Betrachtete Themen. 326

30.1.6 Erläuterung. 327

30.2 Aufgabe 47 - Maps. 328

30.2.1 Aufgabenstellung. 328

30.2.2 Lernziele. 328

30.2.3 Ergebnis Programmlauf 328

30.2.4 Lösung. 329

30.2.5 Betrachtete Themen. 329

30.2.6 Erläuterung. 330

30.3 Aufgabe 48 - Streams. 331

30.3.1 Aufgabenstellung. 331

30.3.2 Lernziele. 331

30.3.3 Ergebnis Programmlauf 331

30.3.4 Lösung. 332

30.3.5 Betrachtete Themen. 332

30.3.6 Erläuterung. 333

30.4 Aufgabe 49 - Lambda. 334

30.4.1 Aufgabenstellung. 334

30.4.2 Lernziele. 334

30.4.3 Ergebnis Programmlauf 334

30.4.4 Lösung. 335

30.4.5 Betrachtete Themen. 336

30.4.6 Erläuterung. 336

30.5 Aufgabe 50 - Projektmitarbeit 337

30.5.1 Aufgabenstellung. 337

30.5.2 Ergebnis Programmlauf 337

30.5.3 Lernziele. 338

30.5.4 Lösung. 338

30.5.5 Betrachtete Themen. 340

30.5.6 Erläuterung. 340

31 Exkurs - Zusammenhänge. 343

31.1 Die Grundidee. 343

31.2 Arrays. 343

31.3 Verkettete Listen. 343

31.4 ArrayList 343

31.5 LinkedList 344

31.6 Link-Arrays 344

31.7 Zusammenfassung. 345

32 Exkurs - Sammlungen (collection) 346

32.1 Warum braucht man Sammlungen?. 346

32.2 Wichtige Sammlungen in Java. 346

32.2.1 List 346

32.2.2 Set 346

32.2.3 Map. 347

32.3 Sammlungen und Arrays. 347

32.4 Zusammenhang mit obigen. 347

33 Exkurs - NoSQL?. 348

33.1 Vergleich. 348

33.2 Objekte als Werte. 348

33.3 Historischer Zusammenhang. 349

34 Themenindex. 350

35 Literatur. 354

H 354

Staud 354

U 354

 

Abkürzungsverzeichnis


Abkürzung

 

API

Application Programming Interface

HS

Hochschule

IT

Informationstechnologie/n

JDBC

Java Database Connectivity

JDK

Java Development Kit. OpenJDK

JRE

Java Runtime Environment

KI

Künstliche Intelligenz

OCL

Object Constraint Language

OID

Objektidentifizierer

RDBS

Relationale Datenbanksysteme

UML

Unified Modeling Language

VM

Virtual Machine. Laufzeitumgebung. Java-Interpreter

 

 

1 Einleitung

1.1 Konzept

Wie oben schon ausgeführt, ist dieser Text ein Begleittext zu [Staud 2026a], den man auf dieser Web-Seite in der Zeile darüber ansteuern kann und der wichtige Teile der UML 2.5 beschreibt. Dort sind einige Java-Programme eingefügt, die aufzuzeigen, wie wichtige Elemente der objektorientierten Theorie in einer objektorientierten Programmiersprache umgesetzt sind. Es geht also um die Umsetzung ausgewählter Konstrukte und Konzepte der objektorientierten Theorie in einer objektorientierten Sprache (dafür wurde Java gewählt) und somit um die Frage:

Begleittext zu [Staud 2026a] (oben)

Wie sind die objektorientierten Konzepte und Konstrukte der UML 2.5 in Java umgesetzt?

In diesem Begleittext werden genau die Elemente von Java erläutert, die für das Verständnis obiger Programme notwendig sind. So sollte z.B. jemand, der objektorientierte oder prozedurale Programmiererfahrung hat, die Java-Programme in [OO2025] nachvollziehen können.

Nicht verwirren lassen: In objektorientierten Programmen (im Gegensatz zu objektorientierten Datenbanken) wird unter Objekten auch die Zusammenstellung von Programmfragmenten für spezifische Aufgaben verstanden. Hier z.B. bei der Klasse File und vielen weiteren (z.B. den sog. Stromklassen, vgl. unten). Es sollten also inhaltlich bedeutsame Objektklassen (Angestellte, ihre Attribute und die mit ihnen zusammenhängenden Methoden) und programmtechnisch notwendige Klassen unterschieden werden. In Java ist dies besonders ausgeprägt. Die gesamte Anwendung besteht aus Klassen, d.h. es müssen auch Vorgänge wie Dateien einrichten/Löschen, in Dateien schreib en, aus Dateien lesen in das Klassen- und Objektkonzept abgebildet werden. Vgl. die Beispiele unten, zu letzterem v.a. die in hsdaten.

Inhaltlich vs. programm-technisch

Dies gilt auch für das Hauptprogramm (main), das aus objektorientierter Sicht eine statische Funktion ist. Auch dieses wird, vgl. die Beispiele unten, in eine eigene Klasse gepackt.

Main

Keine Einführung in Java

Dieser Text ist keine Einführung in Java. Dafür gibt es umfassende Literatur, siehe z.B. die Literaturliste. Eine umfassende Darstellung, auch mit kleinen Beispielen, findet sich in der API-Dokumentation von Oracle:

https://docs.oracle. com/en/java/java se/21/docs/api/java.base/java/util/regex/Pattern.html

Sollte die Suchmaschine mit der langen Adresse nicht klarkommen, einfach mit

https://docs.oracle.com/en/

beginnen und im Oracle-Menü weitergehen.

1.2 Bibliotheken

Wie in den Beispielen zu sehen ist, sind in Java viele der benötigten Programmleistungen in Bibliotheken ausgelagert, die bei Bedarf importiert werden können. Zum Beispiel so:

import javax.swing.JOptionPane;

Die wichtigste ist die Java-Standardbibliothek. Ihre einzelnen Pakete (vgl. unten) sind hierarchisch in einer Baumstruktur angeordnet. Die Wurzel ist java, bzw. javax. Danach folgen z.B.:

  • java.awt (Abstract Windowing Toolkit). Klassen zur Arbeit mit grafischen Oberflächen.
  • java.math. Paket für mathematische Methoden.
  • java.util. "Bietet Typen für Datenstrukturen, Raum und Zeit sowie für Teile der Internationalisierung sowie für Zufallszahlen." [Ullenboom 2024, S. 1579]. In den Beispielen hier wird die Klasse java.util.Scanner sehr oft benötigt.
  • java.sql und javax.sql. Zur Arbeit mit relationalen Datenbanksystemen.
  • java.io und java.nio. "Möglichkeiten zur Ein- und Ausgabe. Dateien werden als Objekte repräsentiert. Datenströme erlauben den sequentiellen Zugriff auf Dateiinhalte." [Ullenboom 2024, S. 1578].
  • java.lang. Dieses Paket ist automatisch eingebunden. Enthält u.a. die String-Klasse, die oft benötigt wird.
  • javax.swing. Stellt Komponenten für grafische Oberflächen zur Verfügung. Hier wird javax.swing.JoptionPane oft genutzt.

1.3 Packages

Für die Programmerstellung und -ausführung werden neben dem Java-Compiler eine Vielzahl von Programmbibliotheken benötigt. Diese sind in Paketen organisiert. Pakete bestehen aus inhaltlich zusammenhängenden Klassen für bestimmte Aufgaben, z.B. Bildschirmeingabe, Dateibearbeitung, mathematische Aufgaben. Diese Pakete und/oder Klassen werden mittels Import-Befehlen zum jeweiligen Programm dazugeladen. Vgl. die Programme unten.

Auch die Quellcodedateien eines Projekts werden in Packages zusammengefasst. Entweder durch einen Befehl in der ersten Zeile des Quellprogramms oder, falls dies unterbleibt, in einem default package. Vgl. die Programme unten.

1.4 Formatierung und Schreibweise

Im Text und in den Grafiken wird jeweils ein bestimmtes Schriftformat für Anwendungsbereiche, Objektorientierte Modelle, Klassen, Objekte, Attribute und Methoden verwendet. Außerdem schreibt die UML bei einigen dieser Bezeichner einen bestimmten Aufbau des Wortes bzgl. Kleinschreibung, Großschreibung und Unterstreichung vor. Dieser wird hier schon mal vorgestellt, bei der Einführung des jeweiligen Theorieelements dann erläutert.

Hier die für diesen Text gewählte Formatierung:

  • Anwendungsbereiche: Unternehmen (Arial, fett, 11)
  • Objektorientierte Modelle: Personalwesen (Arial, fett, Großbuchstaben, 9).
  • Klassen: Angestellte (Arial, fett, 9)
  • Objekte: Maier:Angestellte (Courier 10, unterstrichen)
  • Attribute: datumEinstellung (Arial)
  • Methoden/Operationen: einstellen() (Arial 9)
  • Programme und Programmfragmente: class Stud {int matrNr; String name; String vName;... (Courier, 10)

Kommentare in den Programmen

Der doppelte Schrägstrich (//) markiert in Java-Programmen einen Kommentar. Diese können bei der Übernahme der Programme belassen werden. Die in einem Kasten gefassten

Texte in den Programmen

wurden bei der Textbearbeitung in die Programme eingefügt und würden bei der Übernahme der Programme eine Fehlermeldung der Entwicklungsumgebung auslösen.

2 Grundlagen

In den Beispielen wird oftmals der Anwendungsbereich Hochschule gewählt. Abgekürzt mit HS. Klassen sind dann z.B. Studierende (Stud), Attribute sind matrikelNr, name, vName, usw. Siehe die Programmbeispiele unten.

2.1 Datentypen

Die Datentypen in Java können wie folgt unterschieden werden:

  • Elementare Typen: Die elementaren (primitiven) Typen sind in Java eingebaute Datentypen für Zahlen, Unicode-Zeichen und Wahrheitswerte.
  • Referenztypen: Mit diesen lassen sich Objektverweise etwa auf Zeichenketten, Datenstrukturen, usw. realisieren.

Die hier verwendeten elementaren Datentypen von Java sind die Folgenden:

Ganzzahlen

  • Byte (Länge 8 Bit)
  • Short (16 Bit)
  • Int (32 Bit)
  • Long ( 64 Bit)

Fließkommazahlen

  • Float (32 Bit)
  • Double (64 Bit)

Sonstige Datentypen

  • Boolean (1 Bit)
  • Char (16 Bit)

Nicht-elementare Datentypen

Strings

Sie spielen eine wichtige Rolle, sind aber keine elementaren Datentypen, sondern Verweise auf Objekte.

Arrays

Ein Array (auf deutsch Feld) ist ein Datentyp, der mehrere gleich definierte Werte (Attributsausprägungen, Objekte, ...) zu einer Einheit zusammenfasst. Z.B. mit elementaren Datentypen, aber auch mit komplexeren. In Projekt HS3 wird ein Array genutzt, um zufällig generierte Objekte zu verwalten. Soll das Array mehrdimensional sein, wird an jeder Position des Array ein weiteres Array gespeichert.

2.2 Attribute

Instanzenattribute

In der objektorientierten Theorie sind Attribute meist mit Objekten (Instanzen) verknüpft. Da bedeutet z.B.

s100.name

s100.vName

s100.anzSem

s100.gebTag

dass es eine Klasse Stud (Studierende) gibt mit einer Studierenden (einem Objekt, einer Instanz) s100 und den Attributen name, vName, anzSem (Anzahl Semester) sowie gebTag (Geburtstag) gibt. Der jeweilige Datentyp wird in der Klassendefinition festgelegt. Vgl. Projekt HS5.

Natürlich kann solch ein Instanzenattribut auch im Programm beschrieben werden. Die Variable ii soll hier die Semesteranzahl darstellen.

S100.anzSem = ii;

Die Bedeutung ist wie folgt: das Attribut anzSem (Anzahl Semester) von Objekt s100 erhält den Wert ii.

Klassenattribute

Wie in der UML vorgesehen, kennt Java auch Klassenattribute. Deren Attributsausprägungen beziehen sich auf die jeweilige Klasse als Ganze. In den Beispielen unten wird das Klassenattribut anzSem (Anzahl Semeseter)sehr oft eingesetzt.

static int anzStud = 0;

Obiges ist dann die Deklaration eines Klassenattributs mit Anfangswert.

Variablen

Der Umgang mit Variablen entspricht weitgehend dem der prozeduralen Programmiersprachen. Die folgenden Situationen kommen in den Beispielprogrammen vor:

int i = 0; //Integer-Variable

short ii = (short) i; //Umwandlung integer in short integer

boolean fertig = false; //boolesche Variable

2.3 Logische Operatoren

In vielen Programmen müssen Bedingungen überprüft werden. Das Ergebnis einer Bedingung ist immer einer der beiden Wahrheitswerte

  • true (wahr)
  • false (falsch)

Zur Verknüpfung oder Veränderung solcher Bedingungen verwendet Java logische Operatoren. Sie werden beispielsweise in if-Anweisungen oder while-Schleifen eingesetzt. Die wichtigsten logischen Operatoren sind:

Logische Operatoren

Operator

Bedeutung

Beispiel

!

Nicht (Negation)

!fertig

amp;amp;

Und

alter >= 18 amp;amp; alter <= 65

||

Oder

note == 1 || note == 2

^

Exklusives Oder (XOR)

a ^ b

 

Nicht (!)

Der Operator Nicht kehrt den Wahrheitswert um.

boolean fertig = false;

if (!fertig) {

  System.out.println("Programm läuft weiter.");

}

Da fertig den Wert false besitzt, wird aus !fertig der Wert true.

Und

Der Operator Und liefert nur dann true, wenn beide Bedingungen erfüllt sind.

if (alter >= 18 amp;amp; alter <= 65) {

  System.out.println("Alter liegt im zulässigen Bereich.");

}

Die Bedingung ist also nur wahr, wenn beide Vergleiche gleichzeitig zutreffen.

Oder (||)

Der Operator Oder liefert true, wenn mindestens eine der Bedingungen erfüllt ist.

if (note == 1 || note == 2) {

  System.out.println("Sehr gutes oder gutes Ergebnis.");

}

Hier genügt es, wenn eine der beiden Bedingungen wahr ist.

Exklusives Oder (^)

Der Operator XOR (Exclusive OR) liefert true, wenn genau eine der beiden Bedingungen wahr ist.

boolean a = true;

boolean b = false;

System.out.println(a ^ b);

führt zu folgender Ausgabe:

true

Sind dagegen beide Bedingungen wahr oder beide falsch, liefert ^ den Wert false. Insgesamt gelten folgende Wahrheitswerte:

Wahrheitswerte

A

B

A amp;amp; B

A || B

A ^ B

false

false

false

false

false

false

true

false

true

true

true

false

false

true

true

true

true

true

true

false

2.4 Referenzen

In einem Text zur Objektorientierung mit Java ist oftmals von Referenzen die Rede. Deshalb soll der Begriff hier geklärt werden. Grundsätzlich gilt:

Eine Referenz ist immer ein Verweis auf ein Objekt.

Der einfachste Fall

Beispiel mit der Klasse Stud (Studierende), die im Text oftmals verwendet wird:

Stud s1 = new Stud();

Hier ist

Stud s1;

eine Referenzvariable. Sie enthält einen Verweis auf ein Objekt der Klasse Stud.

Referenzen bei Vererbung

Nehmen wir Vererbung hinzu, wieder mit einem Beispiel, das hier im Text öfters vorkommt: Superklasse Fahrzeug, Subklassen PKW und LKW. Jetzt schreibt man

Fahrzeug f;

Die Referenzvariable besitzt den Typ

Fahrzeug

Nun kann sie auf verschiedene Objekte zeigen.

f = new PKW();

oder

f = new LKW();

Die Referenz bleibt dieselbe. Nur das Objekt ändert sich. Dies funktioniert, weil ein PKW ein spezielles Fahrzeug ist, ebenso ein LKW. Deshalb darf eine Fahrzeug-Referenz auf beide zeigen.

Polymorphie

Dies führt zum Konzept der Polymorphie. Wird

f.fahren();

aufgerufen, schaut Java nicht nur auf den Typ der Referenz

Fahrzeug

sondern auf das Objekt. Zeigt

f

auf einen PKW, wird

PKW.fahren()

aufgerufen. Zeigt dieselbe Referenz später auf einen LKW, wird

LKW.fahren()

aufgerufen.

Somit gilt: Bei der Polymorphie unterscheidet man zwischen dem Typ der Referenzvariablen und der Klasse des Objekts. Der Typ der Referenzvariablen wird bereits beim Übersetzen des Programms festgelegt. Die Klasse des Objekts kann sich während des Programmlaufs ändern. Welche überschriebene Methode tatsächlich ausgeführt wird, richtet sich nach der Klasse des Objekts.

Referenztyp und Objekttyp

Dies führt zur Unterscheidung von Referenz- und Objekttyp. Bei

Fahrzeug f = new PKW();

gibt es zwei Typen. Den Referenztyp

Fahrzeug

und den Objekttyp

PKW

Diese beiden müssen nicht identisch sein.

Definition

Damit kann der Begriff Referenz wie folgt definiert werden:

Eine Referenz ist eine Variable, die nicht das Objekt selbst enthält, sondern einen Verweis auf ein Objekt. Mehrere Referenzen können auf dasselbe Objekt zeigen. Bei Vererbung kann eine Referenz der Oberklasse auch auf Objekte ihrer Unterklassen verweisen.

2.5 Der Begriff "Typ" in Java

In diesem Text ist oft von Typ als objektorientiertem Konstrukt die Rede. Deshalb hier eine Klärung.

Ganz allgemein bedeutet Typ in Java folgendes: Ein Typ legt fest, welche Werte eine Variable annehmen darf und welche Operationen auf diesen Werten zulässig sind.

Beispiele:

int alter;

double gehalt;

String name;

Hier sind

  • int
  • double
  • String

jeweils Typen. Sie bestimmen,

  • welche Werte gespeichert werden können,
  • welche Methoden oder Operationen verwendet werden dürfen.

Das ist sozusagen der einfache Fall, der sich auch in der andernorts üblichen Bezeichnung Datentyp äußert. Interessanter ist der folgende Fall.

Typen bei Objekten

Auch Klassen sind Typen. Betrachten wir:

Stud s;

Hier ist Stud der Typ der Variablen s. Das bedeutet, s darf nur auf Objekte der Klasse Stud (oder einer Unterklasse von Stud) verweisen.

Typen bei Referenzvariablen

In obigem Abschnitt liegt die Referenzvariable Fahrzeug f vor. Dabei ist Fahrzeug der Typ der Referenzvariablen. Der Compiler weiß dadurch:

  • f darf auf Fahrzeuge zeigen,
  • also auch auf PKW oder LKW,
  • und es dürfen nur Methoden aufgerufen werden, die in Fahrzeug bekannt sind.

Nun wird geschrieben: f = new PKW(); Damit gilt: Der Typ der Referenzvariablen Fahrzeug und die Klasse des Objekts von PKWsind zwei verschiedene Dinge.

Definition

Damit kann man den Begriff Typ in Java wie folgt definieren:

3 Konsolenarbeit

3.1 Ausgeben auf Konsole

Die Ausgabe von Daten auf der Konsole erfolgt mit dem Befehl System.out.print in seinen Varianten, z.B.:

System.out.print("Matrikelnummer: "); //ohne Zeilenvorschub

System.out.println(); //mit Zeilenvorschub

System.out.printf("MNr: " + mNr);

Printf bedeutet: als erstes wird ein String verlangt, dann können beliebig viele weitere Übergabewerte folgen. Vgl. [Ullenboom 2024, S. 471].

Die Ausgabe von Objektdaten auf der Konsole erfolgt entsprechend:

System.out.println(stud.matrikelNr);

System.out.println(stud.vName + " " + stud.name);

System.out.println(stud.plz + " " + stud.ort);

System.out.println(stud.strasse + " " + stud.hausnummer);

Oben: Das Objekt stud mit einigen seiner Attribute. Vgl. zur Klasse Stud die Programme unten, z.B. HS1.

3.2 Lesen von der Konsole

Mit java.util.scanner

Für das Lesen von der Konsole (z.B. Benutzerangaben) gibt es unterschiedliche Varianten. Die einfachste ist die mit der Klasse java.util.Scanner. Eine einfache zeilenweise Konsoleneingabe mit Einlesen der Eingabe in eine Variable kann mit ...

java.util.Scanner(System.in)

realisiert werden. Der Quellcode sieht dann z.B. so aus (vgl. HS5):

...

System.out.println("Objekt eingeben (j/n): ");

String s = new java.util.Scanner(System.in).nextLine();

switch(s) {

  case "j":

    System.out.println("Eingabe:");

    System.out.print("Matrikelnummer: ");

    int mNr = new java.util.Scanner(System.in).nextInt();

...

Auf dem Bildschirm ergibt sich das mit Windows 11 wie folgt:

Weitere Beispiele

String e1 = new java.util.Scanner(System.in).nextLine();//String einlesen

int int i = new java.util.Scanner(System.in).nextInt();

double dd = new java.util.Scanner(System.in).nextDouble();//Datentyp double einlesen

Zu beachten ist die zum Datentyp passende Methodenbezeichnung.

Mit dem Swing-Paket

Das Swing-Paket erlaubt grafisch gestaltete Eingabefenster. Im Quellcode muss folgendes eingegeben werden:

import javax.swing.JOptionPane;

...

String eingabe;

eingabe = JOptionPane.showInputDialog("Ihre Wahl");

Dann sieht das Eingabefenster so aus:

Varianten:

eingabe = JOptionPane.showInputDialog("Welcher Student (0 / 1)?");

int nr = Integer.parseInt(eingabe);

Dabei liefert parseInt die Umwandlung der Stringvariablen in eine Integervariable.

Anmerkung

Diese Option steht nur zur Verfügung, falls in Eclipse das module-info.java eingerichtet ist:

In der Datei steht, bei diesem Beispiel und im einfachsten Fall:

/**

*

*/

/**

*

*/

module HS1 {

  requires java.desktop;

}

4 Klassen, Objekte, Methoden

Hier nun ein Blick auf das Einrichten von Klassen, das Instanziieren von Objekten und den Einsatz von Methoden.

4.1 Klassen einrichten

4.1.1 Grundlagen

Mit Angabe der Attribute (Instanzvariablen) und dem Standardkonstruktor, einfachste Form (vgl. HS2, Stud.java):

public class Studierende {

  public String matrikelNr;

  public String name;

  public String vName; //Vorname

  public String plz;

  public String ort;

  public String strasse;

  public int hausnummer;

}

Mit Instanzattributen, einem Klassenattribut, einem ausgewiesenen Konstruktor und einer Methode (vgl. HS1, Stud.java):

public class Stud { //Beginn der Klassendefinition

  public int mNr; //Matrikelnummer

  public String name;

  public String vName; //Vorname

  public int anzSem; //Erreichtes Semester

  public String gebTag; //Geburtstag

  

Klassenattribut/statisches Attribut mit Anfangswert. Static bedeutet: Es ist ein Klassenattribut. D.h. seine Attributsausprägungen beziehen sich auf die Klasse als Ganzes, nicht auf einzelne Objekte. Programmiertechnisch bedeutet dies, dass seine Werte ohne Objektaufruf bearbeitet und aufgerufen werden können.

  static int anzStud = 0

  

Konstruktor, bei dem das Klassenatttribut für die Anzahl der Objekte der Klasse hochgezählt wird.

  public Stud () {

    anzStud++;

  }

Instanzmethode zum Ausgeben der Daten eines Objekts:

void ausgeben() {

  System.out.println("Matrikelnummer: " + mNr);

  System.out.println("Name: " + name);

  System.out.println("Vorname:" + vName);

  System.out.println("Erreichtes Semester: " + anzSem);

  System.out.println("Geburtstag: " + gebTag);

  }

}

4.1.2 Sichtbarkeit

Wie in der UML vorgesehen, kann auch in Java der Zugriff auf die Attribute und Methoden einer Klasse durch Sichtbarkeitsmodifizierer kontrolliert werden:

Modifier

  • Public (öffentliches Attribut, öffentliche Variable, öffentliche Methode) bedeutet, auf die Klasse kann vom Paket aus, von abgeleiteten Klassen und von außerhalb zugegriffen werden.
  • Protected (internes Attribut, interne Variable, interne Methode) bedeutet, es kann vom Paket aus und von abgeleiteten Klassen zugegriffen werden.
  • Paketsichtbar (package-private) ist eingestellt, falls kein Modifizierer angegeben wird. Diese Sichtbarket bedeutet, dass nur von anderen Klassen im gleichen Paket auf die Klasse zugegriffen werden kann.
  • Private (privates Attribut, private Variable, private Methode) bedeutet, es kann nur innerhalb der Klasse zugegriffen werden.

Damit ist auch ein zentrales Anliegen der objektorientierten Theorie, die Möglichkeit der Methoden- und Datenkapselung, erfüllt. Daten, die auf Attributen/Variablen beruhen, und Methoden sollten nicht unberechtigt geändert bzw. aufgerufen werden können. Die Modifier Protected und Private erlauben dies in adäquater abgestufter Form.

Kapseln

Das Ganze wird noch verfeinert durch die Getter- und Setter-Methoden, die sozusagen trotz Kapselung den Zugriff über definierte Methoden erlauben:

Getter, Setter

  • Eine Getter-Methode erlaubt trotz Zugriffsbeschränkungen den Zugriff auf die aktuelle Attributsausprägung eines Objekts.
  • Eine Setter-Methode erlaubt trotz Zugriffsbeschränkungen das Beschreiben einer Attributsausprägung eines Objekts.

Beidesmal über Methoden, die bei der Klasse definiert sein müssen. Betrachten wir beispielhaft eine Klasse Stud (Studierende), wie sie unten in den Programmen sehr häufig vorkommt:

Vgl. auch Projekt HS8

public class Stud {

  public String mNr; //Matrikelnummer

  private String name;

...

Hier soll der Namen mit private gekapselt sein. Bei der Klasse werden dann die Getter- und Setter-Methoden in einfachster Form so hinterlegt:

//Getter

public String getName(){

  return name;

}

//Setter

public void setName(String name) {

  this.name = name;

}

Dann kann im Hauptprogramm (also im selben Paket) der Name mit den Methoden getName() bzw. setName() gelesen oder überschrieben werden. Vgl. Projekt HS8.

Pakete: Zusammenfassung der Klassen eines Projekts. Vgl. die Programme unten. Das Schlüsselwort ist package, die Deklaration erfolgt in der ersten Zeile des Programms, z.B. mit package hochschule.

4.2 Objekte einrichten - Konstruktoren

Hier einige Beispiele für Konstruktoren aus den folgenden Programmen.

(1) Der Standardkonstruktor (vgl. HS4), der automatisch aufgerufen wird:

Noten n1 = new Noten();

n1 ist die Bezeichnung des neuen Objekts.

(2) Ein Konstruktor mit einer Klassenvariablen, die bei jeder Objekterzeugung hochgezählt wird (anzStud = Anzahl Studierende):

public Stud () {

  anzStud++;

}

(3) Der Standardkonstruktor mit anschließendem Beschreiben des Objekts:

public class HS2 {

  public static void main(String[] args) {

    Stud s0 = new Stud(); //Aufruf Konstruktor

    s0.mNr = 12345;

    s0.name = "Sauer";

    s0.vName = "Otto";

    s0.anzSem = 5;

    s0.gebTag = "21.09.2002";

    

(4) Instanziierung mit Beschreiben des Objekts beim Aufruf des Konstruktors (vgl. HS4, HP):

Aufruf im HP:

Stud s0 = new Stud("1111", "Maier", "Klara", 5, "21.09.2002");

Konstruktor in Klasse Stud:

public Stud (String mNr, String name, String vName, int anzSem, String gebTag) {

  this.mNr =mNr;

  this.name =name;

  this.vName = vName;

  this.anzSem = anzSem;

  this.gebTag = gebTag;

  anzStud++;

}

Zu beachten ist das keyword this, das die Zuordnung der Parameterwerte auf die Attribute des Objektes realisiert.

(5) Verschachtelte Konstruktoren:

stud1 = new BufferedReader (new FileReader("C:/Meine Dateien/" + dateiBez + ".txt"));

Der eine Konstruktor ist Parameter im anderen. Näheres dazu in Abschnitt 6.1.

Abschließend noch das Einlesen von Objektdaten von der Konsole:

eingabe = JOptionPane.showInputDialog("Matrikelnummer:");

stud.matrikelNr = eingabe;

eingabe = JOptionPane.showInputDialog("Name:");

stud.name = eingabe;

eingabe = JOptionPane.showInputDialog("Vorname:");

stud.vName = eingabe;

eingabe = JOptionPane.showInputDialog("Postleitzahl:");

stud.plz = eingabe;

eingabe = JOptionPane.showInputDialog("Ort:");

stud.ort = eingabe;

eingabe = JOptionPane.showInputDialog("Straße:");

stud.strasse = eingabe;

eingabe = JOptionPane.showInputDialog("Hausnummer: ");

stud.hausnummer = Integer.parseInt(eingabe);

Klasse Stud mit Objekt stud (vgl. HS1). Mit Einlesen von Konsole.

4.3 Record-Klassen

Java erlaubt die Anlage von sog. Record-Klassen, die wohl von den Datensätzen (records) relationaler Datenbanken motiviert sind. Sie erlauben (für entsprechende Daten) eine schlankere Programmierung, v.a., weil sie eine schlankere Syntax aufweisen und weil einige Methoden gleich bei der Einrichtung durch den Konstruktor angelegt werden.

Vgl. die Beispiele in den Programmen, z.B. HS6

Die Besonderheiten:

  • Record-Klassen verwalten für ihre Objekte nur Attribute (hier auch Record-Komponenten genannt).
  • Ihre Objekte sind unveränderlich.
  • Die Deklaration erfolgt mit dem Schlüsselwort record. Vgl. für die sonstige Syntax das Beispiel in Projekt HS6.
  • Ihr Konstruktor übergibt in einer Parameterliste die Attributsausprägungen.
  • Er erzeugt die Methoden equals(), toString() und hashCode(). Falls nötig, können sie verändert werden. Weitere Methoden können hinzugefügt werden.
  • Für jedes Attribut wird automatisch eine Abfragemethode erstellt.
  • Es gibt Schnittstellen, aber keine Vererbung

Die Methode equals() wird zum Vergleich von records verwendet. Sie sieht zwei records als gleich an, wennn sie vom gleichen Typ sind und falls jede Komponente denselben Wert hat. Dies leistet hier der Gleichheitsoperator nicht.

Der Zugriff auf die Instanzvariablen erfolgt über Methoden mit derselben Bezeichnung wie das jeweilige Attrbut. Gibt es also ein Attribut name, steht die Methode name() zur Verfügung.

Das Beispiel in HS6 stellt dieses Programmierkonstrukt dar.

Vgl. [Ullenboom 2024, S. 897], [Kofler 2023, S. 220f] und [Habelitz 2025, S. 387] für weitergehende Beschreibungen.

4.4 Aufruf von Methoden und Attributen

Hier wird gezeigt, wie der Aufruf von Methoden und Attributen in Java geregelt ist.

(1) Aufruf einer Instanzmethode aus dem HP heraus (HS2):

public class HS2 {

  public static void main(String[] args) {

    Stud s0 = new Stud();//Standardkonstruktor

    s0.mNr = 12345;

    s0.name = "Sauer";

    s0.vName = "Otto";

    s0.anzSem = 5;

    s0.gebTag = "21.09.2002";

    System.out.println("\n------\n");

    

    s0.ausgeben();

  

In Klasse Stud von HS1 wurde die Methode wie folgt angelegt:

public class Stud {

  .....

  //Methode zum Ausgeben der Daten eines Objekts

  void ausgeben() {

    System.out.println("Matrikelnummer: " + mNr);

    System.out.println("Name: " + name);

    System.out.println("Vorname:" + vName);

    System.out.println("Erreichtes Semester: " + anzSem);

    System.out.println("Geburtstag: " + gebTag);

  }

(2) Aufruf eines Attributs (Variablen) einer anderen Klasse (HS4):

System.out.println("\nAusgabe der Noten in Klasse HS4:");

System.out.println("Matrikelnummer: " + s1.mNr + "\nName: " + s1.name + "\nVorname: " + s1.vName + "\nFach: " + n1.fach + "\nNote: " + n1.note);

Mit Objektbezeichnung und Attributsname, verknüpft durch einen Punkt, wird die jeweilige Attributsausprägung angefordert.

Das führt zu folgender Ausgabe:

5 Prozedurales

Java ist umfassend objektorientert. Es gibt nur Klassen, auch das Hauptprogramm ("main") wird in eine Klasse gepackt. Aber auch eine solche Programmiersprache benötigt prozedurale Komponenten, wenn es um die Realisierung von Methoden geht, um die dynamischen Aspekte der Anwendung. Es sind die üblichen von C und anderen prozeduralen Sprachen bekannten Konstrukte. Deren Ausprägung in Java wird hier kurz vorgestellt.

5.1 Schleifen und Verzweigungen

Eine While-Schleife mit enthaltener Switch-Verzweigung von Projekt HS2:

boolean fertig = false;

while (!fertig) {

  System.out.println(" ");

  System.out.println("1 = Eingabe");

  System.out.println("2 = Ausgabe");

  System.out.println("3 = StudDaten wechseln");

  System.out.println("4 = Programm beenden");

  eingabe = JOptionPane.showInputDialog("Ihre Wahl");

  int auswahl = Integer.parseInt(eingabe);

Oben: Menü auf der Konsole. Lesen der Nutzereingabe.

switch(auswahl) {

Beginn der Switch-Verzweigungen, wie man es kennt mit case x und break.

  case 1:

    eingabe = JOptionPane.showInputDialog("Matrikelnummer:");

    stud.matrikelNr = eingabe;

.....

    break;

  case 2:

    System.out.println(stud.matrikelNr);

    System.out.println(stud.vName + " " + stud.name);

    System.out.println(stud.plz + " " + stud.ort);

    System.out.println(stud.strasse + " " + stud.hausnummer);

    break;

  case 3:

    eingabe = JOptionPane.showInputDialog("Welcher Student (0 oder 1)?");

    int nr = Integer.parseInt(eingabe);

    

Verzweigung mit if/else.

    if (nr == 0) {

        stud = stud0;

      }

      else if (nr ==1) {

        stud = stud1;

      } else {

        System.out.println("Eingabefehler!");

      }

      

    break;

  case 4:

    fertig = true;

  break;

  default:

    System.out.println("Eingabefehler!");

  }

}

Vgl. für weitergehende Informationen die Projektbeschreibung unten (HS2).

5.2 Das Try/Catch-Konstrukt

In Java wird ein Programmierkonstrukt angeboten, das kurz so beschrieben werden kann: Der Konstrollfluss wird unterteilt in einen Abschnitt mit dem gewünschten Ablauf ("try") und einen, der im Fehlerfall bei Laufzeitfehlern aktiv wird ("catch").

Der Try-Block kann unterschiedlich lang sein. Auch sehr kurz, z.B. für einen Einlesevorgang mit While-Schleife (hsdaten/Klasse Lesen) oder eine Arbeit mit dem Dateisystem, wie in hsdaten/Schreiben. Natürlich muss es ein zusammenhängender Abschnitt sein, so dass die Fehlermeldung im Catch-Block aussagekräftig ist.

Vgl. die Beispiele im Projekt hsdaten.

Die folgende Abbildung erläutert die Syntax am Beispiel eines Programmfragments von hsdaten/Erstellen.java. Mit try und catch werden die beiden Abschnitte getrennt. Im Try-Block findet sich ein Programmabschnitt, bei dem eine Datei in einem bestimmten Verzeichnis (hier unter Windows) eingerichtet werden soll. Der Fehler, dass die Datei bereits existiert, wird direkt im Programm abgefangen und dem Nutzer gemeldet. Eventuelle Laufzeitfehlermeldungen werden im Catch-Block ausgeführt. Dies sind erstmal vordefinierte Meldungen, die aber spezifiziert werden können. Vgl. Kapitel 7 und die dort gegebenen Hinweise.

Abbildung 5.2-1: Das Try/Catch-Konstrukt in Java.

Vgl. Kapitel 9 in [Habelitz 2025] für eine weitergehende Beschreibung. Für zahlreiche Beispiele vgl. die Projekte unten, v.a. hsdaten.

6 Textdateien bearbeiten

6.1 Einleitung

Für das Lesen aus und das Schreiben in Dateien werden (Daten-)Stromklassen benötigt, mit denen es möglich ist, "die abstrakten Methoden von Reader, Writer, InputStream und OutputStream auf Dateien abzubilden" [Ullenboom 2024, S. 1919]. Solche Stromklassen sind

(Daten-)Strom-klassen

  • FileInputStream,
  • FileReader und
  • FileOutputStream.

Die hier zur Arbeit mit Textdateien benötigten Programme, werden im Projekt hsdaten vorgestellt:

  • Erstellen.java zum Erstellen einer TXT-Datei in einem beliebigen Verzeichnis mit der Klasse File.
  • Schreiben.java für das Schreiben eines Variablentextes in eine Datei mit der Klasse FileWriter.
  • Schreiben2.java für das Schreiben in eine Datei mit den Klassen FileWriter und BufferedWriter.
  • Lesen.java für das Lesen einer TXT-datei mit der Klasse BufferedReader.
  • ErzeugLoesch.java für das Erzeugen, Beschreiben und Löschen einer TXT-Datei mit den Klassen FileWriter, BufferedWriter und einer grafischen Eingabe mit JOptionPane.showInputDialog.
  • Anhaengen.java für das Anhängen von Daten in eine TXT-Datei. Dateneingabe mit Scanner.
  • EinlesenCSV.java für das Lesen und Ausgeben einer CSV-Datei. Dateneingabe mit Scanner
  • Loeschen.java für das Löschen einer Datei. Dateneingabe mit Scanner.

6.2 Paket java.io

Die gesamte für die Arbeit mit Dateien notwendige Funktionalität ist im Paket java.io vorhanden. Davon werden hier nur die Lese- und Schreibvorgänge mit TXT-Dateien benötigt. Es geht also um Eingabeströme (Eingeben in die Datei) und Ausgabeströme (Lesen aus der Datei). Außerdem um das Einrichten und Arbeiten mit Verzeichnissen und Dateien.

Vgl. die Abbildung unten.

Die benötigten Komponenten von java.io müssen im jeweiligen Programm importiert werden. Entweder als Ganzes mit

Import java.io.*

oder über die Klassen, z.B: so:

import java.io.BufferedReader;

import java.io.FileReader;

import java.io.FileWriter;

Zu den Klassen vgl. unten.

6.3 Zugriffe mit Streams

Bei den Eingabe- und Ausgabeströmen bzgl. Textdateien wird zwischen Character Streams und Byte Streams unterschieden:

  • Character Streams bestehen aus 16-Bit-Einheiten mit Char-Zeichen, kodiert nach Unicode.
  • Byte Streams bestehen aus 8-Bit-Einheiten mit dem Datentyp Byte.

Die Basisfunktionalität wird durch abstrakte Klassen bereitgestellt. Dies sind Reader und Writer für Character Streams und InputStream sowie ExportStream für Byte Streams. Die eigentliche Arbeit leisten dann davon abgeleitete Klassen, die jeweils in ihrer Bezeichnung die der abstrakten Klasse enthalten. Zum Beispiel:

  • BufferedInputStream
  • BufferedReader
  • BufferedWriter
  • FileWriter
  • FileReader

6.4 Klasse File

Die abstrakte Klasse File aus dem Paket java.io stellt die für den Umgang mit Dateien und Verzeichnissen benötigte Funktionalität zur Verfügung. Ein Objekt dieser Klasse repräsentiert jeweils ein Verzeichnis oder eine Datei. Im Fall von Dateien enthält das Objekt Daten wie Bezeichnung, Pfad, Größe, usw., nicht aber die Inhalte der Datei.

File

Eine wichtige der Klasse File zugeordnete Methode ist createNewFile(). Vgl. [hsdaten, Erstellen.java]. Mit dem Konstruktor

Public File(String pathname)

wird ein Objekt von File im angegebenen Zugriffspfad eingerichtet.

Die davon abgeleiteten Klassen tragen auch hier in ihrer Bezeichnung die der abstrakten Klasse. Zum Beispiel:

  • FileWriter. Richtet ein Objekt ein, mit dem in Dateien geschrieben werden kann. Es stellt die Verbindung her zu einer Datei aus dem Dateisystem des jeweiligen Rechners. Wichtige Methoden dieser Klasse sind write(), close().
  • FileReader. (vgl. hsdaten/Lesen.java). Dessen "Objekte" stellen eine Verbindung zu einer Datei her, so dass ein zeilenweiser Lesezugriff ermöglicht wird.
  • BufferedReader. Liest mit seiner Methode readLine() zeilenweise aus einer vom FileReader bereitgestellten Datei. Wenn das Ende der Datei erreicht ist, liefert readLine() als Ergebnis null.
  • BufferedWriter. Liefert einen Eingabestrom, der "gepuffert" Unicode-Zeichen schreibt.

"Gepuffert" (buffered) meint hier, das der Ein- und Ausgabestrom nicht direkt ausgeführt wird. Die Daten werden also nicht zeitgleich mit der Lese- oder Schreibanweisung ausgeführt, sondern in einen Puffer geschrieben und erst transportiert, wenn der Puffer voll ist oder der Stream geschlossen wird.

Beispiele

hsdaten, Schreiben.java

String dateiBez = "stud003";

stud1 = new FileWriter("C:/Meine Dateien/" + dateiBez + ".txt");

String text = "Sinnspruch Nummer 3\n"

  + "Der Weg ist weit, der Weg ist lang,"

  + " doch uns wird - wie immer - nicht bang";

stud1.write(text);

stud1.close();

Erläuterung:

  • Im obigen Beispiel wird zuerst die Dateibezeichnung in eine String-Variable geschrieben.
  • In der zweiten Zeile wird ein Objekt der Klasse FileWriter erzeugt mit den Angaben zum Verzeichnis und zur Datei.
  • Dann wird die Stringvariable text beschrieben
  • Anschließend wird mit der Instanzmethode write() in das Objekt stud1 der Text der String-Variablen geschrieben.
  • Abschließend wird das Objekt stud1 wieder geschlossen.

Das Ergebnis (mit Notepad):

hsdaten, Schreiben2.java, EinlesenCSV.java

Hier nun ein Beispiel für den Einsatz der Klasse BufferedWriter bei einem Schreibvorgang. Zuerst wird ein FileWriter-Objekt erzeugt. Durch Verzicht auf Angabe eines Verzeichnissses, wird die Datei im Standardverzeichnis (hier, mit Windows 11: c:/eclipse/hsdaten) angelegt.

Der Schreibvorgang soll mit BufferedWriter realisiert werden (obwohl diese Datenmenge dies nicht nahelegt). Dazu wird ein BufferedWriter-Objekt buwr erzeugt, das als Input das zuvor erstellte FileWriter-Objekt fiwr hat:

FileWriter fiwr = new FileWriter("stud07.txt");

BufferedWriter buwr = new BufferedWriter(fiwr);

Danach kann dann in das Objekt (die Datei) buwr geschrieben werden. In hsdaten/schreiben2 geschieht dies mit den Methoden write() und newLine().

Das Zusammenwirken der beiden Klassen kann auch verschachtelt programmiert werden. Im folgenden Beispiel aus hsdaten/EinlesenCSV geht es um einen Lesevorgang. Gelesen wird aus der Datei mitarbeiter.csv. Die Verschachtelung erfolgt auf Basis der Konstruktoren der beiden Klassen. Der Konstruktor von BufferedReader erhält als Parameter die Instanziierung von FileReader, die als Parameter den Dateinamen über die Variable fileName hat.

String fileName = "mitarbeiter.csv";

stud1 = new BufferedReader(new FileReader(fileName ));

java.util.Formatter

Die unten auch verwendete Klasse java.util.Formatter dient (hier) dazu, die Eingaben in eine Textdatei zu formatieren. Hier ein Beispiel aus hsdaten/Anhaengen.java:

File datei = new File("C:/Meine Dateien/" + fileName + ".txt");

FileWriter writer = new FileWriter(datei, true);

Formatter formatter = new Formatter(writer)) {

Er benötigt ein Objekt der Klasse FileWriter als Parameter und stellt dann die benötigte Programmfunktionalität bereit. Die Methode format erlaubt dann die Spezifizierung:

formatter.format("%s%n", newInput);

Vgl. die weiteren Ausführungen hierzu in hsdaten/Anhaengen.java.

Die folgende Abbildung zeigt die hier verwendeten Klassen im Überblick.

Abbildung 6.4-1: Klassen für Dateibearbeitung

Da die IO-Klassen Systemressourcen benötigen, sollte man die entsprechenden Objekte schließen, wenn sie nicht mehr benötigt werden. Dies geschieht mit der Methode close().

Für weitergehende Beschreibungen vgl. die Literatur, z.B.:

  • [Ullenboom 2022, Abschnitt 20.4.4] für eine Beschreibung der Basisklase Writer und Abschnitt 20.4.6 für eine Beschreibung der Basisklasse Reader.
  • [Ratz 2018, Kapitel 17] enthält eine verständliche und fundierte Darstellung der "Ein- und Ausgabe über I/O-Streams".

7 Exceptions

Exceptions bedeutet hier Laufzeitfehler. Sie wurden in den unten gezeigten Programmen v.a. mit dem Try/catch-Konstrukt bei Zugriffen auf Dateien eingesetzt (IOExceptions). Exception ist auch eine Klasse, deren Objekte Fehlermeldungen sind. Die allgemeinste Fehlermeldung hat die Bezeichnung e. Sie kann auf den konkreten Fall zugeschnitten werden.

In den jeweiligen Programmen muss diese Klasse mit import java.io.IOException importiert werden. Dies ist unten auch geschehen, v.a. für die Arbeit mit dem Dateisystem:

  • Zum Melden des Scheiterns der Methode createNewFile() (hsdaten/Erstellen.java) in einer Try/catch-Situation.
  • Falls das Einrichten einer Datei und/oder des Schreibens in die Datei schief geht (hsdaten/Schreiben.java), ebenfalls in einer Try/catch-Situation.
  • Beim Start eines Programms mit Dateizugriffen (hsdaten/Schreiben2.java).

Vgl. für eine ausführliche Beschreibung [Habelitz 2025, Kapitel 9].

8 Projekt HS1 - Objekterzeugung

HS1 steht für das erste Projekt zum Anwendungsbereich Hochschule. Die Programme mit den Bezeichnungen HS1-HS9 und hsdaten dienen zur exemplarischen Darstellung wichtiger Theorieelemente. Dabei werden, gemäß dem Konzept dieses Textes, die Ausführungen zur Theorie gleich ergänzt mit aussagekräftigen Programmen.

Aufbau der Beschreibungen

Der Aufbau dieser Projektbeschreibungen ist immer gleich: Zuerst das Hauptprogramm (Klasse HP), dann die inhaltlichen Klassen. Viele Programme werden im Text zum einen ohne Erläuterungen angegeben, zum Kopieren und für den schnellen Überblick. Zum anderen aber auch mit Erläuterungen, für das schnelle Verstehen. Im letzten Abschnitt wird jeweils ein exemplarischer Programmlauf angegeben. In einem abschließenden Abschnitt werden jeweils die betrachteten Themen aufgelisted.

Bei diesen Beispielsprojekten erfasst die Klasse HP den Programmablauf. Dazu wird wie immer in Java ein Hauptprogramm ("main") eingerichtet:

public static void main(String[] args)

Zum Projekt HS1

Hier geht es schlicht darum, eine einfache Klasse Stud (Studierende) anzulegen, Objekte zu erzeugen und mit ihnen zu arbeiten. Die Klasse Stud beschreibt Studierende, ein weiterer Programmteil, sozusagen das Hauptprogramm (HP) beschreibt die dynamischen Aspekte, hier lediglich einige Handlungen mit den Objekten.

Die Java-Erschaffer haben festgelegt, dass alle Programmelemente in Java-Programmen als Klassen angelegt werden müssen. Der gesamte Programmcode einer Anwendung ist also in Klassen gepackt, auch wenn dies manchmal keine Klassen im eigentlichen Sinn sind, wie schon das Beispiel hier mit dem Hauptprogramm ("main") und seinem Ablauf zeigt.

Demonstriert wird in HS1:

  • Die Objekterzeugung (new Stud())
  • Der Einsatz von Konstruktoren (mehrere Varianten)
  • Das direktes Setzen von Attributen
  • Der Einsatz eines Klassenattributs (anzStud)

8.1 Klasse HP (Hauptprogramm; main)

Gleich zu Beginn wird ein Objekt erzeugt, s0 ("s" steht für Student/in). Dabei wird der Standardkonstruktorr verwendet. In der Klasse Stud ist zu sehen, dass es möglich ist, dem Standardkonstruktor ein Klassenattribut mit einer Wertezuweisung zuzuordnen.

Nacheinander werden vier unterschiedliche Konstruktoren, die in der Klasse Stud angelegt sind, eingesetzt. Java wählt jeweils den geeigneten aus. D.h.:

Eine Klasse kann verschiedene Konstruktoren besitzen.

Klasse HP mit Erläuterungen

package hochschule;

Auch das jeweilige Hauptprogramm (HP / main) muss in eine Klasse gepackt sein, vgl. die Anmerkung oben.

public class HP {

  public static void main(String[] args) {

Objekt s0 wird mit dem Standardkonstruktor erzeugt (instanziiert), das Klassenattribut anzStud (Anzahl Studierende) wird bearbeitet. Vgl. Klasse Stud.

  Stud s0 = new Stud();

  System.out.println("s0 ohne Daten, nach Instanziierung:\n");

Zur Veranschaulichung die Ausgabe der Objektdaten: Nach der Instanziierung sind die Attribute mit Default-Werten beschrieben. Das Klassenattribut anzStud hat den Wert 1. Unten wird gezeigt, dass der Werte des Klassenattributs bei jeder Instanziierung um 1 erhöht wird.

  s0.ausgeben();

Dasselbe wie oben, jetzt für das zweite Objekt s1. Das Klassenattribut hat jetzt den Wert 2.

  Stud s1 = new Stud();

  System.out.println("");

  System.out.println("s1 ohne Daten, nach Instanziierung:\n");

  s1.ausgeben();

Objekt s0 wird beschrieben. Dies ist einfach. Objekt- und Attributsbezeichnung identifizieren den konkreten Informationsträger, ansonsten Zuweisung wie bei Attributen.

  s0.mNr = 12345;

  s0.name = "Sauer";

  s0.vName = "Otto";

  s0.anzSem = 5;

  s0.gebTag = "21.09.2002";

  

  System.out.println("");

  System.out.println("s0 beschrieben:\n");

Das Objekt s0 wird mit der Instanzmethode ausgeben() aus der zum Projekt gehörenden Klasse Stud auf der Konsole ausgegeben. Innerhalb desselben Projekts genügt der Aufruf ohne Klassenangabe.
Bei jedem Aufruf der Methode wird der Wert des hochgezählten Attributs anzStud ausgegeben. Siehe Programmlauf.

  s0.ausgeben();

Auch das Objekt s1 wird beschrieben und ausgegeben.

  s1.mNr = 98765;

  s1.name = "Widder";

  s1.vName = "Andrea";

  s1.anzSem = 2;

  s1.gebTag = "2.7.2005";

System.out.println(""); ist in Java plattformunabhängig gültig. Auf allen Plattformen (Windows, Linux, macOS, usw.) wird eine Leerzeile ausgegeben.

  System.out.println("");

  System.out.println("s1 beschrieben:\n");

  s1.ausgeben();

Nun der zweite Konstruktor, siehe Klasse Stud. Hier werden alle Attribute in der Parameterliste beschrieben.

  Stud s2 = new Stud(1111, "Maier", "Klara", 5, "21.09.2002");

  System.out.println("\ns2 mit Daten vom Konstruktoraufruf:\n");

  s2.ausgeben();

Der dritte Konstruktor, siehe Klasse Stud, erhält nur einen Teil der Attributsausprägungen über die Parameterliste: mNr, name, vName. Für die übrigen nimmt er die Standardwerte null bzw. 0.

  Stud s3 = new Stud(1111, "Maier", "Klara");

  System.out.println("\ns3 mit teilweisen Daten vom Konstruktoraufruf:\n");

  s3.ausgeben();

Java entscheidet aufgrund des Konstruktoraufrufs, hier auf Basis der Parameterliste, welcher der Konstruktoren aus der Klasse Stud verwendet wird.

    }

}

Klasse HP ohne Erläuterungen

package hochschule;

public class HP {

  public static void main(String[] args) {

    Stud s0 = new Stud(); //Objekt s0, Standardkonstruktor

    

    System.out.println("s0 ohne Daten, nach Instanziierung:\n");

    s0.ausgeben();

    

    Stud s1 = new Stud();

    System.out.println("");

    System.out.println("s1 ohne Daten, nach Instanziierung:\n");

    s1.ausgeben();

    

    s0.mNr = 12345;

    s0.name = "Sauer";

    s0.vName = "Otto";

    s0.anzSem = 5;

    s0.gebTag = "21.09.2002";

    System.out.println("");

    System.out.println("s0 beschrieben:\n");

    s0.ausgeben();

    

    s1.mNr = 98765;

    s1.name = "Widder";

    s1.vName = "Andrea";

    s1.anzSem = 2;

    s1.gebTag = "2.7.2005";

    System.out.println("");

    System.out.println("s1 beschrieben:\n");

    s1.ausgeben();

    

    Stud s2 = new Stud(1111, "Maier", "Klara", 5, "21.09.2002");

    System.out.println("\ns2 mit Daten vom
Konstruktoraufruf:\n");

    s2.ausgeben();

    

    Stud s3 = new Stud(1111, "Maier", "Klara");

    System.out.println("\ns3 mit teilweisen Daten

      vom Konstruktoraufruf:\n");

    s3.ausgeben();

  }

}

8.2 Klasse Stud

Klasse Stud mit Erläuterungen

package hochschule;

Beginn der Klassendefinition

public class Stud {

Inhaltliche Attribute (Instanzattribute) zur Beschreibung der Objekte (Instanzen).

  public int mNr; //Matrikelnummer

  public String name;

  public String vName; //Vorname

  public int anzSem; //Erreichtes Semester

  public String gebTag; //Geburtstag

Definition eines Klassenattributs (statisches Attribut) mit Anfangswert. Static fixiert die Unveränderlichkeit, int legt den Datentyp fest und anzStud = 0 einen Anfangswert.

  static int anzStud = 0;

Aufruf Konstruktor 1. Der Standardkonstruktor trägt Standardwerte in die Attribute des Objekts ein (o, null). Ergänzt hier durch den Wert des Klassenattributs anzStud.

  public Stud () {

    anzStud++;

  }

Aufruf Konstruktor 2, siehe Klasse Stud. Hier werden alle Attribute in der Parameterliste beschrieben

  public Stud (int mNr, String name, String vName, int anzSem, String gebTag) {

    this.mNr =mNr;

    this.name =name;

    this.vName = vName;

    this.anzSem = anzSem;

    this.gebTag = gebTag;

    anzStud++;

  }

Aufruf Konstruktor 3, siehe Klasse Stud. Dieser legt nur einen Teil der Attributsausprägungen in der Parameterliste fest: mNr, name, vName. Für die übrigen nimmt er die Standardwerte false, null oder 0.

  public Stud (int mNr, String name, String vName) {

    this.mNr =mNr;

    this.name =name;

    this.vName = vName;

    anzStud++;

  }

Instanzmethode zum Ausgeben der Daten eines Objekts. Das Klassenattribut anzStud gibt jeweils an, wieviele Objekte angelegt sind. Vgl. Programmlauf.

  void ausgeben() {

    System.out.println("Matrikelnummer: " + mNr);

    System.out.println("Name: " + name);

    System.out.println("Vorname:" + vName);

    System.out.println("Erreichtes Semester: " + anzSem);

    System.out.println("Geburtstag: " + gebTag);

    System.out.println("Anzahl Studierende: " + anzStud);

    }

  }

}

}

Klasse Stud ohne Erläuterungen

package hochschule;

public class Stud { //Beginn der Klassendefinition

  public int mNr; //Matrikelnummer

  public String name;

  public String vName; //Vorname

  public int anzSem; //Erreichtes Semester

  public String gebTag; //Geburtstag

  static int anzStud = 0;

  public Stud () {

    anzStud++; //Zähler für die Objekte der Klasse

  }

  public Stud (int mNr, String name, String vName, int anzSem, String gebTag) {

    this.mNr =mNr;

    this.name =name;

    this.vName = vName;

    this.anzSem = anzSem;

    this.gebTag = gebTag;

    anzStud++;

  }

  public Stud (int mNr, String name, String vName) {

    this.mNr =mNr;

    this.name =name;

    this.vName = vName;

    anzStud++;

  }

  void ausgeben() {

    System.out.println("Matrikelnummer: " + mNr);

    System.out.println("Name: " + name);

    System.out.println("Vorname:" + vName);

    System.out.println("Erreichtes Semester: " + anzSem);

    System.out.println("Geburtstag: " + gebTag);

    System.out.println("Anzahl Studierende: " + anzStud);

    }

}

8.3 Ergebnis Programmlauf

s0 ohne Daten, nach Instanziierung:

Matrikelnummer: 0

Name: null

Vorname:null

Erreichtes Semester: 0

Geburtstag: null

Anzahl Studierende: 1

s1 ohne Daten, nach Instanziierung:

Matrikelnummer: 0

Name: null

Vorname:null

Erreichtes Semester: 0

Geburtstag: null

Anzahl Studierende: 2

s0 beschrieben:

Matrikelnummer: 12345

Name: Sauer

Vorname:Otto

Erreichtes Semester: 5

Geburtstag: 21.09.2002

Anzahl Studierende: 2

s1 beschrieben:

Matrikelnummer: 98765

Name: Widder

Vorname:Andrea

Erreichtes Semester: 2

Geburtstag: 2.7.2005

Anzahl Studierende: 2

s2 mit Daten vom Konstruktoraufruf:

Matrikelnummer: 1111

Name: Maier

Vorname:Klara

Erreichtes Semester: 5

Geburtstag: 21.09.2002

Anzahl Studierende: 3

s3 mit teilweisen Daten vom Konstruktoraufruf:

Matrikelnummer: 1111

Name: Maier

Vorname:Klara

Erreichtes Semester: 0

Geburtstag: null

Anzahl Studierende: 4

8.4 Betrachtete Themen

  • Attribut
  • Datentypen
  • import
  • Instanziierung
  • Klasse
  • main()
  • Objekt
  • package x
  • Referenzvariable

9 Projekt HS2 - Objekte und Referenzen

Hier werden zwei Objekte (stud0 und stud1) eingerichtet und bearbeitet. Es soll deutlich werden, dass bei einer Vorgehensweise wie hier ein Objekt das aktive ist. Um dies zu vereinfachen, werden gleich zwei Objekte angelegt, mit denen dies geprüft werden kann. Es gibt ein Konsolenmenü ...

... mit einer grafischen Eingabe per JOptionPane.showInputDialog (siehe Programmlauf).

Da im Programm schon zwei Objekte angelegt sind, kann der Punkt 3 gleich ausprobiert werden. Dazu wird zuerst 2 angefordert, was zur Anzeige des aktiven Objekts führt. Dann wird mit 3 das Objekt gewechselt und wiederum mit 2 das jetzt aktive Objekt angezeigt.

Mit 1 können neue Objekte eingegeben werden, die dann jeweils das aktive sind. Wie zu sehen ist, werden immer nur zwei Objekte bereitgehalten.

9.1 Klasse HP

Klasse HP mit Erläuterungen

package hochschule;

Anmelden der Klasse javax.swing für die grafische Eingabe.

import javax.swing.JOptionPane;

public class HP {

  public static void main(String[] args) {

    String eingabe;

Erzeugen zweier Objekte mit dem Standardkonstruktor.

    Stud stud0 = new Stud();

    Stud stud1 = new Stud();

Weiteres Objekt, mit Referenzcharakter. Zeigt aktuell auf das "aktive Objekt".

    Stud stud = stud0;

Daten zum Testen. Initialisierung von stud0.

    stud0.matrikelNr = "12345";

    stud0.name = "Erster";

    stud0.vName= "Paul";

    stud0.plz = "7878";

    stud0.ort="Weingarten";

    stud0.strasse="Hinterm Berg";

    stud0.hausnummer=99;

Initialisierung von stud1.

    stud1.matrikelNr = "67890";

    stud1.name = "Zweite";

    stud1.vName= "Viola";

    stud1.plz = "8989";

    stud1.ort="Ravensburg";

    stud1.strasse="Hauptstr.";

    stud1.hausnummer=88;

Damit nicht gleich zum Ausprobieren Daten eingegeben werden müssen, werden stud0 und stud1 mit Daten beschrieben. So ist es möglich, gleich nach dem Start die Ausgabe mit dem aktiven Objekt anzufordern (es kommt stud0), dann mit Punkt 3 das aktive Objekt zu wechseln (auf 1) und dieses wieder auszugeben. Das ist dann stud1.

    boolean fertig = false;

Beginn einer While-Schleife. Ende siehe unten.

  while (!fertig) {

    System.out.println("\nEingabe von Daten \nin die Studierendendatei");

Menüauswahl auf Konsole. Anzeige siehe unten.

    System.out.println("1 = Eingabe");

    System.out.println("2 = Ausgabe");

    System.out.println("3 = Aktives Objekt wechseln");

    System.out.println("4 = Programm beenden");

Eingabe über Dialogfenster in die String-Variable Eingabe.

    eingabe = JOptionPane.showInputDialog("Ihre Wahl");

Die String-Variable eingabe wird in einen Integer-Wert gewandelt ("parseInt") und in die Integer-Variable auswahl geschrieben.

    int auswahl = Integer.parseInt(eingabe);

    System.out.println("\nEingegeben wurde: " + auswahl + "\n");

Verzweigung mit switch auf Basis der Nutzereingaben, die in Integerwerte gewandeltl wurden.

  switch(auswahl) {

    case 1:

Studierendendaten eingeben. Bespielt wird Objekt stud. Es erfolgt jeweils eine grafische Anzeige der Eingabeaufforderung (vgl. unten) und das Übertragen der Nutzereingabe in die String-Variable eingabe. Diese wird in das entsprechende Objektattribut geschrieben. Für die Hausnummer wurde die Umwandlung des Strings in eine Integerzahl vorgenommen (mit parseInt).

      stud.matrikelNr = JOptionPane.showInputDialog("Matrikelnummer:");

      stud.name = JOptionPane.showInputDialog("Name:");

      stud.vName = JOptionPane.showInputDialog("Vorname:");

      stud.plz = JOptionPane.showInputDialog("PLZ:");

      stud.ort = JOptionPane.showInputDialog("Ort:");

      stud.strasse = JOptionPane.showInputDialog("Straße:");

      stud.hausnummer = Integer.parseInt(

      JOptionPane.showInputDialog("Hausnummer:")

      );

break wird für jede Case-Variante verlangt. Damit wird die Switch-Verzweigung verlassen.

    break;

      case 2:

Diese Wahl führt zur Ausgabe der Studierendendaten des jeweils "aktiven" Objekts.

        System.out.println(stud.matrikelNr);

        System.out.println(stud.vName + " " + stud.name);

        System.out.println(stud.plz + " " + stud.ort);

        System.out.println(stud.strasse + " " + stud.hausnummer);

        break;

      case 3:

Aktives Objekt wechseln

        int nr = Integer.parseInt(

        JOptionPane.showInputDialog("Welcher Student (0 oder 1)?")

        );

        if (nr == 0) {

        stud = stud0; // Referenz zeigt jetzt auf stud0

        System.out.println("Jetzt ist Student 0 aktiv.");

        } else if (nr == 1) {

        stud = stud1; // Referenz zeigt jetzt auf stud1

        System.out.println("Jetzt ist Student 1 aktiv.");

        } else {

        System.out.println("Fehler: Bitte 0 oder 1 eingeben!");

        }

        break;

      case 4:

Programm beenden

        fertig = true;

        break;

      default:

Fehler bei falscher Menüeingabe

        System.out.println("Ungültige Eingabe!");

      }

    }

  }

}

Klasse HP ohne Erläuterungen

package hochschule;

import javax.swing.JOptionPane;

public class HP {

  public static void main(String[] args) {

  String eingabe;

  Stud stud0 = new Stud();

  Stud stud1 = new Stud();

  Stud stud = stud0;

  stud0.matrikelNr = "12345";

  stud0.name = "Erster";

  stud0.vName = "Paul";

  stud0.plz = "7878";

  stud0.ort = "Weingarten";

  stud0.strasse = "Hinterm Berg";

  stud0.hausnummer = 99;

  

  stud1.matrikelNr = "67890";

  stud1.name = "Zweite";

  stud1.vName = "Viola";

  stud1.plz = "8989";

  stud1.ort = "Ravensburg";

  stud1.strasse = "Hauptstr.";

  stud1.hausnummer = 88;

  

  boolean fertig = false;

  while (!fertig) {

    System.out.println("\nEingabe von Daten \nin die Studierendendatei");

    System.out.println("\n1 = Eingabe");

    System.out.println("2 = Ausgabe");

    System.out.println("3 = Aktives Objekt wechseln");

    System.out.println("4 = Programm beenden");

    

    eingabe = JOptionPane.showInputDialog("Ihre Wahl");

    int auswahl = Integer.parseInt(eingabe);

    System.out.println("\nEingegeben wurde: " + auswahl + "\n");

    switch (auswahl) {

    case 1:

      stud.matrikelNr = JOptionPane.showInputDialog("Matrikelnummer:");

      stud.name = JOptionPane.showInputDialog("Name:");

      stud.vName = JOptionPane.showInputDialog("Vorname:");

      stud.plz = JOptionPane.showInputDialog("PLZ:");

      stud.ort = JOptionPane.showInputDialog("Ort:");

      stud.strasse = JOptionPane.showInputDialog("Straße:");

      stud.hausnummer = Integer.parseInt(

      OptionPane.showInputDialog("Hausnummer:")

      );

      break;

    case 2:

      System.out.println("\n--- Studentendaten ---");

      System.out.println(stud.matrikelNr);

      System.out.println(stud.vName + " " + stud.name);

      System.out.println(stud.plz + " " + stud.ort);

      System.out.println(stud.strasse + " " + stud.hausnummer);

      break;

    case 3:

      int nr = Integer.parseInt(

      JOptionPane.showInputDialog("Welcher Student (0 oder 1)?")

      );

      

      if (nr == 0) {

      stud = stud0; // Referenz zeigt jetzt auf stud0

      System.out.println("Jetzt ist Student 0 aktiv.");

      } else if (nr == 1) {

      stud = stud1; // Referenz zeigt jetzt auf stud1

      System.out.println("Jetzt ist Student 1 aktiv.");

      } else {

      System.out.println("Fehler: Bitte 0 oder 1 eingeben!");

      }

      break;

    case 4:

      fertig = true;

      break;

    default:

      System.out.println("Ungültige Eingabe!");

      }

    }

  }

}

9.2 Klasse Stud

Diese Klasse besteht nur aus der Klassendefinition.

package hochschule;

public class Stud {

  public String matrikelNr; //Instanzvariablen

  public String name;

  public String vName;

  public String plz;

  public String ort;

  public String strasse;

  public int hausnummer;

}

9.3 Ergebnis Programmlauf

Nach dem Start des Programms erscheint das Konsolenmenü und die grafische Eingabeaufforderung:

 

 

Ein Programmlauf:

Eingabe von Daten

in die Studierendendatei

1 = Eingabe

2 = Ausgabe

3 = Aktives Objekt wechseln

4 = Programm beenden

Eingegeben wurde: 2

--- Studierendendaten ---

12345

Paul Erster

7878 Weingarten

Hinterm Berg 99

Eingabe von Daten

in die Studierendendatei

1 = Eingabe

2 = Ausgabe

3 = Aktives Objekt wechseln

4 = Programm beenden

Eingegeben wurde: 3

Jetzt ist Student 1 aktiv.

Eingabe von Daten

in die Studierendendatei

1 = Eingabe

2 = Ausgabe

3 = Aktives Objekt wechseln

4 = Programm beenden

Eingegeben wurde: 2

--- Studierendendaten ---

67890

Viola Zweite

8989 Ravensburg

Hauptstr. 88

Eingabe von Daten

in die Studierendendatei

1 = Eingabe

2 = Ausgabe

3 = Aktives Objekt wechseln

4 = Programm beenden

Eingegeben wurde: 4

Wird Eingabe gewählt, werden die einzelnen Adresselemente abgefragt (mit JoptionPane.showInputDialog(...)) und die neue Adresse wird zum aktiven Objekt.

9.4 Anmerkungen zum Programm

Objekte und Referenzen

Stud stud = stud0;

"stud" ist kein neues Objekt, sondern zeigt auf stud0. Dies wird hier Referenz genannt. Stud0 ist das aktive Objekt.

Mit

stud = stud1;

zeigt stud auf ein anderes Objekt. Alle Änderungen betreffen dann stud1 statt stud0. Dieses wird zum aktiven Objekt. Der Begriff "aktives Objekt" soll helfen zu verstehen:

  • dass mehrere Objekte existieren,
  • aber immer nur eines aktuell benutzt wird, weil die Referenzvariablegerade auf dieses Objekt zeigt.

Statt "aktives Objekt" könnte man methodisch genauer sagen: "Das aktuell referenzierte Objekt" oder "Das Objekt, auf das die Referenzvariablestud zeigt".

JoptionPane

JoptionPane ist eine Klasse aus der Java-Bibliothek zur einfachen Erstellung von Dialogfenstern. Sie gehört zum Paket javax.swing. Darum wird in HS2 zu Beginn geschrieben:

import javax.swing.JOptionPane;

Mit JOptionPane können Programme einfach

  • Eingaben abfragen
  • Meldungen anzeigen
  • Warnungen ausgeben
  • Auswahlfenster darstellen

ohne selbst grafische Fenster programmieren zu müssen. In HS2 wird JOptionPane zur Eingabe verwendet. Zum Beispiel:

eingabe = JOptionPane.showInputDialog("Ihre Wahl");

Dadurch erscheint ein kleines Eingabefenster. showInputDialog() liefert immer einen String, auch wenn der Benutzer eine Zahl eingibt. Darum benötigt HS2:

Integer.parseInt(eingabe);

Damit wird ein String in eine Ganzzahl (int) umgewandelt.

Typische Methoden von JOptionPane sind:

  • JOptionPane.showInputDialog(...) für Benutzereingaben.
  • JOptionPane.showMessageDialog(null, "Hallo") für Meldungsfenster.
  • JOptionPane.showConfirmDialog(...) für ein Bestätigungsfenster.

Methodischer Hintergrund

JOptionPane ist eine Klasse der GUI-Bibliothek Swing. GUI bedeutet Graphical User Interface (grafische Benutzeroberfläche).

9.5 Betrachtete Themen

  • Aktives Objekt (aktuell referenziertes Objekt)
  • Benutzereingaben mit JOptionPane.showInputDialog()
  • Erzeugen von Objekten mit new
  • Import-Anweisungen
  • Instanzvariablen
  • Klassen und Objekte
  • Konsolenausgabe mit System.out.println()
  • Lokale Variablen
  • Methoden
  • Methodenaufruf
  • Methodensignatur
  • Objektreferenzen
  • Objektzustand
  • Parameterlose Methoden
  • Referenzvariablen
  • switch-Anweisung
  • Umschalten zwischen Objekten durch Zuweisung einer Referenz
  • Umwandlung von String nach int mit Integer.parseInt()
  • Verzweigungen mit if und else if
  • while-Schleife
  • Zusammenarbeit mehrerer Klassen

10 Projekt HS3 - Array mit Objekten

Array mit Objekten, zufallsgeneriert

In diesem Projekt werden mehrere Objekte mit zufallsgenerierten fiktiven Daten erzeugt. Damit man nicht die Objekte einzeln im Progamm instanziieren muss, wird ein Array zu Hilfe genommen, in dem die Objekte per Programm angelegt werden. Ein solches Array wird auch komplexes Objekt genannt.

Im Programm HP wird zuerst gefragt, wieviele Objekte generiert werden sollen, dann wird ein Array mit dieser Länge angelegt. In diesem werden dann gleich viele Objekte mit Leereinträgen erstellt. Diese haben die Attributsausprägungen 0 (für numerische Instanzvariablen), null (für Strings) und false (für boolesche Attribute). Damit liegt also ein Array mit Objekten vor. Wie üblich, sind die Positionen des Arrays nummeriert, beginnend mit 0. Wenn es also 50 Objekte sind, haben diese die Nummerierungen 0 bis 49 und die Adressen studiosi[i] für i gleich 0 bis 49. Über diese Array-Adresse können die Objekte angesprochen werden.

studiosi[]

Für die zu generierenden Objekte liegen zwei String-Arrays vor, einer mit 20 Nachnamen, einer mit 20 Vornamen. Auf deren Basis werden mit Hilfe der Methode java.util.Random() zufällige Namen und Vornamen erzeugt. Auch die übrigen Attributsausprägungen werden zufällig generiert.

Wichtige Aspekte sind hier also:

  • Erzeugen eines Arrays aus Objekten (komplexes Objekt)
  • Nutzung der Methode Random

Außerdem werden mit Hilfe der Klasse javax.swing.JoptionPane grafische Eingabemöglichkeiten genutzt:

 

In diesem Beispiel wird, wie immer in dieser Lernhilfe, nicht die Nutzereingabe und "Tiefensemantik" kontrolliert. Z.B. könnte das Programm zwei gleiche Namenskombinationen oder Matrikelnummern erzeugen.

10.1 Klasse HP

Klasse HP mit Erläuterungen

package hochschule;

Benötigte Klassen importieren

import java.util.Random;

import javax.swing.JOptionPane;

public class HP {

  public static void main(String[] args) {

Überschrift ausgeben

  System.out.println("--- Objekte mit generierten Daten anlegen ---\n");

1. Benutzer nach Anzahl der Objekte fragen

 

Eingabefenster anzeigen

  String eingabe =

    JOptionPane.showInputDialog(

      "Wieviele Objekte (maximal 100):");

Texteingabe in eine ganze Zahl umwandeln

  int ianz = Integer.parseInt(eingabe);

Eingabe prüfen

  if (ianz < 1 || ianz > 100) {

    System.out.println(

      "Fehler: Die Anzahl muss zwischen 1 und 100 liegen.");

Programm beenden

    return;

  }

Kontrollausgabe ausgeben

  System.out.println("Anzulegende Objekte: " + ianz);

  System.out.println();

2. Array für Studierende erzeugen

  Stud[] studiosi = new Stud[ianz];

3. Datenfelder vorbereiten

Zur Verfügung gestellte Nachnamen

  String[] namen = {

    "Ritter", "Knecht", "Maier", "Müller",

    "Stauder", "Siskor", "Oduro", "Picarda",

    "Ricker", "Maistro", "Studer", "Saskor",

    "Podur", "Prickel", "Bond", "Jones",

    "Riker", "Nobel"

  };

Zur Verfügung gestellte Vornamen

  String[] vornamen = {

    "Paul", "Frieder", "Angelika", "Karolin",

    "Xian", "Ezri", "Jadzia", "Miriam",

    "Elfriede", "Josef", "Guiseppe", "Angie",

    "Frieda", "Andrea", "Ludwig", "Ricardo",

    "Lisa", "Friedrich"

  };

4. Objekte erzeugen, d.h. für jedes Feld im Array ein Stud-Objekt erzeugen

  for (int i = 0; i < studiosi.length; i++) {

    studiosi[i] = new Stud();

  }

  System.out.println("Inhalte generieren und Objekte ausgeben");

Zufallszahlengenerator erzeugen

  Random r = new Random();

5. Daten generieren

  int nr = 0;

  while (nr < studiosi.length) {

    System.out.println();

    System.out.println("Objekt Nr = " + nr);

Matrikelnummer erzeugen. Zufallszahl zwischen 10000 und 19999

    studiosi[nr].mNr = r.nextInt(10000, 20000);

Nachnamen erzeugen. Zufälligen Namen aus dem Array wählen.

    studiosi[nr].name = namen[r.nextInt(namen.length)];

Vornamen erzeugen.

    studiosi[nr].vName = vornamen[r.nextInt(vornamen.length)];

Anzahl Semester erzeugen. Zufallszahl zwischen 1 und 19.

    studiosi[nr].anzSem = r.nextInt(1, 20);

6. Geburtsdatum erzeugen

Monat zwischen 1 und 12

    int monat = r.nextInt(1, 13);

Variable für den Tag

    int tag;

Prüfen, wie viele Tage der Monat hat

    if (monat == 2) {

    tag = r.nextInt(1, 29);

    }

Monate mit 30 Tagen

    else if (monat == 4 ||

      monat == 6 ||

      monat == 9 ||

      monat == 11) {

Tag 1 bis 30

    tag = r.nextInt(1, 31);

    }

Monate mit 31 Tagen

    else {

    tag = r.nextInt(1, 32);

    }

Jahr zwischen 1995 und 2006

    int jahr =

      r.nextInt(1995, 2007);

Datum als String zusammensetzen

    studiosi[nr].gebTag = tag + "." + monat + "." + jahr;

7. Objekt ausgeben, Methode der Klasse Stud aufrufen

    studiosi[nr].ausgeben();

Zum nächsten Objekt wechseln

    nr++;

  }

8. Gesamtanzahl ausgeben

  System.out.println(

    "\nAnzahl Studierende: " + Stud.anzStud + "\n");

  }

}

Klasse HP ohne Erläuterungen

package hochschule;

import java.util.Random;

import javax.swing.JOptionPane;

public class HP {

  public static void main(String[] args) {

    System.out.println("--- Objekte mit generierten Daten anlegen ---\n");

    String eingabe = JOptionPane.showInputDialog("Wieviele Objekte (maximal 100):");

    int ianz = Integer.parseInt(eingabe);

    

    if (ianz < 1 || ianz > 100) {

    System.out.println("Fehler: Die Anzahl muss zwischen 1 und 100 liegen.");

    return;

    }

    

    System.out.println("Anzulegende Objekte: " + ianz);

    System.out.println();

    

    Stud[] studiosi = new Stud[ianz];

    

    String[] namen = {

      "Ritter", "Knecht", "Maier", "Müller", "Stauder",

      "Siskor", "Oduro", "Picarda", "Ricker", "Maistro",

      "Müller", "Studer", "Saskor", "Podur", "Prickel",

      "Ricker", "Bond", "Jones", "Riker", "Nobel"

    };

    

    String[] vornamen = {

      "Paul", "Frieder", "Angelika", "Karolin", "Xian",

      "Ezri", "Jadzia", "Miriam", "Elfriede", "Josef",

      "Guiseppe", "Angie", "Frieda", "Andrea", "Karolin",

      "Ludwig", "Ricardo", "Andrea", "Lisa", "Friedrich"

    };

    for (int i = 0; i < studiosi.length; i++) {

    studiosi[i] = new Stud();

    }

    System.out.println("Inhalte generieren und Objekte ausgeben");

    Random r = new Random();

    int nr = 0;

    while (nr >= 0 amp;amp; nr < studiosi.length) {

      System.out.println();

      System.out.println("Objekt Nr = " + nr);

      studiosi[nr].mNr = r.nextInt(10000, 20000);

      studiosi[nr].name = namen[r.nextInt(namen.length)];

      studiosi[nr].vName = vornamen[r.nextInt(vornamen.length)];

      studiosi[nr].anzSem = r.nextInt(1, 20);

      int monat = r.nextInt(1, 13);

      int tag;

      

      if (monat == 2) {

        tag = r.nextInt(1, 29);

      } else if (monat == 4 || monat == 6 || monat == 9 || monat == 11) {

        tag = r.nextInt(1, 31);

      } else {

        tag = r.nextInt(1, 32);

      }

      int jahr = r.nextInt(1995, 2007);

      

      studiosi[nr].gebTag = tag + "." + monat + "." + jahr;

      studiosi[nr].ausgeben();

      nr++;

  }

  System.out.println("\nAnzahl Studierende: " + Stud.anzStud + "\n");

  }

}

10.2 Klasse Stud

Klasse Stud mit Erläuterungen

package hochschule;

Klassenattribut und statisches Attribut mit Anfangswert.

public class Stud {

  public int mNr; //Matrikelnummer

  public String name;

  public String vName;

  public int anzSem; //Erreichtes Semester

  public String gebTag; //Geburtstag

  static int anzStud = 0;

  

Konstruktor mit Klassenattribut.

  public Stud () {

  anzStud++;

  }

Methode zum Ausgeben der Daten eines Objekts

  void ausgeben() {

    System.out.println("Matrikelnummer: " + mNr);

    System.out.println("Name: " + name);

    System.out.println("Vorname:" + vName);

    System.out.println("Erreichtes Semester: " + anzSem);

    System.out.println("Geburtstag: " + gebTag);

    }

}

Klasse Stud ohne Erläuterungen

package hochschule;

public class Stud {

  public int mNr;

  public String name;

  public String vName;

  public int anzSem;

  public String gebTag;

  static int anzStud = 0;

  public Stud () {

  anzStud++;

  }

  void ausgeben() {

    System.out.println("Matrikelnummer: " + mNr);

    System.out.println("Name: " + name);

    System.out.println("Vorname:" + vName);

    System.out.println("Erreichtes Semester: " + anzSem);

    System.out.println("Geburtstag: " + gebTag);

    }

}

10.3 Ergebnis Programmlauf

Hier wurden aus Platzgründen nur drei Objekte angefordert.

---Objekte mit generierten Daten anlegen

Anzulegende Objekte: 3

Inhalte generieren und Objekte ausgeben

Objekt Nr = 0

Matrikelnummer: 17591

Name: Saskor

Vorname: Jadzia

Erreichtes Semester: 17

Geburtstag: 27.10.2006

Objekt Nr = 1

Matrikelnummer: 16863

Name: Ricker

Vorname: Miriam

Erreichtes Semester: 14

Geburtstag: 13.3.2005

Objekt Nr = 2

Matrikelnummer: 15464

Name: Oduro

Vorname: Ezri

Erreichtes Semester: 3

Geburtstag: 20.0.2002

Anzahl Studierende: 3

10.4 Beschreibung des Programms

Das Projekt HS3 erweitert das Programm HS2: Statt nur mit zwei Studierenden wird jetzt mit vielen gleichzeitig gearbeitet. Die Daten werden automatisch erzeugt und in einem Array gespeichert.

Zusätzlich gibt es das statische Attribut (Klassenattribut) static int anzStud. Diese Variable zählt, wie viele Studierende insgesamt erzeugt wurden.

Der Konstruktor ist

public Stud() {

  anzStud++;

}

Jedes Mal, wenn ein Objekt erzeugt wird, wird der Zähler erhöht.

Die Methode ausgeben() gibt alle Daten eines Studierenden aus:

studiosi[nr].ausgeben();

Das ist eine typische Objektmethode.

Im Hauptprogramm (HP) passiert die eigentliche Verarbeitung.

Schritt 1: Anzahl der Objekte festlegen

eingabe = JOptionPane.showInputDialog("Wieviele Objekte...");

Der Benutzer gibt ein, wie viele Studierendendaten erzeugt werden sollen.

Schritt 2: Array erzeugen

Stud[] studiosi = new Stud[ianz];

Das ist ein Array (Feld) für mehrere Objekte. Es ist zuerst leer, enthält nur Platz für Objekte.

Schritt 3: Objekte erzeugen

for (int i = 0; i < studiosi.length; i++) {

  studiosi[i] = new Stud();

}

Jetzt werden die Objekte tatsächlich erstellt. Ergebnis: studiosi[0], studiosi[1], ... sind "echte Studierende".

Schritt 4: Zufallsdaten erzeugen

  • Mit Random r = new Random() kann man Zufallszahlen erzeugen.
  • Matrikelnummern: r.nextInt(10000, 20000) ergibt eine Zahl zwischen 10000 und 19999
  • Namen auswählen: namen[r.nextInt(20)]; wählt zufällig den Nachnamen aus einer Liste
  • Vornamen auswählen: vornamen[r.nextInt(20)];
  • Semester: r.nextInt(1, 20);

Datum erzeugen

  • Monat wird zufällig gewählt
  • Tag hängt vom Monat ab
  • Jahr wird zufällig bestimmt

Daraus wird ein String gebaut wie: 12.5.2001

Schritt 5: Verarbeitungsschleife

while (nr >= 0 amp;amp; nr < studiosi.length){...}

Diese Schleife geht über alle Objekte. Für jedes Objekt geschieht:

  • Daten erzeugen
  • Daten speichern
  • Daten ausgeben

Mit

studiosi[nr].ausgeben();

werden die Daten der Objekte angezeigt. Die Methode ist in der Klasse Stud definiert.

Am Ende erfolgt die Ausgabe der Gesamtanzahl aller erzeugten Objekte:

System.out.println("Anzahl Studierende: " + Stud.anzStud);

Mit der Klasse Random ein Zufallszahlengenerator erzeugt werden. So wie hier:

Random r = new Random();

D.h., das instanziierte Objekt r von Random() ist ein Zufallszahlengenerator. Man kann sich r wie eine kleine Maschine vorstellen, die immer neue Zufallszahlen liefert. Z.B. eine zufällige Matrikelnummer:

studiosi[nr].mNr = r.nextInt(10000, 20000);

Oder, so wie oben, einen zufälligen Namen aus einem Array:

studiosi[nr].name = namen[r.nextInt(namen.length)];

10.5 Betrachtete Themen

  • Arrays von Objekten
  • Erzeugen zufälliger Testdaten
  • Erzeugen mehrerer Objekte mit new
  • Referenztypen
  • Primitive Datentypen
  • Typ einer Variablen
  • Typ eines Objekts
  • Objektreferenzen in Arrays
  • Array-Index
  • Durchlaufen eines Arrays
  • for-Schleife
  • while-Schleife
  • Zufallszahlen mit Random
  • Erzeugen zufälliger Testdaten
  • Klassenattribute (statische Attribute)
  • static-Attribut
  • Konstruktor
  • Automatisches Zählen erzeugter Objekte
  • Objektmethoden
  • Methodenaufruf
  • Benutzereingaben mit JOptionPane.showInputDialog()
  • Umwandlung von String nach int mit Integer.parseInt()
  • Konsolenausgabe mit System.out.println()
  • Zusammenarbeit mehrerer Klassen

11 Projekt HS4 - Aufrufe über Klassengrenzen

Hier werden Aufrufe über Klassengrenzen hinweg demonstriert, darunter auch eine, die zur Verknüpfung von Inhalten führt. Neben der Klasse mit dem HP liegen zwei inhaltlich motivierte Klassen vor: Stud (Studierende) und Noten.

11.1 Klasse HP

Zu Beginn werden mittels zweier Konstruktoraufrufe die Objekte (Studierenden) s0 und s1 instanziiert und deren Daten ausgegeben. Anschließend erfolgt der Konstruktoraufruf zur Klasse Noten. Auch hier werden die Daten per Parameteraufruf eingetragen. Abschließend wird die Ausgabe verknüpfter Daten gezeigt. Insgesamt erfolgt hier an folgenden Stellen ein Aufruf einer anderen Klasse:

  • s0.ausgeben() ruft im HP eine Methode der Klasse Stud auf.
  • s1.name, usw. fordert im HP aus Stud die Daten des Objekts an.
  • n1.fach fordert von Noten das Studienfach der Notenvergabe an, usw.

In Stud ist ein Klassenattribut eingefügt: anzStud. Es hält die Anzahl der Studierenden (die Anzahl der Objekte von Stud) fest. Das Hochzählen erfolgt im Konstruktor von Stud.

Klassen-
attribut

Klasse HP mit Erläuterungen

package hochschule;

public class HP {

  

  public static void main(String[] args) {

Konstruktor für das Objekt s0. Die Attribute des Objekts werden über die Parameterliste des Konstruktors befüllt. Vgl. Stud für den entsprechenden Aufbau des Konstruktors.

    Stud s0 = new Stud("123", "Maier", "Klara", 5, "21.09.2002");

    System.out.println("Ausgabe der Daten in HP mit Methode der Klasse Stud:");

Aufruf der Instanzmethode ausgeben() der Klasse Stud.java.

    s0.ausgeben();

Ein weiteres Objekt von Stud entsteht und wird ausgegeben.

    Stud s1 = new Stud("234", "Müller", "Ulla", 5, "1.1.1999");

    s1.ausgeben();

Konstruktor der Klasse Noten mit Dateneingabe per Parameter,. Es entsteht Objekt n1. EinfWI bedeutet: Einführung in die Wirtschaftsinformatik. Dann erfolgt der Aufruf der Instanzenmethode ausgNote() zur Ausgabe der Noten.

    Noten n1 = new Noten("234", "EinfWI", 1);

    n1.ausgNote();

Mit den folgenden Befehlen werden, von HP aus, verknüpfte Daten von s1 und n1 ausgegeben. Das Beispiel ist aus didaktischen Gründen stark fokussiert auf die Aufruf- und Konstruktorthematik. In Wirklichkeit müssten natürlich die passenden Studierenden und Noten zuerst gesucht werden.

    System.out.println("\nAusgabe der Noten in Klasse HP:");

    System.out.println("Matrikelnummer: " + s1.mNr + "\nName: " + s1.name + "\nVorname: " + s1.vName + "\nFach: " + n1.fach + "\nNote: " + n1.note);

  }

}

Klasse HP ohne Erläuterungen

package hochschule;

public class HP {

  public static void main(String[] args) {

    Stud s0 = new Stud("123", "Maier", "Klara", 5, "21.09.2002");

    System.out.println("Ausgabe der Daten in HP mit Methode der Klasse Stud:");

    s0.ausgeben();

    Stud s1 = new Stud("234", "Müller", "Ulla", 5, "1.1.1999");

    s1.ausgeben();

    Noten n1 = new Noten("123", "EinfWI", 1);

    n1.ausgNote();

    System.out.println("\nAusgabe der Noten in Klasse HP:");

    System.out.println("Matrikelnummer: " + s1.mNr + "\nName: "

    + s1.name + "\nVorname: " + s1.vName + "\nFach: " + n1.fach

    + "\nNote: " + n1.note);

  }

}

11.2 Klasse Noten

Die Klasse Noten erfasst mit den Attributen mNr (Matrikelnummer), fach (Studienfach) und Note (erreichte Note in Klausur, mündlicher Prüfung, Hausarbeit, usw.) die Noten der Studierenden.

Der Konstruktor ist wiederum so angelegt, dass die Attributsausprägungen (Werte) gleich beim Aufruf übergeben werden. Eine Methode ausgNote() erlaubt es, die Noten eines Studierenden bzgl. eines Faches anzeigen zu lassen.

Klasse Noten mit Erläuterungen

package hochschule;

Ein Konstruktor mit Werteübergabe in der Parameterliste. Damit entsteht dann nicht ein unbeschriebenes Objekt mit 0, null und false, sondern gleich eines mit beschriebenen Attributsausprägungen.
Der Aufruf im HP ist wie folgt: Noten n1 = new Noten("12345", "EinfWI", 1). Zu beachten ist die Zuweisung der übergebenen Werte an die Attribute des Objekts mittels this.

public class Noten {

  public String mNr; //Matrikelnummer

  public String fach; //Studienfach

  public int note;

  public Noten(String matrNr, String fach, int note)

  {

    this.mNr = matrNr;

    this.fach = fach;

    this.note = note;

}

Definition der Instanzmethode ausgNote() ("Noten ausgeben") Diese steht dann im gesamten Programm zur Verfügung, dank package hochschule. Aufruf im HP z.B. so: n1.ausgNote().

  void ausgNote() {

    System.out.println("\nAusgabe der Daten in Klasse Noten:");

    System.out.println("Student (Matrikelnummer): " + mNr);

    System.out.println("Fach: " + fach);

    System.out.println("Note: " + note);

  }

}

Klasse Noten ohne Erläuterungen

package hochschule;

public class Noten {

  public String mNr;

  public String fach;

  public int note;

  public Noten(String matrNr, String fach, int note)

  {

    this.mNr = matrNr;

    this.fach = fach;

    this.note = note;

  }

  void ausgNote() {

    System.out.println("\nAusgabe der Daten in Klasse Noten:");

    System.out.println("Student (Matrikelnummer): " + mNr);

    System.out.println("Fach: " + fach);

    System.out.println("Note: " + note);

  }

}

11.3 Klasse Stud

Die Klasse Stud enthält neben den Attributen mNr (Matrikelnummer), name (Name), vName (Vorname), gebTag (Geburtstag) und anzSem (erreichtes Semester) das Klassenattribut anzStud (Anzahl Studierende). Diese wird bei der Einrichtung auf einen Anfangswert gesetzt.

Die hier gewählte Konstruktorvariante zeigt die Erstellung eines Objekts mit Hilfe der Parameterliste. Wie der zugehörige Aufruf aussieht, zeigt die Klasse HP. Die Einträge der im Aufruf übergebenen Werte erfolgt mit Hilfe des Key Words this. Außerdem wird hier wiederum gleich beim Konstruktor das Attribut anzStud um 1 hochgezählt.

Die Klasse hat außerdem die Methode ausgeben(), mit der die Daten des gerade erstellten Objekts auf der Konsole angezeigt werden.

Klasse Stud mit Erläuterungen

package hochschule;

public class Stud {

Instanzenattribute zur Beschreibung der Objekte.

  public String mNr; //Matrikelnummer

  public String name;

  public String vName;

  public int anzSem; //Erreichtes Semester

  public String gebTag; //Geburtstag

Klassenattribut (statisches Attribut) mit Anfangswert:

  static int anzStud = 0;

Konstruktor mit Parameterliste. Zu beachten ist das Eintragen der Parameterwerte mittel this in die Attribute.
Der Aufruf im HP ist hier wie folgt: Stud s0 = new Stud("1111", "Maier", "Klara", 5, "21.09.2002").
Das Klassenattribut anzStud() wird bei jedem Konstruktoraufruf um 1 erhöht.

  public Stud (String mNr, String name, String vName, int anzSem, String gebTag) {

    this.mNr =mNr;

    this.name =name;

    this.vName = vName;

    this.anzSem = anzSem;

    this.gebTag = gebTag;

    anzStud++; //Zähler für Klassenattribut

  }

Methode zum Ausgeben der Daten eines Objekts. Der Aufruf im HP ist z.B. wie folgt: s1.ausgeben();

  void ausgeben() {

    System.out.println("\nMatrikelnummer: " + mNr);

    System.out.println("Name: " + name);

    System.out.println("Vorname:" + vName);

    System.out.println("Erreichtes Semester: " + anzSem);

    System.out.println("Geburtstag: " + gebTag);

  }

}

Klasse Stud ohne Erläuterungen

package hochschule;

public class Stud {

  public String mNr;

  public String name;

  public String vName;

  public int anzSem;

  public String gebTag; //Geburtstag

  static int anzStud = 0;

  public Stud (String mNr, String name, String vName, int anzSem, String gebTag) {

    this.mNr =mNr;

    this.name =name;

    this.vName = vName;

    this.anzSem = anzSem;

    this.gebTag = gebTag;

    anzStud++;

  }

  void ausgeben() {

    System.out.println("\nMatrikelnummer: " + mNr);

    System.out.println("Name: " + name);

    System.out.println("Vorname:" + vName);

    System.out.println("Erreichtes Semester: " + anzSem);

    System.out.println("Geburtstag: " + gebTag);

  }

}

11.4 Ergebnis Programmlauf

Ausgabe der Daten in HP mit Methode der Klasse Stud:

Matrikelnummer: 123

Name: Maier

Vorname:Klara

Erreichtes Semester: 5

Geburtstag: 21.09.2002

Matrikelnummer: 234

Name: Müller

Vorname:Ulla

Erreichtes Semester: 5

Geburtstag: 1.1.1999

Ausgabe der Daten in Klasse Noten:

Student (Matrikelnummer): 123

Fach: EinfWI

Note: 1

Ausgabe der Noten in Klasse HP:

Matrikelnummer: 234

Name: Müller

Vorname: Ulla

Fach: EinfWI

Note: 1

11.5 Beschreibung des Programms

Das Projekt HS4 besteht aus den drei Klassen HP, Stud und Noten. Es zeigt, wie mehrere Klassen in einem Java-Programm zusammenwirken. Außerdem werden Konstruktoraufrufe mittels Parameterübergabe ausführlicher dargestellt.

Das Projekt besteht aus drei Dateien:

  • HP.java
  • Stud.java
  • Noten.java

Im Projekt werden Studierende erzeugt, deren Daten gespeichert, Noten angelegt und alle Informationen ausgegeben. Dabei wird gezeigt, wie mehrere Klassen zusammenarbeiten.

Die Klasse Noten beschreibt eine einzelne Note. Gespeichert werden:

  • Matrikelnummer des Studierenden
  • Fach
  • Note

Wichtig: Die 1:n-Beziehung zwischen Studierenden und Noten existiert natürlich, wird aber zunächst nur vereinfacht über die Matrikelnummer bei den Notenobjekten erfasst. Die Beschreibung der Einrichtung einer objektorientierten 1:n-Beziehung folgt später. Dabei erhält die Klasse Stud eine Notenliste: List<Noten> noten = new ArrayList<>(); Damit können dann einem Studierenden mehrere Notenobjekte direkt zugeordnet werden.

Die Klasse Noten besitzt zwei Konstruktoren:

  • einen parameterlosen Konstruktor
  • einen Konstruktor mit Parametern

Mit Parametern ergibt sich folgendes:

public Noten(String matrNr, String fach, int note)

Damit werden Werte direkt beim Erzeugen des Objekts übergeben, z.B. so:

Noten n1 = new Noten("123", "EinfWI", 1);

Ohne Parameter ist der Aufruf wie folgt:

public Noten()

Dabei können Standardwerte gesetzt werden, so wie hier:

mNr = "000";

fach = "unbekannt";

note = 0;

Der Aufruf ist:

Noten n2 = new Noten();

Solche Konstruktoren sind nützlich, wenn zunächst ein "leeres" Objekt erzeugt werden soll.

Statische Variable

In der Klasse Stud gibt es eine sog. statische Variable, bzw. ein Klassenattribut:

static int anzStud = 0;

Ein solches bezieht sich nicht auf ein einzelnes Objekt, sondern auf die gesamte Klasse. Hier gibt es an, wie viele Studierenden-Objekte erzeugt wurden. Dafür wird jeweils im Konstruktor um eins erhöht:

anzStud++;

11.6 Betrachtete Themen

  • Aufrufe über Klassengrenzen
  • Ausgabe von Objektdaten
  • Initialisierung von Objektattributen
  • Instanzattribute
  • Klassen und Objekte
  • Klassenattribute (statische Attribute)
  • Konstruktoren
  • Konstruktoren mit Parameterliste
  • Methoden mit Parametern
  • Methodenaufruf
  • Objekterzeugung mit new
  • Objektmethoden
  • Objektreferenzen
  • Parameter und Argumente
  • Parameterübergabe
  • Rückgabewerte
  • static-Attribut
  • this-Referenz
  • Vorbereitung einer 1:n-Beziehung
  • Zusammenarbeit mehrerer Klassen
  • Zusammenarbeit von Stud- und Notenobjekten

12 Projekt HS5 - Eingaben

Benutzereingaben verarbeiten und daraus Objekte erzeugen.

Hier werden mit java.util.Scanner Konsoleneingaben (Integerwerte und Strings) gelesen. Vgl. Abschnitt 3.1. Dann wird ein Objekt erzeugt und mit den eingelesenen Daten bespielt. Mithilfe der Instanzmethode ausgeben() aus Stud.java wird dann dieses Objekt auf dem Bildschirm angezeigt. Abschließend wird das Hochzählen einer Klassenvariablen demonstriert.

12.1 Klasse HP

Klasse HP mit Erläuterungen

package hochschule;

import java.util.Scanner;

public class HP {

  public static void main(String[] args) {

Scanner-Objekt erzeugen. Mit Scanner können Daten über die Tastatur eingelesen werden.
System.in bedeutet: Eingabe von der Konsole.

  Scanner scanner = new Scanner(System.in);

Benutzer fragen, ob ein Objekt angelegt werden soll.

  System.out.print("Objekt eingeben (j/n): ");

Eingabe als Zeichenkette einlesen.

  String s = scanner.nextLine();

Auswahl abhängig von der Eingabe.
switch überprüft den Inhalt der Variablen s.

  switch (s) {

Falls der Benutzer "j" eingibt:

    case "j":

    System.out.println("Eingabe:");

Matrikelnummer abfragen.
nextLine() liefert zunächst einen String.
Integer.parseInt(...) wandelt den String in eine Ganzzahl um.

    System.out.print("Matrikelnummer: ");

    int mNr = Integer.parseInt(scanner.nextLine());

Namen einlesen.

    System.out.print("Name: ");

    String name = scanner.nextLine();

Vorname einlesen.

    System.out.print("Vorname: ");

    String vName = scanner.nextLine();

Aktuelles Semester einlesen.

    System.out.print("Aktuelles Semester: ");

    int anzSem = Integer.parseInt(scanner.nextLine());

Geburtstag einlesen.

    System.out.print("Geburtstag: ");

    String gebTag = scanner.nextLine();

Neues Objekt der Klasse Stud erzeugen. Der parameterlose Konstruktor wird aufgerufen.

    Stud s100 = new Stud();

Die eingegebenen Werte werden den Attributen zugewiesen. Das Objekt s100 wird dadurch beschrieben.

    s100.mNr = mNr;

    s100.name = name;

    s100.vName = vName;

    s100.anzSem = anzSem;

    s100.gebTag = gebTag;

Ausgabeüberschrift.

    System.out.println("\n--Objekt mit externer Methode anzeigen:\n");

Methode ausgeben() der Klasse Stud aufrufen. Die Methode gibt die Daten des Objekts aus.

    s100.ausgeben();

    System.out.println();

Ende dieses case-Zweigs.

    break;

Falls nicht "j" eingegeben wurde. Programmabbruch melden.

  default:

    System.out.println("Abbruch");

    break;

  }

Scanner schließen. Dadurch werden Systemressourcen freigegeben.

  scanner.close();

  }

}

Klasse HP ohne Erläuterungen

package hochschule;

import java.util.Scanner;

public class HP {

  public static void main(String[] args) {

    Scanner scanner = new Scanner(System.in);

    System.out.print("Objekt eingeben (j/n): ");

    String s = scanner.nextLine();

    switch (s) {

      case "j":

        System.out.println("Eingabe:");

        System.out.print("Matrikelnummer: ");

        int mNr = Integer.parseInt(scanner.nextLine());

        System.out.print("Name: ");

        String name = scanner.nextLine();

        System.out.print("Vorname: ");

        String vName = scanner.nextLine();

        System.out.print("Aktuelles Semester: ");

        int anzSem = Integer.parseInt(scanner.nextLine());

        System.out.print("Geburtstag: ");

        String gebTag = scanner.nextLine();

        Stud s100 = new Stud();

        s100.mNr = mNr;

        s100.name = name;

        s100.vName = vName;

        s100.anzSem = anzSem;

        s100.gebTag = gebTag;

        System.out.println("\n-----Objekt mit externer Methode anzeigen:\n");

        s100.ausgeben();

        System.out.println();

        break;

      default:

        System.out.println("Abbruch");

        break;

      }

      scanner.close();

  }

}

12.2 Klasse Stud

Klasse Stud mit Erläuterungen

package hochschule;

public class Stud {

  public int mNr; //Matrikelnummer

  public String name;

  public String vName;

  public int anzSem; //Erreichtes Semester

  public String gebTag; //Geburtstag

Klassenattribut/statisches Attribut anzStud mit Anfangswert.

  static int anzStud = 0;

Konstruktor mit Klassenattribut als Zähler. Bei jedem Aufruf wird er um 1 höher gesetzt.

  public Stud() {

    anzStud++; //Zähler für Klassenattribut

  }

Methode zum Ausgeben der Daten eines Objekts.

  void ausgeben() {

    System.out.println("Matrikelnummer: " + mNr);

    System.out.println("Name: " + name);

    System.out.println("Vorname:" + vName);

    System.out.println("Erreichtes Semester: " + anzSem);

    System.out.println("Geburtstag: " + gebTag);

  }

}

Klasse Stud ohne Erläuterungen

package hochschule;

public class Stud {

  public int mNr; //Matrikelnummer

  public String name;

  public String vName;

  public int anzSem; //Erreichtes Semester

  public String gebTag; //Geburtstag

  static int anzStud = 0;

  public Stud () {

    anzStud++; //Zähler für Klassenattribut

  }

  void ausgeben() {

    System.out.println("Matrikelnummer: " + mNr);

    System.out.println("Name: " + name);

    System.out.println("Vorname:" + vName);

    System.out.println("Erreichtes Semester: " + anzSem);

    System.out.println("Geburtstag: " + gebTag);

  }

}

12.3 Ergebnis Programmlauf

Objekt eingeben (j/n):

j

Eingabe:

Matrikelnummer: 12345

Name: Studer

Vorname: Guiseppe

Aktuelles Semester: 8

Geburtstag: 21.9.2000

-----Objekt mit externer Methode anzeigen:

Matrikelnummer: 12345

Name: Studer

Vorname:Guiseppe

Erreichtes Semester: 8

Geburtstag: 21.9.2000

12.4 Anmerkungen zum Programm

Der Programmlauf zeigt, dass die Konsoleneingabe mit java.util.Scanner recht schlicht ist. Es gibt die Möglichkeit, mit

System.out.println("\nMatrikelnummer:");

den Cursor auf dem Bildschirm in die nächste Zeile zu stellen, was aber auch nicht überzeugend ist. Deshalb werden hier in den weiteren Programmen noch andere Vorgehensweisen für Eingaben demonstriert.

Das Programm ist bewusst einfach gehalten und dient Lernzwecken:

  • Die Attribute sind öffentlich zugänglich
  • Es erfolgt keine Fehlerbehandlung bei falschen Eingaben

Dies müsste in professionellen Anwendungen anders gelöst werden.

12.5 Betrachtete Themen

  • Automatisches Zählen erzeugter Objekte
  • Einlesen von Benutzereingaben
  • Einlesen von Strings mit nextLine()
  • Erzeugen eines Scanner-Objekts
  • Freigeben von Ressourcen mit close()
  • Initialisierung von Objektattributen
  • Instanzattribute
  • Klasse Scanner
  • Klassenattribute (statische Attribute)
  • Konsolenausgabe mit System.out.print() und System.out.println()
  • Konsoleneingaben mit Scanner
  • Methodenaufruf
  • Objekterzeugung mit new
  • Objektmethoden
  • Parameterloser Konstruktor
  • static-Attribut
  • switch-Anweisung
  • System.in
  • Umwandlung von String nach int mit Integer.parseInt()
  • Zusammenarbeit mehrerer Klassen

13 Exkurs - Nachrichtenverkehr

In der UML spielt der Nachrichtenverkehr zwischen Objekten eine wichtige Rolle bei der Erbringung der Programmleistung. An dieser Stelle nun die Frage: Gibt es in Java auch das Konzept der Nachrichten zwischen Objekten und wie ist es umgesetzt.

Der Hintergrund des Nachrichtenkonzepts der UML ist ja, dass ein Objekt einer Klasse ein Objekt einer anderen Klasse zu einer Aktion aufruft. Dies geschieht in Java durch Methodenaufrufe, wie sie auch hier in den Programmen zu sehen sind. Ein einfaches Beispiel ist, dass aus dem HP der Ausdruck aus einer Klasse Drucker angefordert wird:

class Drucker {

void drucken(String text) {

  System.out.println(text);

  }

}

public class HP {

  public static void main(String[] args) {

    Drucker d = new Drucker();

    d.drucken("Hallo Welt");

  }

}

Hier erhält also das Objekt d der Klasse Drucker die Nachricht

drucken("Hallo Welt")

und führt daraufhin die Methode aus.

Beispiel aus den HS-Projekten: In der Klasse HP aus HS9 liegt vor:

s1.addNote(n1);

Die Methode ist in der Klasse Stud und wird dort für das Objekt s1 ausgeführt.

Im HP des Projekts HS6 wird mittels

s1.ausgeben();

die entsprechende Methode in Stud aufgerufen und für das Objekt s1 ausgeführt. Näheres siehe dort.

Merksatz

Objektorientierte Programme bestehen aus Objekten, die sich gegenseitig über Methodenaufrufe Nachrichten senden und die damit gemeinsam die Aufgabenerledigung realisieren.

14 Projekt HS6 - Record-Klassen

Hier geht es um Record-Klassen. Vgl. auch Abschnitt 4.7.

14.1 Projektbeschreibung

Ziel des Projekts

Das Projekt HS6 führt in das Konzept der Records in Java ein. Records sind eine spezielle Form von Klassen, die verwendet werden, um Objekte mit Daten besonders einfach zu beschreiben. Viele Standardfunktionen werden dabei automatisch von Java erzeugt.

Das Projekt zeigt:

  • Erzeugen von Record-Objekten
  • Arbeiten mit Attributen
  • automatische Zugriffsmethoden
  • automatische toString()-Methode
  • Vergleich von Objekten mit equals()
  • eigene Methoden innerhalb eines Records

Aufbau des Projekts

Das Projekt besteht aus den zwei Programmen HP.java (Hauptprogramm, wie üblich) und Stud.java mit den Records für Studierende.

Im Hauptprogramm werden

  • mehrere Record-Objekte erzeugt
  • Attributwerte abgefragt
  • ganze Objekte ausgegeben
  • Objekte miteinander verglichen

Der Record Stud

Mit dem Record Stud werden Studierende beschrieben. Die Definition lautet:

public record Stud(

  String name,

  String vName,

  int plz,

  String ort,

  int anzSem,

String gebTag)

Die einzelnen Bestandteile heißen Komponenten des Records. Sie entsprechen den Attributen eines normalen Objekts. Ein Record unterscheidet sich von einer normalen Klasse. Java erzeugt automatisch:

  • einen Konstruktor
  • Zugriffsmethoden
  • toString()
  • equals()
  • hashCode()

Dadurch wird der Quellcode deutlich kürzer.

Erzeugen von Record-Objekten

Im Hauptprogramm werden drei Objekte erzeugt:

Stud s1 = new Stud(...);

Stud s2 = new Stud(...);

Stud s3 = new Stud(...);

Beim Aufruf von new wird automatisch der Konstruktor des Records verwendet. Die Werte (Attributsausprägungen) werden direkt als Parameter übergeben.

Zugriff auf einzelne Werte

Für jede Komponente erzeugt Java automatisch eine Zugriffsmethode. Für die Komponente:

String name

entsteht automatisch die Methode:

name()

Beispiel:

System.out.println(s1.name());

Damit wird der Nachname des Objekts s1 ausgegeben.

Ausgabe eines gesamten Objekts

Records besitzen automatisch die Methode:

toString()

Damit können alle Inhalte eines Objekts auf einmal ausgegeben werden. Beispiel:

System.out.println(s1.toString());

Die Ausgabe enthält:

  • den Record-Namen
  • alle Komponenten
  • die zugehörigen Werte

Vergleich von Record-Objekten

Der Vergleich erfolgt mit:

equals()

Beispiel:

s2.equals(s3)

Das Ergebnis ist:

true

weil alle Werte identisch sind. Dagegen liefert:

s1.equals(s2)

den Wert:

false

weil sich mehrere Werte unterscheiden.

14.2 Klasse HP

Klasse HP mit Erläuterungen

package hochschule;

public class HP {

  public static void main(String[] args) {

Drei Record-Objekte werden erzeugt. Auch hier wird mit new der Konstruktor angeworfen. Vgl. Record Stud unten. Die records werden per Parameterübergabe beschrieben.

  Stud s1 = new Stud("Stauder", "Franz", 88888, "Überall", 5, "20.1.2000");

  Stud s2 = new Stud("Müller", "Franz", 99999, "Nirgendwo", 13, "1.1.2001");

  Stud s3 = new Stud("Müller", "Franz", 99999, "Nirgendwo", 13, "1.1.2001");

Für jedes Attribut wird bei Records automatisch eine gleichnamige Abfragemethode bereitgestellt. Für name also name(), wie hier gezeigt.

  System.out.println("\nAusgabe einzelner Attributsausprägungen:\n");

  System.out.println("Name: " + s1.name());

  System.out.println("Name: " + s2.name());

Die Methode toString() liefert Ausgaben eines ganzen Record-Objekts mit Attributsbezeichnung und Attributsausprägung. Vgl. Programmlauf.

  System.out.println("\nAusgabe records s1 ... s3:\n");

  System.out.println(s1.toString());

  System.out.println(s2.toString());

  System.out.println(s3.toString());

  System.out.println("\nVergleich der Objekte:\n");

Der Vergleich von Record-Objekten erfolgt mit der Methode equals(). Eine Abfrage mittels einfacher Gleichsetzung klappt nicht. Vgl. die in Abschnitt 4.7 angegebene Literatur.

  if (s1.equals(s2))

    System.out.println("s1 und s2: Identische Studierende.");

  else

    System.out.println("s1 und s2: Nicht identische Studierende.");

  if (s3.equals(s2))

    System.out.println("s3 und s2: Identische Studierende.");

  else

    System.out.println("s3 und s2: Nicht identische Studieren-de.");

  if (s3.equals(s1))

    System.out.println("s3 und s1: Identische Studierende.");

  else

    System.out.println("s3 und s1: Nicht identische Studieren-de.");

Zur Veranschaulichung hier noch die "klassische" Ausgabe auf der Konsole.

  System.out.println("\nStudierende/r s1:");

  s1.ausgeben();

  System.out.println("\nStudierende/r s2:");

  s2.ausgeben();

  System.out.println("\nStudierende/r s3:");

  s3.ausgeben();

  }

}

Klasse HP ohne Erläuterungen

package hochschule;

public class HP {

  public static void main(String[] args) {

  Stud s1 = new Stud("Stauder", "Franz", 88888, "Überall", 5, "20.1.2000");

  Stud s2 = new Stud("Müller", "Franz", 99999, "Nirgendwo", 13, "1.1.2001");

  Stud s3 = new Stud("Müller", "Franz", 99999, "Nirgendwo", 13, "1.1.2001");

  System.out.println("\nAusgabe einzelner Attributsausprägungen:\n");

  System.out.println("Name: " + s1.name());

  System.out.println("Name: " + s2.name());

  System.out.println("\nAusgabe records s1 ... s3:\n");

  System.out.println(s1.toString());

  System.out.println(s2.toString());

  System.out.println(s3.toString());

  System.out.println("\nVergleich der Objekte:\n");

  if (s1.equals(s2))

    System.out.println("s1 und s2: Identische Studierende.");

  else

    System.out.println("s1 und s2: Nicht identische Studierende.");

  if (s3.equals(s2))

    System.out.println("s3 und s2: Identische Studierende.");

  else

    System.out.println("s3 und s2: Nicht identische Studieren-de.");

  if (s3.equals(s1))

    System.out.println("s3 und s1: Identische Studierende.");

  else

    System.out.println("s3 und s1: Nicht identische Studieren-de.");

  System.out.println("\nStudierende/r s1:");

  s1.ausgeben();

  System.out.println("\nStudierende/r s2:");

  s2.ausgeben();

  System.out.println("\nStudierende/r s3:");

  s3.ausgeben();

  }

}

14.3 Klasse Stud

package hochschule;

Definition des Records Stud. Die in der Klammer angegebenen Bestandteile sind die Komponenten des Records. Java erzeugt daraus automatisch einen Konstruktor sowie Zugriffsmethoden.

public record Stud(String name, String vName, int plz, String ort, int anzSem, String gebTag) {

  void ausgeben() {

    System.out.println("Name: " + name);

    System.out.println("Vorname: " + vName);

    System.out.println("PLZ: " + plz);

    System.out.println("Ort: " + ort);

    System.out.println("Anzahl Semester: " + anzSem);

    System.out.println("Geburtstag: " + gebTag);

  }

}

14.4 Ergebnis Programmlauf

Ausgabe einzelner Attributsausprägungen:

Name: Stauder

Name: Müller

Ausgabe records s1 ... s3:

Stud[name=Stauder, vName=Franz, plz=88888, ort=Überall, anzSem=5, gebTag=20.1.2000]

Stud[name=Müller, vName=Franz, plz=99999, ort=Nirgendwo, anzSem=13, gebTag=1.1.2001]

Stud[name=Müller, vName=Franz, plz=99999, ort=Nirgendwo, anzSem=13, gebTag=1.1.2001]

Vergleich der Objekte:

s1 und s2: Nicht wertgleiche Studierende.

s3 und s2: Wertgleiche Studierende.

s3 und s1: Nicht wertgleiche Studierende.

Studierende/r s1:

Name: Stauder

Vorname: Franz

PLZ: 88888

Ort: Überall

Anzahl Semester: 5

Geburtstag: 20.1.2000

Studierende/r s2:

Name: Müller

Vorname: Franz

PLZ: 99999

Ort: Nirgendwo

Anzahl Semester: 13

Geburtstag: 1.1.2001

Studierende/r s3:

Name: Müller

Vorname: Franz

PLZ: 99999

Ort: Nirgendwo

Anzahl Semester: 13

Geburtstag: 1.1.2001

14.5 Betrachtete Themen

  • Abfrage einzelner Komponenten mit name(), vName() usw.
  • Ausgabe vollständiger Record-Objekte
  • automatische equals()-Methode
  • automatische hashCode()-Methode
  • automatische Konstruktorerzeugung
  • automatische toString()-Methode
  • automatische Zugriffsmethoden
  • boolesche Ausdrücke
  • Datenorientierte Klassen
  • eigene Methoden in Records
  • Erzeugen von Record-Objekten mit new
  • if/else-Anweisung
  • kompakte Schreibweise für Datenobjekte
  • Konsolenausgabe mit System.out.println()
  • Konstruktoraufruf
  • Methode ausgeben()
  • Objektreferenzen
  • Package-Konzept
  • Parameterübergabe an den Record-Konstruktor
  • Record-Klassen
  • Record-Komponenten
  • Schlüsselwort record
  • Unterschied zwischen equals() und ==
  • Unterschied zwischen Record und normaler Klasse
  • unveränderliche Objektdaten
  • Vergleich von Record-Objekten
  • wertbasierte Gleichheit
  • Zugriffsmethoden ohne get-Präfix

14.6 Exkurs - Record vs. Klasse

Ein Record ist in Java eine besondere Form einer Klasse. Jeder Record ist gleichzeitig auch eine Klasse, aber nicht jede Klasse ist ein record. Intern wird für einen Record automatisch eine Klasse erzeugt. Das bedeutet

public record Stud(...)

entspricht ungefähr

public final class Stud,

allerdings mit automatisch erzeugtem Zusatzcode.

Der Record-Kopf gibt die Komponenten des Records an:

(String name, String vName, int plz, String ort, int anzSem, String gebTag)

Java erzeugt daraus automatisch:

  • private finale Attribute
  • einen Konstruktor
  • Getter-Methoden
  • toString()
  • equals()
  • hashCode()

Intern entsteht ungefähr:

private final String name;

private final String vName;

private final int plz;

...

Java erzeugt auch automatisch einen Konstruktor, etwa so:

public Stud(String name, String vName, int plz, String ort, int anzSem, String gebTag) {

  this.name = name;

  this.vName = vName;

  ...

}

Ebenso entsteht für jede Komponente auch eine Zugriffsmethode, die den ansonsten üblichen Getter-Methoden entspricht:

name()

vName()

plz()

ort()

anzSem()

gebTag()

Der Methodenaufruf erfolgt dann z.B. so:

s1.name()

Merksatz

Ein Record ist eine spezielle Klasse zur einfachen Verwaltung unveränderlicher Daten.

14.7 Exkurs - Methode hashCode()

Die Methode hashCode() liefert zu einem Objekt eine ganze Zahl (int), die als eine Art "numerischer Fingerabdruck" des Objekts dient. Sie wird vor allem für Datenstrukturen verwendet wie:

  • HashMap
  • HashSet
  • Hashtable

Dort hilft der Hashcode dabei, Objekte schnell wiederzufinden. Einfaches Bild:

  • Der Hashcode bestimmt, in welches "Fach" oder welchen "Behälter" ein Objekt einsortiert wird.
  • Dadurch muss später nicht die gesamte Sammlung durchsucht werden.

Beispiel:

  • String s1 = "Hallo";
  • System.out.println(s1.hashCode());

Ausgabe z.B.:

69609650

Damit wurde in Java eine alte Dateitechnik herangezogen, die der Direktzugriffsdateien/Hashing. Vgl. für eine Kurzbeschreibung:

https://www.staud.info/rm2/rm_t_1.php#Abschnitt22.5

Wichtig ist, in einem "Behälter" sind tatsächlich mehrere Objekte, dort muss also weiterhin sequentiell gesucht werden. Trotzdem reduziert sich der insgesamt notwendige Suchaufwand enorm.

15 Projekt HS7 - Vererbung, Attribute

Hier wird an einem einfachen Beispiel die Umsetzung des Vererbungskonzepts in Java gezeigt. Dies erfolgt anhand des folgenden Auschnitts aus der Vererbungshierarchie von Abbildung 6.5-1 aus [Staud 2019] und anhand von Attributen.

Die Superklasse ist Fahrzeuge, zwei Subklassen davon sind PKW und LKW, eine weitere Subklasse, jetzt aber von PKW, ist VAN. Attribute sind unten in den Programmen angegeben.

Abbildung 14.7-1: Generalisierung / Spezialisierung mit Vererbung. Quelle: Auszug aus [Staud 2019, Abbildung 14.7-2]

Darstellung

Bei diesem Projekt betrachten wir zuerst die Klassen der Generalisierung / Spezialisierung mit ihrer Vererbungshierarchie, dann das HP (Hauptprogramm). Auch hier werden die Beispiele ganz einfach gehalten und auf die aktuelle Fragestellung zugeschnitten.

15.1 Klasse Fahrzeuge

Klasse Fahrzeuge mit Erläuterungen

package fahrzeuge;

public class Fahrzeuge {

  int fzNr = 0;

  float listenPreis = 0.0F;

Eingeschränkte Sichtbarkeit mit private.

  private float marktPreis = 0.0f;

  String hersteller;

  String bez;

  int baujahr;

  String datumZul;

  double gewicht;

Mögliche Einträge in antrieb: d = Diesel, b = Benzin, e = Elektro, h = Hybrid

  char antrieb = 'n'

  int kw;

  String getriebe;

Der Marktpreis des jeweiligen Objekts kann von den anderen Klassen wegen Sichtbarkeit private nicht direkt abgefragt werden. Deshalb die folgende Methode. Diese wird dann von HP aufgerufen.

  void marktPreis() {

    System.out.println("Derzeitiger Marktpreis vor EINTRAGEN: " + marktPreis);

  }

Eintragen in private marktPreis.

  void marktPreisEintragen(float marktPreis) {

    this.marktPreis = marktPreis;

    System.out.println("Derzeitiger Marktpreis nach EINTRAGEN: " + marktPreis);

  }

}

Klasse Fahrzeuge ohne Erläuterungen

package fahrzeuge;

public class Fahrzeuge {

  int fzNr = 0;

  float listenPreis = 0.0F;

  private float marktPreis = 0.0f;

  String hersteller;

  String bez;

  int baujahr;

  String datumZul;

  double gewicht;

  char antrieb = 'n';

  int kw;

  String getriebe;

  

  void marktPreis() {

    System.out.println("Derzeitiger Marktpreis vor EINTRAGEN: " + marktPreis);

  }

  

  void marktPreisEintragen(float marktPreis) {

    this.marktPreis = marktPreis;

    System.out.println("Derzeitiger Marktpreis nach EINTRAGEN: " + marktPreis);

  }

}

15.2 Klasse PKW

Diese Subklasse von Fahrzeuge enthält Attribute, die spezifisch sind für PKW oder die im jeweiligen Kontext nur für PKW erhoben werden sollen.

package fahrzeuge;

public class PKW extends Fahrzeuge {

  String garantie = "nicht erfasst";

  String typ;

  int autonomStufe;

15.3 Klasse LKW

Klasse LKW mit Erläuterungen

package fahrzeuge;

public class LKW extends Fahrzeuge {

Spezifische Attribute für LKW. Erbt alle Attribute und Methoden von Fahrzeuge.

  double ladeKap;

  int zahlAchsen;

  String zustand = "---";

Konstruktor mit Datenübergabe per Parameter. Aufruf siehe HP.

  public LKW(int fzNr, float listenPreis, String hersteller, String bez, int baujahr, String datumZul, double gewicht, char antrieb, int kw, String getriebe, double ladeKap, int zahlAchsen, String zustand) {

      this.fzNr = fzNr;

      this.listenPreis = listenPreis;

      this.hersteller = hersteller;

      this.bez = bez;

      this.baujahr = baujahr;

      this.datumZul = datumZul;

      this.gewicht = gewicht;

      this.antrieb = antrieb;

      this.kw = kw;

      this.getriebe = getriebe;

      this.ladeKap = ladeKap;

      this.zahlAchsen = zahlAchsen;

      this.zustand = zustand;

  }

Standardkonstruktor. Ist voreingestellt da und muss normalerweise nicht angegeben werden. Hier aber notwendig, weil oben ein spezifischer Konstruktor angegeben wurde.

  public LKW() {

  }

}

Klasse LKW ohne Erläuterungen

package fahrzeuge;

public class LKW extends Fahrzeuge {

  double ladeKap;

  int zahlAchsen;

  String zustand = "---";

  public LKW(int fzNr, float listenPreis, String hersteller,

    String bez, int baujahr, String datumZul, double gewicht,

      char antrieb, int kw, String getriebe, double ladeKap,

        int zahlAchsen, String zustand) {

      this.fzNr = fzNr;

      this.listenPreis = listenPreis;

      this.hersteller = hersteller;

      this.bez = bez;

      this.baujahr = baujahr;

      this.datumZul = datumZul;

      this.gewicht = gewicht;

      this.antrieb = antrieb;

      this.kw = kw;

      this.getriebe = getriebe;

      this.ladeKap = ladeKap;

      this.zahlAchsen = zahlAchsen;

      this.zustand = zustand;

  }

  public LKW() {

  }

}

15.4 Klasse VAN

Klasse VAN mit Erläuterungen

Subklasse von PKW. Diese kann also die Attribute und Methoden von PKW und von Fahrzeuge erben. Sie selbst enthält Attribute, die spezifisch sind für VANs oder die im jeweiligen Kontext nur für VANs erhoben werden sollen.

package fahrzeuge;

public class VAN extends PKW {

  int maxPers = 0;

  boolean bank = false;

Konstruktor mit Übergabe der Werte beim Aufruf.

  public VAN(int maxPers, boolean bank) {

    this.maxPers = maxPers;

    this.bank = bank;

  }

Methode ausgeben() in VAN mit Zugriff auf die Attribute von Superklassen (übergeordneten Klassen).
Alle folgenden Attribute geerbt von Fahrzeuge.

  void ausgeben() {

    System.out.println("Inventarnummer: " + fzNr);

    System.out.println("Hersteller: " + hersteller);

    System.out.println("Bezeichnung: " + bez);

    System.out.println("Baujahr: " + baujahr);

    System.out.println("Datum der Erstzulassung: " + datumZul);

    System.out.println("Antriebsart: " + antrieb);

    System.out.println("Listenpreis: " + listenPreis);

    System.out.println("Gewicht: " + gewicht);

    System.out.println("Nennleistung: " + kw);

    System.out.println("Getriebe: " + getriebe);

Die nächsten drei Attribute stammen von der Klasse PKW.

    System.out.println("Garantie: " + garantie);

    System.out.println("Typ: " + typ);

    System.out.println("Automatisches Fahren, Stufe: " + autonomStufe);

Die nächsten zwei von der Klasse VAN.

    System.out.println("Maximale Anzahl Personen: " + maxPers);

    System.out.println("Bank: " + bank);

  }

}

Klasse VAN ohne Erläuterungen

package fahrzeuge;

public class VAN extends PKW {

  int maxPers = 0;

  boolean bank = false;

  public VAN(int maxPers, boolean bank) {

    this.maxPers = maxPers;

    this.bank = bank;

  }

  void ausgeben() {

    System.out.println("Inventarnummer: " + fzNr);

    System.out.println("Hersteller: " + hersteller);

    System.out.println("Bezeichnung: " + bez);

    System.out.println("Baujahr: " + baujahr);

    System.out.println("Datum der Erstzulassung: " + datumZul);

    System.out.println("Antriebsart: " + antrieb);

    System.out.println("Listenpreis: " + listenPreis);

    System.out.println("Gewicht: " + gewicht);

    System.out.println("Nennleistung: " + kw);

    System.out.println("Getriebe: " + getriebe);

    System.out.println("Garantie: " + garantie);

    System.out.println("Typ: " + typ);

    System.out.println("Automatisches Fahren, Stufe: " + autonomStufe);

    System.out.println("Maximale Anzahl Personen: " + maxPers);

    System.out.println("Bank: " + bank);

  }

}

15.5 Klasse HP

Im Hauptprogramm (Klasse HP) wird die Nutzung des Vererbungsmechanismus demonstriert. Zuerst mit Hilfe des Konstruktors von VAN, dessen Objekte von zwei übergeordneten Klassen die Attribute übernehmen. Dann mit der Klasse LKW, die von der Superklasse erbt. Abschließend wird ein Objekt von Fahrzeuge instanziiert, das von keiner anderen Klasse erbt. Außerdem sind mehrere Methodenaufrufe entlang der Vererbungsbeziehungen eingefügt.

Nicht vergessen: "requires java.desktop;" eintragen in module-info.java

package fahrzeuge;

import javax.swing.JOptionPane;

public class HP {

  public static void main(String[] args) {

    VAN van1 = new VAN(1, false);

    String eingabe;

    

    System.out.println("\nKonstruktor mit Klasse VAN");

    System.out.println("(geerbt wird von PKW und Fahrzeuge):");

    System.out.println();

    System.out.println("Ausgabe des Objekts der Klasse VAN im HP \nOHNE DATEN mit Methode aus VAN:\n");

    van1.ausgeben();

  

    System.out.println("\nBeschreiben aller Attribute des Objekts der Klasse VAN im HP\n");

    van1.fzNr = 1007;

    van1.listenPreis = 50000;

    van1.hersteller = "VW";

    van1.bez = "Touran";

    van1.baujahr = 2020;

    van1.datumZul = "1.1.2020";

    van1.gewicht = 3.5D;

    van1.antrieb = 'd';

    van1.kw = 150;

    van1.getriebe = "Automatik";

    van1.garantie = "3 Jahre";

    van1.typ = "L";

    van1.autonomStufe = 1;

    van1.maxPers = 10;

    van1.bank = true;

    

    System.out.println(

    "\n===========Ausgabe des Objekts der Klasse VAN im HP \nMIT DATEN und der Methode aus VAN:\n");

    van1.ausgeben();

    

    System.out.println("\n==========Methoden in Fahrzeuge aufrufen:==========\n");

    van1.marktPreis();

    System.out.println("Marktpreis festlegen");

    eingabe = JOptionPane.showInputDialog("Neuer Marktpreis: ");

    float marktPreis = Float.parseFloat(eingabe);

    van1.marktPreisEintragen(marktPreis);

    

    System.out.println("\n=====Methodeneinsatz für Preiserhöhung=====");

    System.out.println("Bisheriger Preis: " + van1.listenPreis);

    eingabe = JOptionPane.showInputDialog("Gewünschte Preiserhöhung in %: ");

    int erhPreis = Integer.parseInt(eingabe);

    

    System.out.println("Alter Preis: " + van1.listenPreis);

    System.out.println("Gewünschte Erhöhung: " + erhPreis + "%");

    

    van1.listenPreis = (van1.listenPreis / 100) * (100 + erhPreis);

    System.out.println("Neuer Preis: " + van1.listenPreis);

    

    System.out.println("\n==========Konstruktor von Klasse LKW==========");

    System.out.println("(geerbt wird nur von Fahrzeuge):\n");

    

    LKW lkw1 = new LKW();

    System.out.println("Daten von Objekt LKW1: \n");

    lkw1.fzNr = 123456;

    System.out.println("Fahrzeugnummer: " + lkw1.fzNr);

    lkw1.listenPreis = 150299.90F;

    System.out.println("Listenpreis: " + lkw1.listenPreis);

    lkw1.antrieb = 'd';

    

    if (lkw1.antrieb == 'd')

    System.out.println("Antrieb: Diesel");

    else if (lkw1.antrieb == 'b')

    System.out.println("Antrieb: Benzin");

    else if (lkw1.antrieb == 'e')

    System.out.println("Antrieb: Elektro");

    else if (lkw1.antrieb == 'h')

    System.out.println("Antrieb: Hybrid");

    else

    System.out.println("Antrieb: unbekannt");

    

    lkw1.datumZul = "1.1.2010";

    System.out.println("Datum Erstzulassung: " + lkw1.datumZul);

    lkw1.gewicht = 7.5D;

    System.out.println("Gewicht: " + lkw1.gewicht);

    lkw1.ladeKap = 40.5;

    System.out.println("Ladekapazität: " + lkw1.ladeKap);

    lkw1.zahlAchsen = 6;

    System.out.println("Anzahl Achsen: " + lkw1.zahlAchsen);

    lkw1.zustand = "Schlecht";

    System.out.println("Zustand des LKW: " + lkw1.zustand);

    

    LKW lkw2 = new LKW(12345, 50000.60F, "BMW", "xyzhybrid",

      2022, "1.1.2022", 1500, 'h', 240, "Automatik", 40.5, 4,

        "Toll");

    

    System.out.println("\nDaten von Objekt LKW2: \n");

    System.out.println("Fahrzeugnummer: " + lkw2.fzNr);

    System.out.println("Listenpreis2: " + lkw2.listenPreis);

    System.out.println("Antrieb: " + lkw2.antrieb);

    System.out.println("Datum Erstzulassung: " + lkw2.datumZul);

    System.out.println("Gewicht: " + lkw2.gewicht);

    System.out.println("Ladekapazität: " + lkw2.ladeKap);

    System.out.println("Anzahl Achsen: " + lkw2.zahlAchsen);

    System.out.println("Zustand des LKW: " + lkw2.zustand);

    

    System.out.println("\n==========Konstruktor von Klasse Fahrzeuge==========");

    System.out.println("(nichts wird geerbt):\n");

    

    Fahrzeuge fz1 = new Fahrzeuge();

    fz1.datumZul = "1.1.2016";

    System.out.println("Zulassung: " + fz1.datumZul);

    fz1.fzNr = 12345;

    System.out.println("Fahrzeugnummer: " + fz1.fzNr);

    fz1.antrieb = 'b';

    System.out.println("Antrieb: " + fz1.antrieb);

    fz1.listenPreis = 120900.00F;

    System.out.println("Preis: " + fz1.listenPreis);

    fz1.gewicht = 2.0D;

    System.out.println("Gewicht: " + fz1.gewicht);

    fz1.kw = 140;

    System.out.println("Motorleistung (kW): " + fz1.kw);

    fz1.getriebe = "Schalter";

    System.out.println("Getriebe: " + fz1.getriebe);

    fz1.baujahr = 2020;

    System.out.println("Baujahr: " + fz1.baujahr);

  }

}

15.6 Ergebnis Programmlauf

Folgende zwei Eingabefenster kommen zum Einsatz:

 

Bildschirmausgabe:

Konstruktor mit Klasse VAN

(geerbt wird von PKW und Fahrzeuge):

Ausgabe des Objekts der Klasse VAN im HP

OHNE DATEN mit Methode aus VAN:

Inventarnummer: 0

Hersteller: null

Bezeichnung: null

Baujahr: 0

Datum der Erstzulassung: null

Antriebsart: n

Listenpreis: 0.0

Gewicht: 0.0

Nennleistung: 0

Getriebe: null

Garantie: nicht erfasst

Typ: null

Automatisches Fahren, Stufe: 0

Maximale Anzahl Personen: 1

Bank: false

Beschreiben aller Attribute des Objekts der Klasse VAN im HP

======Ausgabe des Objekts der Klasse VAN im HP

MIT DATEN und der Methode aus VAN:

Inventarnummer: 1007

Hersteller: VW

Bezeichnung: Touran

Baujahr: 2020

Datum der Erstzulassung: 1.1.2020

Antriebsart: d

Listenpreis: 50000.0

Gewicht: 3.5

Nennleistung: 150

Getriebe: Automatik

Garantie: 3 Jahre

Typ: L

Automatisches Fahren, Stufe: 1

Maximale Anzahl Personen: 10

Bank: true

=====Methoden in Fahrzeuge aufrufen:=====

Derzeitiger Marktpreis vor EINTRAGEN: 0.0

Marktpreis festlegen

Derzeitiger Marktpreis nach EINTRAGEN: 40000.0

=====Methodeneinsatz für Preiserhöhung=====

Bisheriger Preis: 50000.0

Alter Preis: 50000.0

Gewünschte Erhöhung: 10%

Neuer Preis: 55000.0

=====Konstruktor von Klasse LKW=====

(geerbt wird nur von Fahrzeuge):

Daten von Objekt LKW1:

Fahrzeugnummer: 123456

Listenpreis: 150299.9

Antrieb: Diesel

Datum Erstzulassung: 1.1.2010

Gewicht: 7.5

Ladekapazität: 40.5

Anzahl Achsen: 6

Zustand des LKW: Schlecht

Daten von Objekt LKW2:

Fahrzeugnummer: 12345

Listenpreis2: 50000.6

Antrieb: h

Datum Erstzulassung: 1.1.2022

Gewicht: 1500.0

Ladekapazität: 40.5

Anzahl Achsen: 4

Zustand des LKW: Toll

=====Konstruktor von Klasse Fahrzeuge=====

(nichts wird geerbt):

Zulassung: 1.1.2016

Fahrzeugnummer: 12345

Antrieb: b

Preis: 120900.0

Gewicht: 2.0

Motorleistung (kW): 140

Getriebe: Schalter

Baujahr: 2020

15.7 Betrachtete Themen

  • Arithmetische Berechnungen
  • Attribute
  • Benutzereingaben mit JOptionPane
  • Boolesche Werte (boolean)
  • Eigene Attribute von Subklassen
  • Geerbte Attribute
  • Geerbte Methoden
  • Generalisierung
  • Hauptprogramm (main())
  • Kapselung
  • Klassen
  • Konsolenausgaben mit System.out.println()
  • Konstruktor mit Parametern
  • Konstruktoren
  • Mehrstufige Spezialisierung von Klassen
  • Mehrstufige Vererbung
  • Methoden
  • Methodenaufrufe
  • Objekte
  • Objekte instanziieren (new)
  • Objektorientierte Modellierung
  • Primitive Datentypen
  • private
  • Referenzdatentypen (String)
  • Sichtbarkeit von Attributen
  • Spezialisierung
  • Standardkonstruktor
  • Subklasse
  • Superklasse
  • Typumwandlung mit Float.parseFloat()
  • Typumwandlung mit Integer.parseInt()
  • Vererbung (extends)
  • Vererbungshierarchie
  • Verwendung von this
  • Verzweigungen mit if - else if - else
  • Wiederverwendung von Programmcode durch Vererbung
  • Zeichenwerte (char)
  • Zugriff auf geerbte Attribute

15.8 Projektbeschreibung

15.8.1 Ziel des Projekts

Das Projekt HS7 dient der weiteren Einführung in die objektorientierte Programmierung mit Java. Im Mittelpunkt stehen folgende Konzepte:

  • Klassen und Objekte
  • Vererbung
  • Konstruktoren
  • geerbte Attribute und Methoden
  • Spezialisierung von Klassen
  • einfache Methodenaufrufe

Das Projekt zeigt anhand verschiedener Fahrzeugtypen, wie gemeinsame Eigenschaften in einer Superklasse zusammengefasst und von Subklassen übernommen werden können.

15.8.2 Aufbau des Projekts

Das Projekt besteht aus fünf Klassen:

 

Klasse

Bedeutung

Fahrzeuge

Superklasse mit allgemeinen Fahrzeugdaten

PKW

Subklasse von Fahrzeuge

VAN

Subklasse von PKW

LKW

Subklasse von Fahrzeuge

HP

Hauptprogramm mit main()

 

Zur Vererbungsstruktur vgl. Kapitelanfang. Es gilt also:

  • Ein PKW ist ein spezielles Fahrzeug.
  • Ein VAN ist ein spezieller PKW.
  • Ein LKW ist ebenfalls ein spezielles Fahrzeug.

D.h., Fahrzeuge ist Superklasse von PKW und LKW, PKW ist Superklasse von VAN.

15.8.3 Beschreibung der einzelnen Klassen

Klasse Fahrzeuge

Die Klasse Fahrzeuge bildet die Superklasse des Projekts. Sie enthält allgemeine Attribute, die für alle Fahrzeuge gelten, zum Beispiel:

  • int fzNr;
  • float listenPreis;
  • String hersteller;
  • String bez;

Zusätzlich enthält die Klasse:

private float marktPreis;

Dieses Attribut ist privat und kann deshalb nicht direkt von außen verändert werden. Der Zugriff erfolgt über Methoden:

  • void marktPreis()
  • void marktPreisEintragen(float marktPreis)

Damit wird das Prinzip der Kapselung vorbereitet.

Klasse PKW

Die Klasse PKW erbt von Fahrzeuge:

public class PKW extends Fahrzeuge

Zusätzlich besitzt ein PKW weitere Eigenschaften:

String garantie;

String typ;

int autonomStufe;

Außerdem enthält die Klasse die Methode:

void komfortAnzeige()

Damit wird gezeigt, dass Subklassen eigene Methoden ergänzen können.

Klasse VAN

Die Klasse VAN erweitert die Klasse PKW:

public class VAN extends PKW

Ein VAN besitzt zusätzliche Attribute:

int maxPers;

boolean bank;

Die Klasse enthält:

  • einen Konstruktor mit Parametern
  • die Methode ausgeben()

Die Methode ausgeben() gibt sowohl eigene als auch geerbte Attribute aus. Dadurch wird sichtbar, dass ein Objekt der Klasse VAN alle Eigenschaften der Superklasse besitzt.

Klasse LKW

Die Klasse LKW erbt direkt von Fahrzeuge:

public class LKW extends Fahrzeuge

Zusätzliche Attribute sind:

double ladeKap;

int zahlAchsen;

String zustand;

Die Klasse besitzt:

  • einen parameterlosen Konstruktor
  • einen Konstruktor mit vielen Parametern

Damit wird der Einsatz verschiedener Konstruktoren demonstriert.

Klasse HP

Die Klasse HP enthält die Methode:

public static void main(String[] args)

Sie dient als Hauptprogramm zum Testen aller Klassen. Im Hauptprogramm werden:

  • Objekte erzeugt
  • Attribute beschrieben
  • Methoden aufgerufen
  • Konstruktoren getestet
  • Ausgaben auf der Konsole erzeugt

15.8.4 Ablauf des Programms

Erzeugen eines VAN-Objekts

VAN van1 = new VAN(1, false);

Zunächst werden nur die Attribute gesetzt, die im Konstruktor von VAN vorkommen. Danach erfolgt eine Ausgabe ohne weitere Daten.

Beschreiben der geerbten Attribute

Anschließend werden zahlreiche geerbte Attribute beschrieben:

van1.fzNr = 1007;

van1.hersteller = "VW";

van1.listenPreis = 50000;

Damit wird gezeigt, dass ein VAN auf geerbte Eigenschaften zugreifen kann.

Methodenaufrufe

Es werden Methoden aus der Superklasse Fahrzeuge verwendet:

van1.marktPreis();

van1.marktPreisEintragen(marktPreis);

Dadurch wird deutlich: Subklassen erben nicht nur Attribute, sondern auch Methoden.

Preisänderung

Im Hauptprogramm wird eine prozentuale Preiserhöhung berechnet. Dabei wird der Umgang mit Benutzereingaben und Berechnungen gezeigt.

Arbeiten mit LKW-Objekten

Es werden zwei LKW-Objekte erzeugt:

LKW lkw1 = new LKW();

LKW lkw2 = new LKW(...);

Dadurch wird der Unterschied zwischen:

  • parameterlosen Konstruktoren
  • Konstruktoren mit Parametern

sichtbar.

Arbeiten mit einem Fahrzeuge-Objekt

Zum Schluss wird ein Objekt der Superklasse direkt erzeugt:

Fahrzeuge fz1 = new Fahrzeuge();

Dadurch wird gezeigt:

  • Auch Superklassen können selbst Objekte erzeugen.
  • Subklassen erweitern lediglich die vorhandenen Eigenschaften.

16 Projekt HS7a - Override und Overload

Hier nun, ganz auf den Punkt gebracht, ein Beispiel zu Override und Overload. Bei den beiden Klassen handelt es sich um gekürzte Versionen von HS7.

Zu beachten ist die Vererbungshierarchie. Die Klasse PKW erbt von der Klasse Fahrzeuge, d.h. die Klasse PKW übernimmt die Attribute fzNr und hersteller sowie grundsätzlich auch die Methode ausgeben() aus der Superklasse.

16.1 Klasse HP

package fahrzeuge;

public class HP {

  public static void main(String[] args) {

  

Ein Objekt der Klasse PKW wird eingerichtet und beschrieben:

    PKW p1 = new PKW();

    p1.fzNr = 1001;

    p1.hersteller = "VW";

    p1.sitzplaetze = 5;

    

    System.out.println("--- Normale Ausgabe ---");

    p1.ausgeben();

    

    System.out.println();

    

    System.out.println("--- Ausgabe mit Überschrift ---");

    p1.ausgeben("Daten des PKW:");

Oben wird einmal die Methode ohne bzw. mit Parametertext ausgegeben.

  }

}

16.2 Klasse Fahrzeuge

package fahrzeuge;

class Fahrzeuge {

  int fzNr;

  String hersteller;

  

  void ausgeben() {

    System.out.println("Fahrzeugnummer: " + fzNr);

    System.out.println("Hersteller: " + hersteller);

  }

}

16.3 Klasse PKW

package fahrzeuge;

class PKW extends Fahrzeuge {

  int sitzplaetze;

Override: Methode aus Fahrzeuge wird überschrieben

  @Override

  void ausgeben() {

    System.out.println("Ausgabe aus der Klasse PKW:");

    System.out.println("Fahrzeugnummer: " + fzNr);

    System.out.println("Hersteller: " + hersteller);

    System.out.println("Sitzplätze: " + sitzplaetze);

  }

Overload: gleicher Methodenname, aber anderer Parameter

  void ausgeben(String titel) {

    System.out.println(titel);

    ausgeben();

  }

}

16.4 Ergebnis Programmlauf

--- Normale Ausgabe ---

Ausgabe aus der Klasse PKW:

Fahrzeugnummer: 1001

Hersteller: VW

Sitzplätze: 5

--- Ausgabe mit Überschrift ---

Daten des PKW:

Ausgabe aus der Klasse PKW:

Fahrzeugnummer: 1001

Hersteller: VW

Sitzplätze: 5

16.5 Betrachtete Themen

  • Annotation @Override
  • Attribute
  • Ausgabe mit System.out.println()
  • Dynamische Bindung bei überschriebenen Methoden
  • Einfache Polymorphie durch Methodenüberschreibung
  • Instanziierung von Objekten (new)
  • Klassen und Objekte
  • Methoden
  • Methoden mit Parametern
  • Methoden ohne Parameter
  • Methodenaufruf
  • Superklasse und Subklasse
  • Objektorientierte Spezialisierung
  • Pakete (package)
  • Überladen von Methoden (Overload)
  • Überschreiben von Methoden (Override)
  • Vererbung (extends)
  • Wiederverwendung von Methoden
  • Zugriff auf geerbte Attribute

16.6 Exkurs: Override vs. Overload

Die Superklasse Fahrzeuge enthält zwei Attribute und eine Methode ausgeben(). Die Klasse PKW enthält ebenfalls zwei Attribute und eine Methode ausgeben(). Die Methode gibt die Daten des Fahrzeugs aus.

Override - Überschreiben einer Methode

Die Superklasse Fahrzeuge besitzt also bereits die Methode void ausgeben(). In PKW wird dieselbe Methode nochmals geschrieben:

  • @Override
  • void ausgeben()

Das wird Override genannt. Die Methode der Superklasse wird in der Subklasse durch eine neue Version ersetzt. Damit können bei einer Ausgabe in PKW die Attribute von Fahrzeuge ergänzt werden. Die Methode wird neu definiert, erweitert und an die Subklasse angepasst.

Die optionale Angabe @Override ist eine sog. Annotation. Sie bedeutet: "Diese Methode überschreibt eine Methode der Superklasse." Der Compiler prüft dadurch, ob wirklich eine passende Methode existiert, ob der Methodenname korrekt geschrieben wurde und ob die Parameter identisch sind.

Overload - Überladen einer Methode

Nun gibt es in PKW zusätzlich:

void ausgeben(String titel)

Damit existieren zwei gleichnamige Methoden, die sich in den Parametern unterscheiden. Dies wird Overload genannt. Die Idee hinter Overload ist, dass eine Methode unterschiedlich verwendet werden kann. Hier z.B. einmal so:

p1.ausgeben();

und zum anderen so:

p1.ausgeben("Daten des PKW:");

Der Methodenname bleibt also gleich, aber die Nutzung wird erweitert. Welche Methode aufgerufen wird, entscheidet Java anhand der Parameter.

Tabellarischer Vergleich

Override

Overload

Gleiche Methodensignatur

Gleicher Methodenname

Vererbung erforderlich

Vererbung nicht erforderlich

Methode der Superklasse wird ersetzt

Zusätzliche Variante der Methode

Laufzeitentscheidung (Polymorphie)

Compilerentscheidung

Beispiel: ausgeben() in PKW

Beispiel: ausgeben() und ausgeben(String titel)

 

17 Projekt HS8 - Kapselung

Hier werden an einfachen Beispielen die Möglichkeiten gezeigt, den Zugriff auf Attribute und Methoden zu kontrollieren. Vgl. auch Abschnitt 4.1.

17.1 Klasse HP

Klasse HP mit Erläuterungen

package hochschule;

public class HP {

  public static void main(String[] args) {

S1 wird mit dem Standardkonstruktor erzeugt.

    Stud s1 = new Stud();

    System.out.println("Leeres Objekt von HP aus mit Methode aus Stud ausgeben:");

Folgendes klappt, weil die Methode ausgeben() in Stud paketsichtbar ist. Wird kein Modifizierer angegeben, gilt in Java die Paketsichtbarkeit. Da HP und Stud beide im Package hochschule liegen, darf HP diese Methode aufrufen. Auch protected wäre hier erlaubt. Würde man ausgeben() dagegen private machen, wäre die Methode von HP aus nicht sichtbar. Ausprobieren!!

    s1.ausgeben();

S0 wird erzeugt (instanziiert) und über den Konstruktor mit Daten versorgt.

    Stud s0 = new Stud("123", "Maier", "Klara", 5, "21.09.2002");

    System.out.println("\nBeschriebenes Objekt von HP aus mit Methode aus Stud ausgeben:");

Folgendes klappt wiederum, weil der Zugriff aus demselben Package kommt und der Modifizierer der Methode ausgeben() dies erlaubt.

    s0.ausgeben();

Das Attribut gebTag kann wegen Sichtbarkeit public abgefragt werden.

    System.out.println("\nDie Ausgabe des Geburtstags klappt: " + s0.gebTag);

Zwei Fehlschläge, verursacht durch den Sichtbarkeitsmodizierer.

    System.out.println("\nDas klappt nicht: s0.name= 'Erwin'");

    System.out.println("\nDas auch nicht: System.out.println(s0.name ohne Setter= + s0.name)");

"Getter" hilft.

    System.out.println("\nDirekte Abfrage s0.name mit Getter-Methode: " + s0.getName());

"Setter" ebenso.

    System.out.println("\nDirektes Beschreiben s0.name mit Setter-Methode (Millifranz): ");

    s0.setName("Millifranz");

    

    System.out.println("\nGeänderter Name: " + s0.getName());

  }

}

Klasse HP ohne Erläuterungen

package hochschule;

public class HP {

  public static void main(String[] args) {

    Stud s1 = new Stud();

    System.out.println("Leeres Objekt von HP aus mit Methode aus Stud ausgeben:");

    s1.ausgeben();

    Stud s0 = new Stud("123", "Maier", "Klara", 5, "21.09.2002");

    

    System.out.println("\nBeschriebenes Objekt von HP aus mit Methode aus Stud ausgeben:");

    s0.ausgeben();

    

    System.out.println("\nDie Ausgabe des Geburtstags klappt: " + s0.gebTag);

    

    System.out.println("\nDas klappt nicht: s0.name= 'Erwin'");

    System.out.println("\nDas auch nicht: System.out.println(s0.name ohne Setter= + s0.name)");

    

    System.out.println("\nDirekte Abfrage s0.name mit Getter-Methode: " + s0.getName());

    

    System.out.println("\nDirektes Beschreiben s0.name mit Setter-Methode (Millifranz): ");

    s0.setName("Millifranz");

    

    System.out.println("\nGeänderter Name: " + s0.getName());

    }

}

17.2 Klasse Stud

Klasse Stud mit Erläuterungen

package hochschule;

public class Stud {

  private String mNr; // Matrikelnummer

  private String name; // privat: Zugriff nur über Getter/Setter

  String vName; // Paketsichtbar

  private int anzSem; // Erreichtes Semester

  public String gebTag; // Geburtstag

Konstruktor mit Parameterübergabe. Er wird vom HP aufgerufen, in der Klasse Stud (natürlich) ausgeführt, deshalb spielen die Sichtbarkeitsmodifizierer keine Rolle

  public Stud(String mNr, String name, String vName, int anzSem, String gebTag) {

    this.mNr = mNr;

    this.name = name;

    this.vName = vName;

    this.anzSem = anzSem;

    this.gebTag = gebTag;

  }

Standardkonstruktor.

  public Stud() {

  }

Getter-Methode. Mit ihr ist es möglich, trotz Sichtbarkeitsmodifizierer private das jeweilige Attribut abzufragen. Siehe HP.

  public String getName() {

    return name;

  }

Setter-Methode. Mit ihr ist es möglich, trotz Sichtbarkeitsmodifizierer private das jeweilige Attribut zu beschreiben. Siehe HP.

  public void setName(String name) {

    this.name = name;

  }

Methode zum Ausgeben der Daten eines Objekts.

  void ausgeben() {

    System.out.println("\nMatrikelnummer: " + mNr);

    System.out.println("Name: " + name);

    System.out.println("Vorname:" + vName);

    System.out.println("Erreichtes Semester: " + anzSem);

    System.out.println("Geburtstag: " + gebTag);

  }

}

Klasse Stud ohne Erläuterungen

package hochschule;

public class Stud {

  private String mNr; // Matrikelnummer

  private String name; // privat: Zugriff nur über Getter/Setter

  String vName; // Paketsichtbar

  private int anzSem; // Erreichtes Semester

  public String gebTag; // Geburtstag

  public Stud(String mNr, String name, String vName, int anzSem, String gebTag) {

    this.mNr = mNr;

    this.name = name;

    this.vName = vName;

    this.anzSem = anzSem;

    this.gebTag = gebTag;

  }

  public Stud() {

  }

  public String getName() {

    return name;

  }

  public void setName(String name) {

    this.name = name;

  }

  void ausgeben() {

    System.out.println("\nMatrikelnummer: " + mNr);

    System.out.println("Name: " + name);

    System.out.println("Vorname:" + vName);

    System.out.println("Erreichtes Semester: " + anzSem);

    System.out.println("Geburtstag: " + gebTag);

  }

}

17.3 Ergebnis Programmlauf

Leeres Objekt von HP aus mit Methode aus Stud ausgeben:

Matrikelnummer: null

Name: null

Vorname:null

Erreichtes Semester: 0

Geburtstag: null

Beschriebenes Objekt von HP aus mit Methode aus Stud ausgeben:

Matrikelnummer: 123

Name: Maier

Vorname:Klara

Erreichtes Semester: 5

Geburtstag: 21.09.2002

Die Ausgabe des Geburtstags klappt: 21.09.2002

Das klappt nicht: s0.name= 'Erwin'

Das auch nicht: System.out.println(s0.name ohne Setter= + s0.name)

Direkte Abfrage s0.name mit Getter-Methode: Maier

Direktes Beschreiben s0.name mit Setter-Methode (Millifranz):

Geänderter Name: Millifranz

17.4 Projektbeschreibung

HS8 behandelt vor allem:

  • Sichtbarkeit von Attributen
  • Zugriffsmodifizierer
  • Getter- und Setter-Methoden
  • kontrollierten Zugriff auf Objektdaten

Es wird gezeigt, dass Attribute nicht immer direkt von außen verändert werden können sollen. Im Projekt gibt es wieder zwei Klassen:

  • HP als Hauptprogramm
  • Stud als Datenklasse für Studierende

Sichtbarkeit

Im Unterschied zu früheren Projekten sind die Attribute jetzt nicht mehr alle gleich sichtbar. In Stud kommen verschiedene Zugriffsarten vor:

  • private String mNr;
  • private String name;
  • String vName;
  • private int anzSem;
  • public String gebTag;

Etwas konkreter:

  • private mit private String mNr. Dieses Attribut darf nur innerhalb der Klasse Stud direkt benutzt werden.

Von HP aus wäre also so etwas nicht erlaubt:

s0.mNr = "999";

oder

System.out.println(s0.anzSem);

  • "protected" ist geschützter als public, aber weniger streng als private. Es bedeutet also, sichtbar im selben Package und in Subklassen, auch wenn diese in einem anderen Package liegen.

Da HP im gleichen Package hochschule liegt, ist der Zugriff hier möglich. Das Projekt zeigt aber zugleich, dass man den Zugriff besser über Methoden steuern kann.

  • Paketsichtbar mit dem Beispiel String vName;

Ohne Modifizierer ist das Attribut im gleichen Package sichtbar.

  • public mit public String gebTag; Dieses Attribut darf von überall direkt benutzt werden. Darum funktioniert in HP System.out.println(s0.gebTag);

Die Sichtbarkeitsmodifizierer zusammengefasst:

 


Modifizierer

Bedeutung

private

Nur innerhalb der Klasse sichtbar

kein Modifizierer

Paketsichtbar

protected

Paket + Subklassen

public

Überall sichtbar


Getter und Setter

Wichtige Komponenten in HS8 sind Getter und Setter:

public String getName() { //Getter

  return name;

}

und

public void setName(String name) {//Setter

  this.name = name;

}

Damit wird name nicht direkt, sondern über Methoden gelesen und geändert. s0.getName() liefert den aktuellen Namen zurück. s0.setName("Millifranz"); ändert den Namen kontrolliert. Damit kann man später Regeln einbauen, zum Beispiel:

public void setName(String name) {

  if (name != null amp;amp; !name.isBlank()) {

    this.name = name;

  }

}

Dann kann nicht einfach irgendein ungültiger Wert gesetzt werden. Das ist ein wichtiger Schritt in Richtung Kapselung.

17.5 Betrachtete Themen

  • Attributzugriff
  • Defaultwerte von Attributen (null, 0)
  • Getter-Methoden
  • Informationsverbergung (Information Hiding)
  • Kapselung
  • Kapselung von Objektdaten
  • Konstruktor mit Parameterübergabe
  • Konstruktoren
  • kontrollierter Attributzugriff
  • Methodenaufruf
  • Objektinstanziierung mit new
  • Package-Konzept
  • Paketsichtbarkeit (package-private)
  • private
  • protected
  • public
  • Setter-Methoden
  • Sichtbarkeitsmodifizierer
  • Standardkonstruktor
  • Validierung über Setter
  • Zugriffsschutz für Attribute

17.6 Anmerkung

Hier soll v.a. gezeigt werden, dass Attribute nicht einfach immer public sein sollten. Die spätere Erweiterung

public void setName(String name) {

  if (name != null amp;amp; !name.isBlank()) {

    this.name = name;

  }

}

zeigt dann, dass neben dem Verstecken von Daten auch ein kontrollierter Zugriff mit Plausibilitätsprüfungen mögöich wird:

18 Projekt HS9 - Assoziationen

Hier geht es um folgende Aspekte:

  • eine Assoziation zwischen Klassen umzusetzen
  • den Umgang mit Objektreferenzen zu üben
  • eine 1:n-Beziehung in Java zu modellieren

Dies erfolgt am Beispiel der Verwaltung von Studierenden und ihren Noten. Die Klassen sind:

  • Stud (Studierende) mit Matrikelnummer, Name, Vorname
  • Noten mit Fach und Note

Die Assoziation soll mit Objektreferenzen umgesetzt werden.

18.1 Klasse HP

package hochschule;

public class HP {

  public static void main(String[] args) {

Konstruktor für Studierendenobjekt

    Stud s1 = new Stud();

    s1.mNr = 1001;

    s1.name = "Maier";

    s1.vName = "Paul";

Noten erzeugen

    Note n1 = new Note("Mathe", 2, s1);

    Note n2 = new Note("Informatik", 1, s1);

    Noten n3 = new Noten("GPM", 3, s1);

Verknüpfen von Studierenden und Noten

    s1.addNote(n1);

    s1.addNote(n2);

    s1.addNote(n3);

Ausgabe

    System.out.println(s1.name + " hat folgende Noten:");

    for (Noten n : s1.noten) {

      System.out.println(n.fach + ": " + n.note);

    }

  }

}

18.2 Klasse Stud

Klasse Stud mit Erläuterungen

package hochschule;

Für die Verwaltung mehrerer Objekte werden Klassen aus der Java-Bibliothek importiert.

Import java.util.ArrayList;

import java.util.List;

class Stud {

  int mNr;

  String name;

  String vName;

Hier wird eine Liste für Noten angelegt. Die Bedeutung im Einzelnen:
- List<Noten>
"List" ist ein allgemeiner Datentyp für Listen.
<Noten> bedeutet: In dieser Liste dürfen nur Objekte der Klasse Noten gespeichert werden.
Die Liste funktioniert ähnlich wie ein flexibles Array. Der Unterschied: Die Größe muss nicht vorher festgelegt werden.
noten: Bezeichnung der Liste.
= new ArrayList<>()
Mit "new" wird die Liste tatsächlich erzeugt.
ArrayList ist eine konkrete Implementierung von List.
In die Liste werdenn später Notenobjekte eingefügt. Anfangs ist die Liste leer.

  List<Note> noten = new ArrayList<>();

Methode zum Hinzufügen einer Note
Der Parameter n ist ein Objekt der Klasse Noten.
noten.add(n); fügt dieses Objekt der Liste hinzu.

    void addNote(Note n) {

    noten.add(n);

  }

}

Klasse Stud ohne Erläuterungen

package hochschule;

import java.util.ArrayList;

import java.util.List;

class Stud {

  int mNr;

  String name;

  String vName;

  

  List<Note> noten = new ArrayList<>();

  

  void addNote(Note n) {

    noten.add(n);

  }

}

18.3 Klasse Note

package hochschule;

class Note {

  String fach;

  int note;

Objekt der Klasse Stud wird angelegt

  Stud student;

Konstruktor mit Parameterübergabe.

  public Noten(String fach, int note, Stud student) {

    this.fach = fach;

    this.note = note;

Damit entsteht die Objetreferenz.

    this.student = student;

  }

}

18.4 Ergebnis Programmlauf

Maier hat folgende Noten:

Mathe: 2

Informatik: 1

GPM: 3

Dabei bedeutet GPM "Geschäftsprozessmanagement".

18.5 Erläuterungen zum Programm

ForEach-Schleife

Im Programm oben finden sich folgende Zeilen:

for (Noten n : s1.noten) {

  System.out.println(n.fach + ": " + n.note);

}

Dies ist eine sogenannte forEach-Schleife. Sie wird benutzt, um nacheinander alle Elemente einer Liste zu durchlaufen. In diesem Projekt bedeutet das: Alle Noten des Studierenden s1 werden ausgegeben. Der Schleifenrumpf:

System.out.println(n.fach + ": " + n.note);

gibt jeweils Fach und Note aus.

Interface

Die Zeile ...

List<Note> noten = new ArrayList<>();

... bedeutet: List<Note> legt den Typ fest. List ist ein Interface aus dem Paket java.util. <Note> bedeutet, die Liste darf nur Objekte vom Typ Note enthalten.

Objektreferenzen

Die Zeile

Stud student;

in der Klasse Note legt eine Referenz auf ein Objekt der Klasse Stud an. Beim Konstruktoraufruf

Note n1 = new Note("Mathe", 2, s1);

wird die Referenz auf den Studierenden s1 an die Note übergeben. Innerhalb des Konstruktors wird sie gespeichert:

this.student = student;

Dadurch bleibt die Verbindung zwischen Studierendem und Note dauerhaft erhalten.

18.6 Betrachtete Themen

  • 1:n-Beziehung
  • ArrayList<>
  • Assoziationen zwischen Klassen
  • bidirektionale Beziehung
  • ForEach-Schleife
  • Generics (<Note>)
  • Hinzufügen von Objekten mit add()
  • Implementierung ArrayList
  • Interface List
  • Iteration über Listen
  • Java Collections Framework
  • Klassenverknüpfungen
  • Konstruktor mit Objektreferenz
  • List<>
  • Listen von Objekten
  • Objektorientierte Modellierung von Beziehungen
  • Objektreferenzen
  • Referenzattribute
  • Studierende und Noten

 

19 Projekt hsdaten - Dateien

Umgesetzt mit Eclipse 2026-06. Package immer: hsdaten.

Das folgende Projekt beschreibt den Umgang mit Textdateien. Damit können in einfacher Form Daten gespeichert und bearbeitet werden, nicht wie in Datenbanksystemen, aber immerhin. Sinnvoll wäre ein in Java integriertes objektorientiertes Datenbanksystem. Das liegt aber nicht vor.

Stattdessen bietet Java APIs (Application Programming Interfaces) für zahlreiche unterschiedliche Datenbanksysteme an. Unter anderem für relationale Datenbanksysteme (RDBS) und ihre Datenbanken. Dies ist sinnvoll, da RDBS sehr weit verbreitet sind. Die dafür zur Verfügung gestellten Schnittstellen werden JDBC (Java Database Connectivity) genannt. Vgl. [Ullenboom 2024, Kapitel 21].

In einer der nächsten Auflagen wird auch das hier vorgestellt, weil (natürlich) viele UML-Elemente auf Datenbestände ausgerichtet sind.

Wobei dies keine wirklich zufriedenstellende Lösung ist. Relationale Datenbanken und objektorientierte sind zwei verschiedene Ansätze zur Lösung der Persistenzfrage, weshalb ihr Zusammenwirken umfangreiche technische und inhaltliche Abstimmungen verlangt. Das wird auch als Object-Relational Impedance Mismatch bezeichnet.

Mismatch

In diesem Projekt wird beschrieben, wie eine Textdatei eingerichtet, beschrieben und gelesen wird. Für die Arbeit mit Dateien werden Klassen aus dem Paket java.io benötigt. Eine zentrale Rolle spielt dabei die Klasse java.io.File.

Slash und Backslash bei Verzeichnissen in Java

Beim Arbeiten mit Dateien muss man Java sagen, wo eine Datei gespeichert oder gelesen werden soll. Dazu verwendet man einen Pfad. Beispiel:

C:\Meine Dateien\Stud004.txt

Dieser Pfad besteht aus:

  • Laufwerk: C:
  • Ordner: Meine Dateien
  • Datei: Stud004.txt

Windows verwendet traditionell den umgekehrten Schrägstrich (Backslash) in Pfaden. Deshalb die Angabe oben. In Java hat allerdings der Backslash eine Sonderbedeutung. Er leitet sogenannte Escape-Sequenzen ein. Zum Beispiel \n für neue Zeile. Darum interpretiert Java einen einzelnen Backslash nicht einfach als gewöhnliches Zeichen, weshalb man schreibt:

C:\\Meine Dateien\\Stud004.txt

Denn: Das Zeichenpaar \\ steht in Java für einen einzelnen Backslash im resultierenden Textaus.

Java akzeptiert allerdings auch normale Schrägstriche. Das funktioniert unter Windows in der Regel problemlos:

C:/Meine Dateien/Stud004.txt

Dieser Pfad, "C:/Meine Dateien/...", wird auch in den folgenden Programmen genutzt.

19.1 Klasse Erstellen

19.1.1 Das Programm

Hier wird mit Hilfe der Klasse java.io.File zunächst ein Dateiobjekt erzeugt. Dazu wird, wie das folgende Beispiel zeigt, im Konstruktor der Pfad, der Dateiname und evtl. weitere Informationen angegeben.

Die eigentliche Datei wird anschließend durch die Methode createNewFile() angelegt. Sie richtet die Datei im gewählten Verzeichnis ein. Gibt es sie schon, ist der Ausdruck false und es wird angegeben, dass die Datei bereits existiert:

  • Datei noch nicht da, datei.createNewFile() ist erfolgreich und im Ausdruck wahr.
  • Datei schon da, der Ausdruck hat den Wert false.

Klasse Erstellen mit Erläuterungen

package hsdaten;

Klasse File der Klassenbibliothek in das Programm einbinden.

import java.io.File;

public class Erstellen {

  public static void main(String[] args) {

Try/Catch-Konstrukt. Vgl. Abschnitt 5.2.

    try {

String-Variable einrichten und bespielen.

      String fileName = "Stud006";

Konstruktor der Klasse File. Mit ihm wird ein Objekt erzeugt, das auf die angegebene Datei im angegebenen Verzeichnis verweist.Mit diesen Parametern wird das Verzeichnis und der Name der Datei angegeben. Das "Objekt" datei hat verschiedene Instanzmethoden.

      File datei = new File("C:/Meine Dateien/" + fileName + ".txt");

createNewFile ist eine Instanzmethode von File. Ist der Ausdruck wahr, kannn Erfolg gemeldet werden.

      if(datei.createNewFile())

        System.out.println("Die Datei wurde erstellt");

      else

        System.out.println("Die Datei existiert bereits");

    }

    catch (Exception e) {

      System.err.println(e);

    }

  }

}

Klasse Erstellen ohne Erläuterungen

package hsdaten;

import java.io.File;

public class Erstellen {

  public static void main(String[] args) {

    try {

      String fileName = "Stud006";

      File datei = new File("C:/Meine Dateien/" + fileName + ".txt");

      if(datei.createNewFile())

        System.out.println("Die Datei wurde erstellt");

      else

        System.out.println("Die Datei existiert bereits");

    }

    catch (Exception e) {

      System.err.println(e);

    }

  }

}

19.1.2 Anmerkung

Objekt datei

Die Variable

File datei

enthält eine Referenz auf ein Objekt der Klasse File. Dieses Objekt repräsentiert die Datei

C:/Meine Dateien/Stud006.txt

und stellt Methoden bereit, mit denen auf die Datei zugegriffen werden kann. Hier wird insbesondere die Instanzmethode

createNewFile()

verwendet.

19.1.3 Betrachtete Themen

  • Arbeiten mit dem Dateisystem
  • boolesche Rückgabewerte (true / false)
  • Dateinamen zusammensetzen
  • Dateipfade
  • Dateiverwaltung in Java
  • Exception-Behandlung
  • Fehlerausgabe mit System.err.println()
  • if/else-Anweisung
  • Import von Bibliotheksklassen
  • Instanziierung von Objekten
  • Instanzmethoden
  • Klasse File
  • Konsolenausgabe mit System.out.println()
  • Konstruktoraufruf
  • Methode createNewFile()
  • Paket java.io
  • String-Variablen
  • Try/Catch-Konstrukt

19.2 Klasse Schreiben

19.2.1 Das Programm

Vgl. zu den Klassen für das Lesen und Schreiben aus bzw. in Dateien Kapitel 6, Abschnitt 6.2. In dieser Klasse wird mit der Klasse FileWriter ohne Puffern gearbeitet. Außerdem wird die Datei in einem spezifischen Verzeichnis angelegt, nicht im Standardverzeichnis (vgl. Schreiben2.java).

Klasse Schreiben mit Erläuterungen

package hsdaten;

Klassen aus dem java.io-Paket. FileWriter: Datenstromklasse zum Schreiben von Zeichen in Textdateien. Java.io.IOException: Diese Klasse hilft, Fehlermeldungen ("exceptions") zu organisieren.

import java.io.FileWriter;

import java.io.IOException;

public class Schreiben {

  public static void main(String[] args) {

Die Variable stud1 verweist auf ein Objekt der Klasse FileWriter, über das später Schreibzugriffe auf die Datei erfolgen.

    FileWriter stud1 = null;

Beginn des Try/Catch-Abschnitts. Vgl. Abschnitt 5.2.
Es gibt dazu auch eine modernere Version:
   try (FileWriter stud1 = new FileWriter("C:/Meine Dateien/" + dateiBez + ".txt")) {
      stud1.write(text);}
Mit ihr erfolgt das Schließen automatisch.
Die hier gewählte Version zeigt aber den Ablauf transparenter.

    try {

In einer Stringvariablen wird der Dateiname festgehalten.

      String dateiBez = "stud003";

Konstruktor der Klasse FileWriter. Er erzeugt ein Objekt der Klasse FileWriter und stellt die Verbindung zur angegebenen Datei her. Existiert die Datei noch nicht, wird sie automatisch angelegt.

      stud1 = new FileWriter("C:/Meine Dateien/" + dateiBez + ".txt");

Text bereitstellen für das Eintragen in die Datei.
Mehrere Zeichenketten können mit dem Operator + zu einem längeren String zusammengesetzt werden.
\n ist eine Escape-Sequenz und erzeugt einen Zeilenumbruch.

      String text = "Sinnspruch Nummer 3\n"

        + "Der Weg ist weit, der Weg ist lang,"

        + " doch uns wird - wie immer - nicht bang.";

Schreiben des Textes über den FileWriter in die Datei stud1.

      stud1.write(text);

Schließen der Datei. Durch close() werden noch eventuell ausstehende Schreibvorgänge abgeschlossen und die Verbindung zur Datei ordnungsgemäß beendet.

      stud1.close();

    }

Für den Fall, dass der Try-Abschnitt scheitert, wird eine Fehlermeldung ausgegeben. IOException ist eine spezielle Exception-Klasse für Ein-/Ausgabeoperationen wie Datei- und Verzeichniszugriffe.

    catch (IOException fehler) { System.out.println("Fehler: " + fehler);

    }

  }

}

Klasse Schreiben ohne Erläuterungen

package hsdaten;

import java.io.File;

import java.io.FileWriter;

import java.io.IOException;

public class Schreiben {

  public static void main(String[] args) {

    FileWriter stud1 = null;

    try {

      String dateiBez = "stud003";

      stud1 = new FileWriter("C:/Meine Dateien/" + dateiBez + ".txt");

      String text = "Sinnspruch Nummer 3\n"

      + "Der Weg ist weit, der Weg ist lang,"

      + " doch uns wird - wie immer - nicht bang.";

      stud1.write(text);

      stud1.close();

    }

    catch (IOException fehler) { System.out.println("Fehler: " + fehler);

    }

  }

}

19.2.2 Anmerkung

Datenstrom

Die Klasse FileWriter gehört zu den sogenannten Datenstromklassen (Streams). Sie stellt einen Ausgabestrom bereit, über den Zeichen in eine Datei geschrieben werden können. Das Programm arbeitet dabei in folgenden Schritten:

  • Dateinamen festlegen
  • FileWriter-Objekt erzeugen
  • Text bereitstellen
  • Text in die Datei schreiben
  • Datei schließen

19.2.3 Betrachtete Themen

  • Arbeiten mit Textdateien
  • Arbeiten mit Verzeichnissen
  • Ausgabestrom
  • Dateierzeugung durch FileWriter
  • Dateipfade
  • Datenströme (Streams)
  • Escape-Sequenzen
  • Exception-Behandlung
  • Fehlerausgabe
  • Klasse FileWriter
  • Klasse IOException
  • Konstruktoraufruf
  • Methode close()
  • Methode write()
  • Paket java.io
  • Referenzvariablen
  • Ressourcenverwaltung
  • Schreiben in Dateien
  • String-Variablen
  • Try/Catch-Konstrukt
  • Zeichenkettenverkettung mit +
  • Zeilenumbruch mit \n

19.3 Klasse Schreiben2

19.3.1 Das Programm

Hier wird die Klasse BufferedWriter verwendet, die einen gepufferten Datenstrom bereitstellt. Das Programm als Ganzes erzeugt eine neue Datei. Entweder in einem festgelegen Verzeichnis, so wie hier im Folgenden, oder im Standardverzeichnis c:/eclipse/hsdaten und trägt in diese gepuffert Daten ein. Vgl. hierzu Abschnitt 6.2.

Gepuffertes Schreiben ist für umfangreiche Datenströme oder Datenströme, die in einer Schleife abgearbeitet werden, notwendig. Hier dient es Demonstrationszwecken.

Klasse Schreiben2 mit Erläuterungen

package hsdaten;

import java.io.*;

public class Schreiben2 {

  public static void main(String[] args) throws IOException {

  

Konstruktor der Klasse FileWriter. Das Objekt fiwr stellt das Programmfragment für den Schreibzugriff auf die angebene Datei bereit.

    FileWriter fiwr = new FileWriter("C:/Meine Dateien/Stud004.txt");

Konstruktor der Klasse BufferedWriter. Das Objekt buwr stellt einen gepufferten Ausgabestrom bereit, der die Schreibvorgänge zunächst in einem Speicherpuffer sammelt und anschließend gesammelt an die Datei weitergibt. Die beiden Konstruktoraufrufe können auch verschachtelt geschrieben werden.
Vgl. zu den E/A-Strömen Abschnitt 6.2.

    BufferedWriter buwr = new BufferedWriter(fiwr);

Schreiben in die Datei mit der Methode write(). Anfordern einer Leerzeile.
Java-Programme sollen ja auf verschiedenen Betriebssystemen (Windows, Linux, usw.) einsetzbar sein ("Plattformunabhängigkeit"). Deshalb hier die Methode newLine() für das Anfordern einer neuen Zeile. Bleibt man unter Windows mit seinem Programm, wäre dafür auch die Zeichenfolge \n in der vorausgehenden Textausgabe möglich. Beides wird hier in den Beispielen demonstriert.

    buwr.write("Name=Staudacher");

    buwr.newLine();

    buwr.write("Vorname=Guiseppe");

    buwr.newLine();

    buwr.write("Ort=Ravensburg");

    buwr.newLine();

Beim Schließen werden eventuell noch im Puffer befindliche Daten automatisch in die Datei geschrieben:

    buwr.close();

  }

}

Zu obigem: Die Methode write() schreibt die Daten zunächst in den Puffer des BufferedWriter. Erst wenn der Puffer gefüllt wird oder die Datei geschlossen wird, gelangen die Daten tatsächlich in die Datei.

Klasse Schreiben2 ohne Erläuterungen

package hsdaten;

import java.io.*;

public class Schreiben2 {

  public static void main(String[] args) throws IOException {

    FileWriter fiwr = new FileWriter("C:/Meine Dateien/Stud004.txt");

    BufferedWriter buwr = new BufferedWriter(fiwr);

    

    buwr.write("Name=Müller");

    buwr.newLine();

    buwr.write("Vorname=Inge");

    buwr.newLine();

    buwr.write("Ort=Ravensburg");

    buwr.newLine();

    buwr.close();

  }

}

19.3.2 Anmerkung

Gepuffertes Schreiben

Beim gepufferten Schreiben werden die Daten zunächst im Arbeitsspeicher gesammelt. Dadurch müssen nicht für jede einzelne Ausgabe Schreibzugriffe auf das Dateisystem erfolgen.

Vorteile:

  • höhere Geschwindigkeit
  • weniger Zugriffe auf den Datenträger
  • besonders geeignet für große Datenmengen
  • sinnvoll bei Schleifen mit vielen Ausgaben

Im vorliegenden Beispiel ist die Datenmenge klein. Das Programm dient nur dazu, die Arbeitsweise eines BufferedWriter kennenzulernen.

19.3.3 Betrachtete Themen

  • Arbeiten mit dem Dateisystem
  • Arbeiten mit Textdateien
  • Ausgabeströme
  • Dateipfade
  • Datenströme (Streams)
  • gepufferte Ein-/Ausgabe
  • IOException
  • Klasse BufferedWriter
  • Klasse FileWriter
  • Konstruktoraufrufe
  • Methode close()
  • Methode newLine()
  • Methode write()
  • Paket java.io
  • plattformunabhängige Zeilenumbrüche
  • Pufferkonzept
  • Referenzvariablen
  • Ressourcenverwaltung
  • Schreiben in Dateien
  • throws-Klausel
  • verschachtelte Konstruktoren

19.4 Klasse Lesen

19.4.1 Das Programm

Hier spielen die Klassen FileReaderund BufferedReadereine zentrale Rolle. Ein Objekt von FileReader (also eine Art Schnittstelle zur Datei) ist der Parameter von BufferedReader.

Klasse Lesen

package hsdaten;

import java.io.BufferedReader;

import java.io.FileReader;

import java.io.IOException;

public class Lesen {

  public static void main(String[] args) {

    String dateiBez = "stud003";

    String pfad =

      "C:/Meine Dateien/" + dateiBez + ".txt";

    System.out.println("\nDateiname: "+ dateiBeyz + "\n");

    try {

      FileReader fr =new FileReader(pfad);

      BufferedReader stud1 = new BufferedReader(fr);

      

      String text;

      

      while ((text = stud1.readLine()) != null) {

        System.out.println(text);

      }

      stud1.close();

    } catch (IOException fehler) {

        System.out.println("Fehler: " + fehler);

    }

  }

}

19.4.2 Anmerkung

Für den zeilenweisen Zugriff auf eine Textdatei werden hier zwei Klassen verwendet:

  • FileReader
  • BufferedReader

Ein FileReader stellt den grundlegenden Zugriff auf eine Textdatei her. Er erhält im Konstruktor den Dateinamen, gegebenenfalls zusammen mit der Pfadangabe. Ein BufferedReader legt sich gewissermaßen über den FileReader und ermöglicht ein komfortables zeilenweises Lesen mit der Methode readLine().

Die beiden Objekte können getrennt erzeugt werden:

FileReader fr =

  new FileReader("C:/Meine Dateien/stud003.txt");

BufferedReader br = new BufferedReader(fr);

oder verschachtelt:

BufferedReader br = new BufferedReader(

new FileReader("C:/Meine Dateien/stud003.txt"));

Die verschachtelte Schreibweise wird häufig verwendet.

19.4.3 Betrachtete Themen

  • Ausnahmebehandlung
  • Automatisches Schließen von Ressourcen
  • Dateipfade
  • Dateiverarbeitung
  • FileNotFoundException
  • IOException
  • Klasse BufferedReader
  • Klasse FileReader
  • Konsolenausgabe
  • Lesen von Textdateien
  • Methode readLine()
  • Prüfung auf Dateiende mit null
  • Schleifen
  • try/catch
  • try-with-resources
  • Verschachtelte Konstruktoraufrufe
  • while-Schleife
  • Zeichenkettenverkettung für Dateinamen
  • Zeilenweises Lesen

19.5 Klasse Anhaengen

19.5.1 Das Programm

Das Programm dieser Klasse leistet folgendes:

  • Es fragt den Namen einer vorhandenen Textdatei ab.
  • Es liest eine neue Textzeile von der Konsole ein.
  • Es hängt diese Textzeile an die angegebene Datei an.
  • Es verwendet dazu Scanner, File, FileWriter und Formatter.

Die Klasse Formatter dient hier dazu, die Eingaben in eine Textdatei zu formatieren. Der Konstruktor ist:

Formatter formatter = new Formatter(writer))

Er benötigt ein Objekt der Klasse FileWriter als Parameter. Die Methode format erlaubt dann die Spezifizierung:

formatter.format("%s%n", newInput);

Die Kürzel:

  • %s bedeutet unformatierte Ausgabe eines Strings.
  • %n bedeutet Anforderung einer neuen Zeile, plattformunabhängig

[Ullenboom 2024, S. 668] empfiehlt wegen der großen Zahl an Formatspezifizierern diesbezüglich den Blick in die API-Dokumentation.

Klasse Anhaengen mit Erläuterungen

package hsdaten;

import java.io.File;

import java.io.FileWriter;

import java.io.IOException;

import java.util.Formatter;

import java.util.Scanner;

public class Anhaengen {

  public static void main(String args[]) {

Ein Objekt der Klasse Scanner wird erzeugt. D.h., es wird die Funktionalität bereitgestellt, mit der Eingaben von der Konsole gelesen werden können. Der Parameter System.in gibt an, dass die Eingabe von der Konsole kommt.

    Scanner scanner = new Scanner(System.in);

Zuerst wird auf der Konsole die angegebene Frage formuliert. Dann wird mit der Methode scanner.next() ein Eingabefeld auf der Konsole eingerichtet und für die Aufnahme der Eingabe eine String-Variable fileName angelegt.
Anschließend wird mit der Methode scanner.next() die Konsoleneingabe (der Dateiname) gelesen und in die String-Variable fileName gespielt.
Mit scanner.nextLine() wird dann die textliche Eingabe gelesen.

    System.out.println("Welche Datei (ohne .txt)?");

    String fileName = scanner.next(); //stud02.txt

    scanner.nextLine();

Konstruktor der Klasse File. Dieses Programmfragment (Objekt) stellt den Zugriff auf die angegebene Datei im jeweiligen Verzeichnis bereit. Es erhält die Bezeichnung myFile.

    File datei = new File("C:/Meine Dateien/" + fileName + ".txt");

    

    try(

Konstruktor der (Strom-)Klasse FileWriter. Das Objekt writer stellt eine Schnittstelle zu der als Parameter angegebenen Datei her.

      FileWriter writer = new FileWriter(datei, true);

Konstruktor der Klasse Formatter. Dieses Objekt stellt die Formatierungsmöglichkeiten für die Ausgabe von Texten bereit. Vgl. die nachfolgende Methode format() dieser Klasse.

      Formatter formatter = new Formatter(writer)) {

Zeilenweises Lesen der Eingabe (das ist der Text, der in der Datei an den vorhandenen Text angefügt wird).
Zu den Formatcodes: %s bedeutet "Zeichenkette". %n bedeutet "neue Zeile"

      System.out.println("Eingabe: ");

      String newInput = scanner.nextLine();

      

      formatter.format("%s%n", newInput);

    }

    catch (IOException e) {

      e.printStackTrace();

    }

Schlusssequenz. Schließen der durch die Klasse Scanner angelegten Programmfragmente. Ausgabe der Erfolgsmeldung.

    scanner.close();

    System.out.println("\nEs hat geklappt!");

  }

}

Klasse Anhaengen ohne Erläuterungen

package hsdaten;

import java.io.File;

import java.io.FileWriter;

import java.io.IOException;

import java.util.Formatter;

import java.util.Scanner;

public class Anhaengen {

  public static void main(String args[]) {

    Scanner scanner = new Scanner(System.in); //Konstruktor

    System.out.println("Welche Datei (ohne .txt)?");

    String fileName = scanner.next(); //stud02.txt

    scanner.nextLine();

    File datei = new File("C:/Meine Dateien/" + fileName + ".txt");

    try(

      FileWriter writer = new FileWriter(datei, true);

      Formatter formatter = new Formatter(writer)) {

      System.out.println("Eingabe: ");

      String newInput = scanner.nextLine();

      formatter.format("%s%n", newInput);

    }

    catch (IOException e) {

      e.printStackTrace();

    }

    scanner.close();

    System.out.println("\nEs hat geklappt!");

    }

}

Ergebnis Programmlauf

Welche Datei (ohne .txt)?

stud02

Eingabe:

Angehängter Text

Es hat geklappt!

Hier noch ein Blick auf die Datei:

19.5.2 Anmerkung

Der zweite Parameter bei FileWriter ist hier entscheidend:

new FileWriter(datei, true)

Das true bedeutet: Der neue Text wird an die vorhandene Datei angehängt. Ohne true würde der vorhandene Dateiinhalt überschrieben. Also:

new FileWriter(datei)

öffnet die Datei zum Überschreiben.

new FileWriter(datei, true)

öffnet die Datei zum Anhängen. Zur Methode scanner.nextLine(). Nach

String fileName = scanner.next();

steht der Zeilenumbruch noch im Eingabepuffer. Deshalb folgt:

scanner.nextLine();

Diese Anweisung liest den restlichen Zeilenumbruch weg. Erst danach kann die eigentliche Texteingabe korrekt gelesen werden:

String newInput = scanner.nextLine();

Ohne diese Zwischenzeile könnte newInput versehentlich leer bleiben.

19.5.3 Theorieaspekte

Diese Klasse zeigt, wie vorhandene Textdateien erweitert werden können. Besonders wichtig ist der Konstruktoraufruf:

new FileWriter(datei, true)

Er macht aus dem normalen Schreiben ein Anhängen. Dies zeigt den Unterschied zwischen:

  • Datei neu schreiben bzw. überschreiben,
  • Datei erweitern bzw. anhängen.

Außerdem zeigt das Beispiel das Zusammenspiel mehrerer Klassen:

  • Scanner -> liest Eingaben von der Konsole
  • File -> beschreibt die Datei
  • FileWriter -> öffnet die Datei zum Schreiben
  • Formatter -> formatiert die Ausgabe

Damit verbindet die Aufgabe Konsoleneingabe, Dateiverarbeitung und formatierte Textausgabe.

19.5.4 Betrachtete Themen

  • Anhängen an Textdateien
  • Ausnahmebehandlung
  • Dateipfade
  • Dateiverarbeitung
  • Eingabepuffer bei Scanner
  • Formatierte Ausgabe
  • Formatspezifizierer %s
  • IOException
  • Klasse File
  • Klasse FileWriter
  • Klasse Formatter
  • Klasse Scanner
  • Konsoleneingabe
  • Konstruktoren
  • Objektverkettung
  • Schließen von Ressourcen
  • Schreiben in Textdateien
  • try-with-resources
  • Unterschied zwischen Überschreiben und Anhängen
  • Zeichenkettenverkettung
  • Zeilenumbruch mit %n

 

19.6 Klasse Loeschen

19.6.1 Das Programm

Dieses Programm löscht eine Textdatei. Die Bezeichnung der zu löschenden Datei wird über die Tastatur eingegeben. Anschließend wird ein Objekt der Klasse File erzeugt, das die angegebene Datei repräsentiert. Mit der Methode

delete()

wird versucht, die Datei zu löschen. Im Beispiel wird im Verzeichnis

C:/Meine Dateien/

gearbeitet. Ohne Pfadangabe würde sich die Datei im aktuellen Arbeitsverzeichnis befinden. Bei einer Standardinstallation von Eclipse ist dies häufig das Projektverzeichnis

c:/eclipse/hsdaten.

Klasse Loeschen mit Erläuterungen

package hsdaten;

import java.io.*;

import java.util.Scanner;

public class Loeschen {

  public static void main(String[] args) {

    Scanner scanner = new Scanner(System.in);

    

Die Bezeichnung der zu löschenden Datei wird von der Konsole eingelesen. Hier wird die Datei stud02.txt gewählt.

    System.out.println("Welche Datei soll gelöscht werden (mit .txt)?");

    String fileName = scanner.next(); //stud02.txt

    scanner.nextLine();

Ein Objekt der Klasse File wird mit der Dateibezeichnung erzeugt. Dies repräsentiert dann die Datei in ihrem Verzeichnis und stellt Methoden zu deren Bearbeitung bereit.

    File file = new File("C:/Meine Dateien/" + fileName);

Die Methode file.delete() wird aufgerufen. Ist sie erfolgreich, kommt es zur Erfolgsmeldung, andernfalls zur Misserfolgsmeldung.

    if(file.delete())

      {

      System.out.println("Die Datei wurde gelöscht.");

      }

    else

      {

      System.out.println("Die Datei konnte nicht gelöscht werden.");

      }

      

  }

}

Klasse Loeschen ohne Erläuterungen

package hsdaten;

import java.io.*;

import java.util.Scanner;

public class Loeschen {

  public static void main(String[] args) {

    Scanner scanner = new Scanner(System.in);

    System.out.println("Welche Datei soll gelöscht werden (mit .txt)?");

    String fileName = scanner.next(); //stud02.txt

    scanner.nextLine();

    File file = new File("C:/Meine Dateien/" + fileName);

    if(file.delete())

      {

      System.out.println("Die Datei wurde gelöscht.");

      }

    else

      {

      System.out.println("Die Datei konnte nicht gelöscht werden.");

      }

  }

}

19.6.2 Ergebnis Programmlauf

Welche Datei soll gelöscht werden (mit .txt)?

stud02.txt

Die Datei wurde gelöscht.

oder

Welche Datei soll gelöscht werden (mit .txt)?

stud02.txt

Die Datei konnte nicht gelöscht werden.

19.6.3 Erläuterungen

Zunächst wird ein Objekt der Klasse Scanner erzeugt:

Scanner scanner = new Scanner(System.in);

Damit können Eingaben von der Tastatur gelesen werden. Nach der Eingabe des Dateinamens wird ein Objekt der Klasse File erzeugt:

File file = new File("C:/Meine Dateien/" + fileName);

Dieses Objekt stellt die gewünschte Datei innerhalb des angegebenen Verzeichnisses dar. Die Datei wird anschließend mit

file.delete();

gelöscht. Die Methode besitzt den Rückgabewert

boolean

und liefert

  • true, wenn das Löschen erfolgreich war,
  • false, wenn die Datei nicht gelöscht werden konnte.

Deshalb kann unmittelbar geschrieben werden:

if (file.delete()) {

...

}

Warum kann delete() fehlschlagen?

Die Methode liefert false, wenn beispielsweise

  • die Datei nicht existiert,
  • die Datei geöffnet ist,
  • keine ausreichenden Zugriffsrechte vorhanden sind,
  • der Dateiname falsch eingegeben wurde.

Das Programm kann dadurch ohne Ausnahmebehandlung erkennen, ob das Löschen erfolgreich war.

Theorieaspekte

Diese Klasse zeigt erstmals eine Änderung am Dateisystem. Während die vorherigen Programme

  • Dateien erzeugten,
  • in Dateien schrieben,
  • Dateien lasen oder
  • Dateien erweiterten,

wird nun eine Datei vollständig entfernt. Es wird gezeigt, dass viele Methoden einen Wahrheitswert (boolean) zurückgeben, der unmittelbar für Entscheidungen verwendet werden kann:

if (file.delete()) {

  ...

}

Außerdem wird deutlich, dass ein Objekt der Klasse File nicht den Inhalt einer Datei enthält, sondern eine Datei bzw. einen Pfad im Dateisystem repräsentiert. Diese Klasse stellt anschließend Methoden zur Verfügung, mit denen auf die Datei zugegriffen oder sie verändert werden kann. Dies ist ein wichtiges Grundprinzip der objektorientierten Dateiverarbeitung in Java.

Objektorientierte Daten-
verarbeitung

19.6.4 Betrachtete Themen

  • Bedingte Anweisungen (if)
  • Dateipfade
  • Dateisystemzugriffe
  • Dateiverarbeitung
  • Erfolgskontrolle von Methoden
  • Klasse File
  • Klasse Scanner
  • Konsoleneingabe
  • Konstruktoren
  • Löschen von Dateien
  • Methode delete()
  • Objekt der Klasse File
  • Ressourcen schließen (scanner.close())
  • Rückgabewert boolean
  • Zeichenketten als Dateinamen

19.7 Klasse ErzeugLoesch

Das Programm erzeugt zunächst eine Textdatei im Verzeichnis

C:/Meine Dateien/

und schreibt mehrere Zeilen in diese Datei. Anschließend fragt das Programm mit einem JOptionPane-Dialog, ob die Datei wieder gelöscht werden soll. Bei Eingabe von j oder J wird die Datei gelöscht. Bei n oder N bleibt sie erhalten.

Klasse ErzeugLoesch mit Erläuterungen

package hsdaten;

import java.io.BufferedWriter;

import java.io.File;

import java.io.FileWriter;

import java.io.IOException;

import javax.swing.JOptionPane;

public class ErzeugLoesch {

    

Die IOException ist zur Fehlerbehandlung notwendig.

  public static void main(String[] args) throws IOException {

Zuerst eine Datei erzeugen

    String dateibez = "C:/Meine Dateien/stud05.txt";

Mit dem "Objekt" fiwr der Klasse FileWriter wird die angegebene Datei zum Beschreiben bereitgestellt. Das "Objekt" buwr der Klasse BufferedWriter stellt Methoden für das gepufferte Schreiben bereit. Der Parameter ist hier die Bezeichnung des File Writer-Objekts. Vgl. auch Abschnitt 6.2.

    FileWriter fiwr = new FileWriter(dateibez);

    BufferedWriter buwr = new BufferedWriter(fiwr);

Mit den Methoden newLine() (Neue Zeile anfordern) und write() (Zeile in die Datei eintragen) wird die Datei beschrieben.

    buwr.write("Name=Mostkopf");

    buwr.newLine();

    buwr.write("Vorname=Paul");

    buwr.newLine();

    buwr.write("Ort=Ravensburg");

    buwr.newLine();

    buwr.close();

    System.out.println("Datei " + dateibez + "

        wurde im Standardverzeichnis erzeugt");

Die Eingabe in die grafische Eingabeaufforderung (siehe unten) führt zu einem String.

    String eingabe = JOptionPane.showInputDialog

      ("Datei wieder löschen (j/n)?");

Da der Umgang mit Strings manchmal schwierig ist und als Beispiel hier das Auslesen des ersten Stringelements in ein Character-Zeichen, mit dem dann die Abfrage, ob j/J oder n/N vorliegt, einfacher ist.

    char erster = eingabe.charAt(0);

    if ((erster == 'j') || (erster == 'J')) {

      System.out.println("j wurde erkannt");

In dateibez steht die Bezeichnung der Datei. Damit wird ein Objekt der Klasse File erzeugt. Dies repräsentiert die Datei in ihrem Verzeichnis.

      File file = new File(dateibez);

Dann wird mit dieser Dateibezeichnung die Methode file.delete() zum Löschen der Datei aufgerufen. Ist sie erfolgreich, kommt es zur Erfolgsmeldung, andernfalls zur Misserfolgsmeldung

      if(file.delete())

      {

      System.out.println("Die Datei wurde gelöscht.");

      }

      else

      {

      System.out.println("Die Datei konnte nicht gelöscht werden.");

      }

    }

  else if ((erster == 'n' ) || (erster == 'N')) {

  System.out.println("Datei wird nicht gelöscht");

  }

  else System.out.println("Fehlerhafte Eingabe");

  }

}

Klasse ErzeugLoesch ohne Erläuterungen

package hsdaten;

import java.io.BufferedWriter;

import java.io.File;

import java.io.FileWriter;

import java.io.IOException;

import javax.swing.JOptionPane;

public class ErzeugLoesch {

  public static void main(String[] args) throws IOException {

    String dateibez = "C:/Meine Dateien/stud05.txt";

    FileWriter fiwr = new FileWriter(dateibez);

    BufferedWriter buwr = new BufferedWriter(fiwr);

    buwr.write("Name=Mostkopf");

    buwr.newLine();

    buwr.write("Vorname=Paul");

    buwr.newLine();

    buwr.write("Ort=Ravensburg");

    buwr.newLine();

    buwr.close();

    System.out.println("Datei " + dateibez + " wurde im Standardverzeichnis erzeugt");

    String eingabe = JOptionPane.showInputDialog("Datei wieder löschen (j/n)?");

    char erster = eingabe.charAt(0);

    if ((erster == 'j') || (erster == 'J')) {

      System.out.println("j wurde erkannt");

      File file = new File(dateibez);

      if(file.delete())

      {

      System.out.println("Die Datei wurde gelöscht.");

      }

      else

      {

      System.out.println("Die Datei konnte nicht gelöscht werden.");

      }

    }

  else if ((erster == 'n' ) || (erster == 'N')) {

  System.out.println("Datei wird nicht gelöscht");

  }

  else System.out.println("Fehlerhafte Eingabe");

  }

}

Eingabefenster:

19.7.1 Ergebnis Programmlauf

Datei C:/Meine Dateien/stud05.txt wurde erzeugt.

j wurde erkannt

Die Datei wurde gelöscht.

 

oder

 

Datei C:/Meine Dateien/stud05.txt wurde erzeugt.

Datei wird nicht gelöscht.

19.7.2 Erläuterungen

Zunächst wird der vollständige Dateiname festgelegt:

String dateibez = "C:/Meine Dateien/stud05.txt";

Danach werden ein FileWriter und ein BufferedWriter erzeugt:

FileWriter fiwr = new FileWriter(dateibez);

BufferedWriter buwr = new BufferedWriter(fiwr);

Der FileWriter stellt die Verbindung zur Datei her. Der BufferedWriter ermöglicht komfortables gepuffertes Schreiben. Mit

buwr.write("Name=Mostkopf");

buwr.newLine();

wird eine Zeile geschrieben und anschließend ein Zeilenumbruch eingefügt. Nach dem Schreiben fragt das Programm über ein Eingabefenster:

JOptionPane.showInputDialog("Datei wieder löschen (j/n)?");

Die Eingabe wird als String zurückgegeben. Mit

char erster = eingabe.charAt(0);

wird das erste Zeichen der Eingabe ausgewertet. Dadurch werden auch Eingaben wie

ja

oder

Ja

erkannt, weil nur das erste Zeichen betrachtet wird.

Theorieaspekte

Diese Klasse verbindet mehrere bisher getrennt behandelte Dateioperationen:

  • Datei erzeugen
  • Datei beschreiben
  • Benutzerentscheidung einholen
  • Datei optional löschen

Besonders wichtig ist dabei die Kombination aus Dateiverarbeitung und Benutzereingabe. Es wird gezeigt, dass ein Programm nach dem Schreiben einer Datei eine Entscheidung vom Benutzer einholen und abhängig davon unterschiedliche Aktionen ausführen kann. Außerdem wird der Unterschied zwischen

String eingabe

und

char erster

sichtbar. Der String enthält die gesamte Eingabe, während charAt(0) nur das erste Zeichen auswertet. Dadurch kann das Programm einfache Ja-/Nein-Entscheidungen verarbeiten.

19.7.3 Betrachtete Themen

  • Dateiverarbeitung
  • Erzeugen von Textdateien
  • Schreiben in Textdateien
  • Löschen von Dateien
  • Klasse File
  • Klasse FileWriter
  • Klasse BufferedWriter
  • Klasse JOptionPane
  • Gepuffertes Schreiben
  • Methode write()
  • Methode newLine()
  • Methode delete()
  • Grafische Eingabe mit showInputDialog()
  • Zeichenketten (String)
  • Zeichen (char)
  • Methode charAt()
  • Bedingte Anweisungen (if, else if, else)
  • Logische Operatoren (||)
  • Ausnahmebehandlung
  • IOException
  • try-with-resources
  • Automatisches Schließen von Ressourcen
  • Prüfung auf null
  • Prüfung auf leere Eingabe
  • Dateipfade
  • Konsolenausgabe

19.8 Klasse EinlesenCSV

19.8.1 Das Programm

CSV steht für Comma Separated Values. Es handelt sich um ein einfaches Dateiformat zum Austausch tabellarischer Daten. Die einzelnen Werte einer Zeile werden durch ein Trennzeichen voneinander getrennt.

Im deutschsprachigen Raum wird häufig nicht das Komma, sondern das Semikolon (;) als Trennzeichen verwendet. Deshalb verwendet dieses Programm:

lesen.useDelimiter(";");

Für dieses Beispiel wurde eine Excel-Datei mitarbeiter.csv erzeugt und anschließend als CSV UTF-8 (durch Trennzeichen getrennt) gespeichert. Im Editor (z. B. Notepad) besitzt die Datei folgenden Aufbau:

nachname;vorname;mitarbeiternummer;abteilung

Tanzer;Toni;1234;KA

Leoprecht;Max;3712;IT

...

Hinweis

Zum Nachvollziehen sollte sich eine Datei

mitarbeiter.csv

im angegebenen Verzeichnis befinden. Beim Speichern aus Excel sollte das Dateiformat

CSV UTF-8 (durch Trennzeichen getrennt)

gewählt werden.

Klasse EinlesenCSV mit Erläuterungen

package hsdaten;

import java.io.File;

import java.util.Scanner;

import java.io.BufferedReader;

import java.io.FileNotFoundException;

import java.io.FileReader;

import java.io.IOException;

public class EinlesenCSV {

  public static void main(String[] args) throws FileNotFoundException {

  

Konstruktor der Klasse File. Vgl. Abschnitt 6.3.

    File CSVDatei = new File("mitarbeiter.csv");

Konstruktor der Klasse Scanner. Richtet ein "Scanner-Objekt" ein, mit dem von der Konsole gelesen werden kann. Klappt nur, falls Excel-Datei als CSV/UTF-8 gespeichert ist. Er erhält als Parameter die Dateibezeichnung und die Angabe des Zeichencodes (8-Bit UCS Transformation Code).

    Scanner lesen = new Scanner(CSVDatei, "UTF-8");

Die Instanzenmethode useDelimiter() wird auf die zum Scannen bereitgestellte Datei angewandt. Hier wird das Trennzeichen zwischen den Einzeleinträgen geklärt. Vgl. die Ausgabe unten.

    lesen.useDelimiter(";");

    

    System.out.println("\nZum Testen, Ausgabe als CSV-Datei:\n");

    while (lesen.hasNext())

    {

      System.out.print(lesen.next() + " / ");

    }

    lesen.close();

  

  System.out.println("\n\nEinfache Ausgabe als Testdatei:");

  BufferedReader stud1 = null;

  String text;

  try {

    String fileName = "C:/Meine dateien/mitarbeiter.csv";

    

Hier liegen wiederum verkapselte "Konstruktor-Objekte" vor. Als Parameter von BufferedReader dient ein FileReader-Objekt.: stud1 = new BufferedReader (new FileReader(fileName ));

    System.out.println("Dateiname:" + fileName + "\n");

    stud1 = new BufferedReader (new FileReader(fileName ));

  

    } catch (FileNotFoundException fehler) {

  System.out.println("Fehler: " + fehler);

  }

Noch ein Try/Catch-Konstrukt. Diesmal mit einer Schleife zum Lesen und Ausgeben der Zeilen der Datei und für die evtl. Fehlermeldung.

  try {

    while ((text = stud1.readLine()) != null) {

      System.out.println(text);

    }

    } catch (IOException fehler) {

      System.out.println("Fehler: " + fehler);

    }

  }

}

Klasse EinlesenCSV ohne Erläuterungen

package hsdaten;

import java.io.File;

import java.io.BufferedReader;

import java.io.FileNotFoundException;

import java.io.FileReader;

import java.io.IOException;

import java.util.Scanner;

public class EinlesenCSV {

  public static void main(String[] args) throws FileNotFoundException {

    File CSVDatei = new File("mitarbeiter.csv");

    Scanner lesen = new Scanner(CSVDatei, "UTF-8");

    lesen.useDelimiter(";");

    System.out.println("\nAusgabe als CSV-Datei:\n");

    while (lesen.hasNext())

    {

      System.out.print(lesen.next() + " / ");

    }

    lesen.close();

    

    System.out.println("\n\nZum Testen, Ausgabe als Textdatei:\n");

    BufferedReader stud1 = null;

    String text;

    try {

      String fileName = "C:/Meine dateien/mitarbeiter.csv";

      System.out.println("Dateiname: " + fileName + "\n");

      stud1 = new BufferedReader(new FileReader(fileName ));

    

    } catch (FileNotFoundException fehler) {

      System.out.println("Fehler: " + fehler);

    }

    try {

      while ((text = stud1.readLine()) != null) {

      System.out.println(text);

    }

    } catch (IOException fehler) {

      System.out.println("Fehler: " + fehler);

    }

  }

}

Ausgabe

Ausgabe als CSV-Datei:

nachname / vorname / mitarbeiternummer / abteilung

Tanzer / Toni / 1234 / KA

Leoprecht / Max / 3712 / IT

Honig / Fritz / 3713 / ORG

Mälker / Kai / 9876 / PW

Staud / Josef / 1097 / IT /

Zum Testen, einfache Ausgabe als Textdatei:

Dateiname: C:/meine dateien/mitarbeiter.csv

nachname;vorname;mitarbeiternummer;abteilung

Tanzer;Toni;1234;KA

Leoprecht;Max;3712;IT

Honig;Fritz;3713;ORG

Mälker;Kai;9876;PW

Staud;Josef;1097;IT

19.8.2 Erläuterungen

Zunächst wird ein Objekt der Klasse File erzeugt:

File csvDatei = new File("C:/Meine Dateien/mitarbeiter.csv");

Dieses Objekt repräsentiert die CSV-Datei im Dateisystem. Anschließend wird ein Scanner erzeugt:

Scanner lesen = new Scanner(csvDatei, "UTF-8");

Der Scanner liest nicht von der Tastatur, sondern direkt aus der angegebenen Datei. Mit

lesen.useDelimiter(";");

wird festgelegt, dass das Semikolon als Trennzeichen zwischen zwei Datenfeldern verwendet wird. Dadurch liefert

lesen.next();

jeweils genau einen Eintrag der CSV-Datei. Im zweiten Programmteil wird dieselbe Datei mit einem BufferedReader gelesen:

BufferedReader stud1 = new BufferedReader(new FileReader(...));

Hier erfolgt keine Interpretation der CSV-Struktur. Jede Zeile wird vollständig gelesen und unverändert ausgegeben. Dadurch wird der Unterschied zwischen

  • strukturierter Verarbeitung
  • und normalem Textlesen

deutlich.

Theorieaspekte

Diese Klasse zeigt zwei unterschiedliche Sichtweisen auf dieselbe Datei. Im ersten Teil wird die CSV-Datei als strukturierte Datensammlung interpretiert. Das Trennzeichen (;) zerlegt jede Zeile in einzelne Datenfelder.

Im zweiten Teil wird dieselbe Datei als gewöhnliche Textdatei betrachtet. Es werden lediglich die einzelnen Zeilen gelesen und unverändert ausgegeben. Dies verdeutlicht den Unterschied zwischen

  • dem Lesen strukturierter Daten und
  • dem Lesen beliebiger Textdateien.

Gleichzeitig wird gezeigt, dass viele Programme (z. B. Excel) Daten im CSV-Format speichern können und dass Java verschiedene Klassen bereitstellt, um solche Dateien je nach Anwendungsfall unterschiedlich zu verarbeiten.

Eine kleine didaktische Empfehlung

Ich würde den Dateinamen in beiden Programmteilen vereinheitlichen:

String dateiname = "C:/Meine Dateien/mitarbeiter.csv";

Dann verwenden beide Leseverfahren dieselbe Variable. Das erleichtert Einsteigern das Verständnis und vermeidet doppelte Pfadangaben. Außerdem würde ich die Verzeichnisbezeichnung konsequent als C:/Meine Dateien/ schreiben (nicht einmal C:/meine dateien/ und einmal C:/Meine Dateien/), um Verwirrung zu vermeiden.

19.8.3 Betrachtete Themen

  • Ausnahmebehandlung
  • CSV-Dateien
  • Dateipfade
  • Datenaustauschformate
  • IOException
  • Klasse BufferedReader
  • Klasse File
  • Klasse FileReader
  • Klasse Scanner
  • Konsolenausgabe
  • Lesen aus Dateien
  • Methode hasNext()
  • Methode next()
  • Methode readLine()
  • Methode useDelimiter()
  • Strukturierte Daten
  • Textdateien
  • Tokenweises Lesen
  • Trennzeichen (Delimiter)
  • try-with-resources
  • Zeichencodierung UTF-8
  • Zeilenweises Lesen

 

20 Übungsaufgaben Teil 1

Die Unterteilung der Übungsaufgaben in "Teile" ist nur darstellungstechnisch bedingt. Es wäre sonst nur schwer möglich, in der Frame-basierten Version die Aufgaben vernünftig anzuordnen.

Hier nun einfache Aufgaben zum Üben der Programmierkompetenz. Durch oftmaliges Lösen solcher Aufgabenstellungen wird die Basiskompetenz erworben. Übrigens können hier auch die modernen KI-Programme hilfreich sein. Nicht nur zum Überprüfen der Lösung, sondern auch zum Stellen von Aufgaben:

Nutzen Sie die KI als Trainer, indem sie sich Aufgaben stellen lassen, zuerst einfacher, dann schwieriger.

20.1 Aufgabe 1 - Klasse und Objekt

Klasse, Objekt und Attribute

20.1.1 Aufgabenstellung

Erstellen Sie eine Klasse Aufgabe01 (Hauptprogramm) und eine Klasse Stud. Letztere erfasst Studierende und erhält folgende Attribute:

  • Matrikelnummer (int)
  • Name (String)
  • Vorname (String)

Erzeugen Sie im Hauptprogramm ein Objekt der Klasse Stud. Beschreiben Sie die Attribute dieses Objekts mit Beispielwerten und geben Sie die Daten anschließend auf der Konsole aus.

20.1.2 Lernziele

In dieser Aufgabe lernen Sie, eine eigene Klasse zu erstellen und daraus ein Objekt zu erzeugen. Sie verstehen, wie Attribute mit Werten belegt und über das Objekt angesprochen werden. Dadurch erwerben Sie die grundlegenden Kenntnisse zum Aufbau und zur Verwendung von Klassen und Objekten in der objektorientierten Programmierung.

 

20.1.3 Lösung

Klasse Stud

package Studierende;

class Stud {

  int mNr;

  String name;

  String vName;

  

  void ausgeben() {

    System.out.println(mNr + " " + vName + " " + name);

  }

}

Klasse Aufgabe01

package Studierende;

public class Aufgabe01 {

  public static void main(String[] args) {

      Stud s = new Stud();

      s.mNr = 123;

      s.name = "Maier";

      s.vName = "Klara";

      s.ausgeben();

  }

}

20.1.4 Ergebnis Programmlauf

123 Klara Maier

20.1.5 Betrachtete Themen

  • Attribute
  • Instanzattribute
  • Instanziierung mit new
  • Klassen
  • Konsolenausgabe
  • Methoden
  • Objekte (Instanzen)
  • Objektzugriff mit Punktnotation
  • Standardkonstruktor

20.1.6 Erläuterung

Mit

Stud s = new Stud();

wird ein neues Objekt der Klasse Stud erzeugt. Die Variable s enthält eine Referenz auf dieses Objekt. Über die Punktnotation werden anschließend die Attribute beschrieben:

s.mNr = 123;

s.name = "Maier";

s.vName = "Klara";

Der Methodenaufruf

s.ausgeben();

führt die Methode ausgeben() für dieses Objekt aus.

 

20.2 Aufgabe 2 - Referenzen

20.2.1 Aufgabenstellung

Wenn obiges Programm um folgende Zeilen ergänzt wird, wie sieht dann das Ergebnis aus?

Stud s1 = new Stud();

Stud s2 = s1;

s1.name = "Meier";

System.out.println(s2.name);

Wie erklärt sich das Ergebnis?

20.2.2 Lernziele

In dieser Aufgabe lernen Sie das Konzept der Objektreferenzen in Java kennen. Sie verstehen, dass Referenzvariablen auf Objekte verweisen und dass mehrere Referenzvariablen auf dasselbe Objekt zeigen können. Dadurch vertiefen Sie Ihr Verständnis für den Umgang mit Objekten und Referenzen in der objektorientierten Programmierung.

 

20.2.3 Lösung

Klasse Aufgabe02

package studierende;

public class Aufgabe02 {

  public static void main(String[] args) {

    Stud s = new Stud();

    s.mNr = 123;

    s.name = "Maier";

    s.vName = "Klara";

    

    s.ausgeben();

    

    Stud s1 = new Stud();

    Stud s2 = s1;

    s1.name = "Meier";

    System.out.println(s2.name);

  }

}

Klasse Stud

package studierende;

class Stud {

  int mNr;

  String name;

  String vName;

  

  void ausgeben() {

    System.out.println(mNr + " " + vName + " " + name);

  }

}

Begründung

Die Variablen s1 und s2 enthalten keine Objekte, sondern Referenzen auf Objekte. Durch die Anweisung

Stud s2 = s1;

wird keine Kopie des Objekts erzeugt. Stattdessen zeigen beide Variablen auf dasselbe Objekt im Speicher. Wird das Objekt über s1 verändert,

s1.name = "Meier";

so ist die Änderung auch über s2 sichtbar:

System.out.println(s2.name);

Deshalb wird

Meier

ausgegeben.

20.2.4 Ergebnis Programmlauf

123 Klara Maier

Meier

20.2.5 Betrachtete Themen

  • Gemeinsamer Zugriff auf Objekte
  • Instanziierung mit new
  • Mehrere Referenzen auf dasselbe Objekt
  • Objekte
  • Objektvariablen
  • Punktnotation
  • Referenzen
  • Referenzvariablen
  • Speichermodell von Objekten
  • Zuweisung von Referenzen

20.2.6 Erläuterung

Bei primitiven Datentypen wird bei einer Zuweisung der Wert kopiert:

int a = 5;

int b = a;

Danach besitzen a und b jeweils ihren eigenen Wert. Bei Objekten ist dies anders:

Stud s1 = new Stud();

Stud s2 = s1;

Hier wird nicht das Objekt kopiert, sondern nur die Referenz. Dadurch zeigen beide Variablen auf dasselbe Objekt.

 

20.3 Aufgabe 3a - Arrays Einführung

20.3.1 Aufgabenstellung

In einer Arztpraxis sollen die Gewichte mehrerer Patienten verwaltet werden. Erstellen Sie eine Klasse Aufgabe03a mit einer main()-Methode und realisieren Sie folgende Aufgaben:

  • Legen Sie ein Array an, das die Gewichte von fünf Patienten speichern kann.
  • Tragen Sie folgende Gewichte in das Array ein:

 


Patient

Gewicht

1

72

2

85

3

64

4

91

5

78

 

  • Ändern Sie anschließend das Gewicht des dritten Patienten auf 66 kg.
  • Geben Sie danach alle Gewichte auf dem Bildschirm aus.
  • Berechnen Sie zusätzlich das Durchschnittsgewicht aller Patienten und geben Sie dieses aus.

Die Bildschirmausgabe soll so aussehen:

Gewichte der Patienten:

Patient 1: 72 kg

Patient 2: 85 kg

Patient 3: 66 kg

Patient 4: 91 kg

Patient 5: 78 kg

Durchschnittsgewicht: 78.4 kg

20.3.2 Lernziele

In dieser Aufgabe lernen Sie, Arrays in Java anzulegen und zu verwenden. Sie verstehen, wie Werte in einem Array gespeichert, über ihren Index angesprochen und mit einer Schleife ausgegeben werden. Dadurch erwerben Sie die Grundlagen für die spätere Verwaltung von Objekten in Arrays.

20.3.3 Lösung

public class Aufgabe03a {

    public static void main(String[] args) {

Array für fünf Gewichte anlegen

      int[] gewichte = new int[5];

Werte eintragen

      gewichte[0] = 72;

      gewichte[1] = 85;

      gewichte[2] = 64;

      gewichte[3] = 91;

      gewichte[4] = 78;

Gewicht des dritten Patienten ändern

      gewichte[2] = 66;

Ausgabe aller Gewichte

      System.out.println("Gewichte der Patienten:");

      for (int i = 0; i < gewichte.length; i++) {

        System.out.println(

        "Patient " + (i + 1) + ": "

        + gewichte[i] + " kg");

      }

Durchschnitt berechnen

      int summe = 0;

      for (int gewicht : gewichte) {

        summe += gewicht;

      }

      double durchschnitt =

        (double) summe / gewichte.length;

      System.out.println(

        "\nDurchschnittsgewicht: "

        + durchschnitt + " kg");

    }

}

20.3.4 Betrachtete Themen

  • Array-Deklaration
  • Array-Elemente ändern
  • Array-Elemente lesen
  • Array-Erzeugung mit new
  • Array-Indizes
  • Arrays
  • Attribut length
  • Datentyp int
  • Durchschnittsberechnung
  • For-Schleife
  • Iteration über Datenstrukturen
  • Konsolenausgabe
  • Primitive Datentypen
  • Schleifen
  • Summenbildung
  • Zählschleife (for)

20.3.5 Erläuterung

Betrachten wir folgende Anweisung:

int[] gewichte = new int[5];

Sie besteht aus mehreren Teilen.

  • Datentyp int

int ist ein primitiver Datentyp für ganze Zahlen.

  • Die eckigen Klammern []

int[]

Die eckigen Klammern zeigen an, dass mehrere Werte gleichen Typs gespeichert werden sollen.

Vergleich:

int gewicht; stellt eine einzelne Zahl dar.

int[] gewichte; entspricht mehreren Zahlen. Man spricht von einem Array von Integer-Werten.

  • Der Variablenname

gewichte

Dies ist der Name der Variablen. Die Variable enthält später einen Verweis auf das Array.

  • Das Schlüsselwort new

new

fordert Java auf, ein neues Objekt anzulegen. Auch Arrays sind in Java Objekte. Deshalb muss Speicher reserviert werden. Alle Elemente werden zunächst automatisch mit 0 gefüllt.

  • Der Array-Typ

new int[5]

Hier wird festgelegt:

  • Typ der Elemente: int
  • Anzahl der Elemente: 5

Java reserviert Speicher für fünf Integer-Werte.

  • Die Größe des Arrays

[5]

bedeutet: 5 Elemente und nicht "letzter Index = 5". Die Indizes beginnen bei 0. Deshalb sind die gültigen Indizes: 0, 1, 2, 3, 4.

Ein Array besitzt nach seiner Einrichtung immer eine feste Länge, im Unterschied zu Listen, die später betrachtet werden.

Zugriff auf einzelne Elemente

gewichte[0] = 72;

gewichte[1] = 85;

gewichte[2] = 64;

gewichte[3] = 91;

gewichte[4] = 78;

Lesen eines Wertes

System.out.println(gewichte[2]);

führt zur Ausgabe:

64

Der Index 2 bezeichnet das dritte Element.

 

Ändern eines Wertes

Mit

gewichte[2] = 66;

wird der bisherige Wert überschrieben.

20.3.6 Hinweis - Arrays von Objekten

Oben wurden Zahlen gespeichert:

int[] gewichte = new int[5];

In objektorientierten Programmen werden häufig Objekte gespeichert:

Stud[] studierende = new Stud[5];

oder

Note[] noten = new Note[10];

Man spricht dann von Objektarrays oder Referenzarrays. Die nächste Aufgabe zeigt, wie Objekte in einem Array gespeichert und verarbeitet werden.

 

20.4 Aufgabe 3b - Arrays mit Objekten

20.4.1 Aufgabenstellung

Wir benötigen die Klassen Stud und Aufgabe03b (als HP).

Erzeugen Sie ein Array für drei Objekte der Klasse Stud. Erzeugen Sie anschließend in einer Schleife für jedes Arrayelement ein neues Objekt der Klasse Stud und speichernSie es im Array.

Weisen Sie jedem Objekt einen Namen zu. Der Name soll aus dem Text "Studierende" und einem Zähler bestehen, also beispielsweise: Studierende1, Studierende2, Studierende3.

Geben Sie danach mit einer forEach-Schleife die Namen aller gespeicherten Objekte auf der Konsole aus. Das sollte zu folgendem Ergebnis führen:

Studierende1

Studierende2

Studierende3

20.4.2 Lernziele

In dieser Aufgabe lernen Sie, Objekte in einem Array zu verwalten. Sie verstehen, wie Objektreferenzen in einem Array gespeichert, die einzelnen Objekte in einer Schleife erzeugt und anschließend mit einer forEach-Schleife ausgegeben werden.

 

20.4.3 Lösung

Klasse Aufgabe03b

package studierende;

public class Aufgabe03 {

  public static void main(String[] args) {

Erzeugung eines Arrays für drei Objekte (Studierende), noch ohne Daten: null

    Stud[] arr = new Stud[3];

Schleife von 0 bis 2

    for (int i = 0; i < arr.length; i++) {

Neues Objekt erzeugen und im Array speichern

      arr[i] = new Stud();

Zugriff auf das Objekt, Name eintragen

      arr[i].name = "Name" + (i+1);

    }

ForEach-Schleife über alle Elemente des Arrays. s ist jeweils ein Stud-Objekt

    for (Stud s : arr) {

      System.out.println(s.name);

    }

  }

}

Klasse Stud

package studierende;

class Stud {

    String name;

}

20.4.4 Ergebnis Programmlauf

Name1

Name2

Name3

20.4.5 Betrachtete Themen

  • Array-Indizes
  • Arrays
  • ForEach-Schleife
  • For-Schleife
  • Instanziierung mit new
  • Iteration über Datenstrukturen
  • Objektarrays
  • Objekte in Arrays
  • Objektverwaltung mit Arrays
  • Punktnotation
  • Referenzen in Arrays
  • Schleifen
  • Zugriff auf Objekte über Arrayelemente

20.4.6 Erläuterung

In Aufgabe 3a wurden Zahlen in einem Array gespeichert:

int[] gewichte = new int[5];

Die Arrayelemente enthielten direkt die Werte. In dieser Aufgabe werden dagegen Objekte gespeichert:

Stud[] arr = new Stud[3];

Dabei entsteht zunächst nur das Array. Die Elemente enthalten anfangs:

null

null

null

Es existieren also noch keine Studierendenobjekte. Erst durch

arr[i] = new Stud();

wird für jedes Arrayelement ein eigenes Objekt erzeugt. Danach zeigt jedes Arrayelement auf ein Objekt der Klasse Stud. Vereinfacht dargestellt:

arr[0] --> Stud

arr[1] --> Stud

arr[2] --> Stud

 

21 Übungsaufgaben Teil 2

21.1 Aufgabe 4a - Klassen ohne Beziehung

Mehrere Klassen ohne Beziehung

Die folgenden Aufgaben behandeln das Zusammenspiel verschiedener Klassen. In dieser ersten Version existieren die Klassen Stud und Note nebeneinander. Es gibt noch keine Beziehung zwischen ihnen. Die Noten werden über die Matrikelnummern den Studierenden zugeordnet.

21.1.1 Aufgabenstellung

Erstellen Sie eine Klasse Aufgabe04a (Hauptprogramm) sowie die Klassen Stud und Note.

Die Klasse Stud soll folgende Attribute besitzen:

  • Matrikelnummer (int)
  • Name (String)
  • Vorname (String)

Die Klasse Note soll folgende Attribute besitzen:

  • Fach (String)
  • Note (int)
  • Matrikelnummer des Studierenden (int)

Legen Sie im Hauptprogramm mindestens zwei Studierende und mehrere Noten an. Speichern Sie bei jeder Note die Matrikelnummer des zugehörigen Studierenden.

Geben Sie anschließend für jeden Studierenden alle zugehörigen Noten aus. Die Zuordnung soll dabei ausschließlich über die Matrikelnummer erfolgen.

Hinweise

  • Die Matrikelnummer wird in der Klasse Note als normales Attribut gespeichert.
  • Zwischen den Klassen Stud und Note besteht noch keine Objektverknüpfung.
  • Die Zuordnung erfolgt ausschließlich durch den Vergleich der Matrikelnummern.

21.1.2 Lernziele

In dieser Aufgabe lernen Sie, mehrere Klassen in einem Java-Programm gemeinsam zu verwenden. Sie verstehen, wie Objekte verschiedener Klassen erzeugt und im Hauptprogramm zusammen eingesetzt werden. Dadurch erwerben Sie die Grundlagen für die spätere Modellierung von Objektbeziehungen.

Zusammen mit den folgenden Aufgaben sollte sich ihnen der Unterschied zwischen einer datenbankbasierten Verknüpfung (so wie hier) und einer objektorientierten (wie in den folgenden Aufgaben) erschließen.

21.1.3 Ergebnis Programmlauf

Paula Maier hat folgende Noten:

Mathe: 2

Informatik: 1

Anna Schulz hat folgende Noten:

Mathe: 3

 

21.1.4 Lösung

package studierende;

public class Aufgabe04a {

  public static void main(String[] args) {

    Stud s1 = new Stud(1001, "Maier", "Paula");

    Stud s2 = new Stud(1002, "Schulz", "Anna");

    

    Note n1 = new Note("Mathe", 2, 1001);

    Note n2 = new Note("Informatik", 1, 1001);

    Note n3 = new Note("Mathe", 3, 1002);

    

    Note[] alleNoten = { n1, n2, n3 };

    

    notenAusgeben(s1, alleNoten);

    System.out.println();

    

    notenAusgeben(s2, alleNoten);

  }

  

  static void notenAusgeben(Stud s, Note[] alleNoten) {

    System.out.println(s.vName + " " + s.name

      + " hat folgende Noten:");

    

    for (Note n : alleNoten) {

      if (n.mNr == s.mNr) {

        System.out.println(n.fach + ": " + n.note);

      }

    }

  }

}

class Stud {

  int mNr;

  String name;

  String vName;

  

  Stud(int mNr, String name, String vName) {

    this.mNr = mNr;

    this.name = name;

    this.vName = vName;

  }

}

class Note {

  String fach;

  int note;

  int mNr;

  Note(String fach, int note, int mNr) {

    this.fach = fach;

    this.note = note;

    this.mNr = mNr;

  }

}

21.1.5 Betrachtete Themen

  • Attribute
  • forEach-Schleife
  • Instanziierung mit new
  • Konstruktoren
  • Matrikelnummer als Zuordnungskriterium
  • Mehrere Klassen
  • Objektarrays
  • Objekte
  • Objektorientierte Modellierung ohne Objektverknüpfung
  • Parameterübergabe an Konstruktoren
  • Suche in einem Array
  • Vergleich von Attributwerten
  • Zuordnung über Identifikator
  • Zusammenarbeit mehrerer Klassen im selben Projekt

21.1.6 Erläuterung

Mit dieser Aufgabe wird gelernt,

  • mehrere Klassen in einem Programm zu verwenden,
  • Objekte anzulegen und mit Attributwerten zu versehen,
  • Daten über einen gemeinsamen Identifikator (Matrikelnummer) miteinander zu verknüpfen,
  • Objekte mit Hilfe einer Schleife zu durchsuchen und passende Datensätze auszuwählen.

Diese Lösung entspricht dem Vorgehen in Datenbanksystemen, bei denen Datensätze häufig über Schlüsselwerte miteinander verbunden werden. Eine direkte Objektverknüpfung zwischen Studierenden und Noten wird in den folgenden Aufgaben eingeführt.

21.2 Aufgabe 4b -Einseitig

Einseitige objektorientierte 1:n-Beziehung zwischen Studierenden und Noten

21.2.1 Aufgabenstellung

In Aufgabe 4a wurden Studierende und Noten lediglich über die Matrikelnummer miteinander verbunden. Nun soll erstmals eine direkte Beziehung zwischen Objekten eingerichtet werden. Erweitern Sie die Lösung aus Aufgabe 4a. Ein Studierender soll mehrere Noten besitzen können.

Ergänzen Sie deshalb die Klasse Stud um eine geeignete Datenstruktur zur Speicherung mehrerer Noten. Legen Sie mindestens drei Studierende und mehrere Noten an und ordnen Sie die Noten den jeweiligen Studierenden zu. Geben Sie anschließend für jeden Studierenden alle gespeicherten Noten aus.

Hinweise

  • Die Noten werden direkt beim Studierenden gespeichert.
  • Zwischen den Klassen Stud und Note besteht eine einseitige Beziehung.
  • Von einem Studierenden kann auf seine Noten zugegriffen werden.
  • Von einer Note kann noch nicht auf den zugehörigen Studierenden geschlossen werden.

21.2.2 Lernziele

In dieser Aufgabe lernen Sie, eine einseitige 1:n-Beziehung zwischen zwei Klassen in Java zu realisieren. Sie verstehen, wie ein Objekt mehrere zugehörige Objekte verwalten kann und wie diese Beziehung über Objektreferenzen hergestellt wird. Dadurch vertiefen Sie Ihr Verständnis für die Modellierung einfacher Objektbeziehungen in der objektorientierten Programmierung.

21.2.3 Ergebnis Programmlauf

Paul Maier

Mathe: 2

Informatik: 1

Anna Schulz

Mathe: 3

Lisa Huber

Programmierung: 1

Datenbanken: 2

 

21.2.4 Lösung

Klasse Stud

package studierende;

import java.util.ArrayList;

import java.util.List;

class Stud {

    int matrNr;

    String name;

    String vName;

===== VERKNÜPFUNG =====
Zu einem Studierenden gehören mehrere Noten.

    List<Note> noten = new ArrayList<>();

    Stud(int matrNr, String name, String vName) {

      this.matrNr = matrNr;

      this.name = name;

      this.vName = vName;

    }

===== VERKNÜPFUNG =====
Fügt eine Note zur Liste hinzu.

    void addNote(Note n) {

      noten.add(n);

    }

    void ausgeben() {

      System.out.println(vName + " " + name);

      for (Note n : noten) {

        System.out.println(n.fach + ": " + n.note);

      }

  }

}

Klasse Note

package studierende;

class Note {

    String fach;

    int note;

    Note(String fach, int note) {

      this.fach = fach;

      this.note = note;

    }

}

Klasse Aufgabe04b - Hauptprogramm

package studierende;

public class Aufgabe04b {

  public static void main(String[] args) {

    Stud s1 = new Stud(1001, "Maier", "Paul");

    Stud s2 = new Stud(1002, "Schulz", "Anna");

    Stud s3 = new Stud(1003, "Huber", "Lisa");

    

    s1.addNote(new Note("Mathe", 2));

    s1.addNote(new Note("Informatik", 1));

    

    s2.addNote(new Note("Mathe", 3));

    

    s3.addNote(new Note("Programmierung", 1));

    s3.addNote(new Note("Datenbanken", 2));

    

    s1.ausgeben();

    System.out.println();

    

    s2.ausgeben();

    System.out.println();

    

    s3.ausgeben();

  }

}

21.2.5 Ergebnis Programmlauf

Siehe Aufgabenstellung.

21.2.6 Betrachtete Themen

  • 1:n-Beziehungen
  • ArrayList
  • Assoziationen
  • ForEach-Schleife
  • Generics (List<Note>)
  • Hinzufügen von Objekten zu Listen
  • List
  • Mehrere Klassen
  • Navigation zwischen Objekten
  • Objekte
  • Objekte als Attribute
  • Objektorientierte Modellierung
  • Objektreferenzen
  • Sammlungen von Objekten
  • Unidirektionale Beziehungen

In dieser Aufgabe wird erstmals eine einseitige Assoziation realisiert. Den Studierenden sind Noten zugeordnet. Von den Noten kann aber (noch) nicht auf die Studierenden geschlossen werden.

21.2.7 Erläuterung

Erstmals wird eine Beziehung eingerichtet. Die Klasse Stud enthält eine Liste von Noten:

class Stud {

  List<Note> noten = new ArrayList<>();

}

Dadurch kann ein Studierender beliebig viele Noten verwalten. Die Methode

void addNote(Note n)

fügt eine weitere Note hinzu. Beispielsweise:

s1.addNote(new Note("Mathe", 2));

Dadurch entsteht eine direkte Objektverknüpfung.

21.2.8 Exkurs - Von Arrays zu Listen

Oftmals macht die Unterscheidung von Arrays und Listen Probleme. Deshalb dieser Exkurs.

Bei List<Note> noten = new ArrayList<>(); kommen gegenüber int[] gewichte = new int[5]; mehrere neue Konzepte hinzu.

1. Feste Größe oder dynamische Größe

Beim Array:

int[] gewichte = new int[5];

ist die Größe fest vorgegeben: 5 Elemente. Später können keine weiteren Elemente hinzugefügt werden. Bei

List<Note> noten = new ArrayList<>();

wird eine dynamische Liste angelegt. Diese wächst automatisch:

noten.add(n1);

noten.add(n2);

noten.add(n3);

Man muss die Größe nicht vorher kennen.

2.Gespeicherte Elemente

Beim Array:

int[] gewichte

werden Integer-Werte gespeichert. Bei

List<Note> noten

werden Objekte der Klasse Note gespeichert. Die Liste enthält also Verweise auf Notenobjekte.

3. Das Interface List

List<Note>

bedeutet:

  • Variable heißt noten
  • Sie verwaltet mehrere Objekte vom Typ Note
  • Die Programmierung erfolgt über das Interface List

Vereinfacht kann man sagen: List ist ein Behälter für beliebig viele Objekte.

4. Generische Typen

<Note>

legten fest, welche Objekte gespeichert werden dürfen. Beispiele:

List<String> namen;

... speichert Zeichenketten

List<Integer> zahlen;

... speichert ganze Zahlen

List<Stud> studierende;

... speichert Studierende

List<Note> noten;

... speichert Noten

5. Das Objekt ArrayList ...

new ArrayList<>()

... erzeugt die eigentliche Liste. Man könnte auch schreiben:

new ArrayList<Note>()

Seit Java 7 genügt meist:

new ArrayList<>()

da Java den Typ automatisch erkennt.

Vergleich der beiden Varianten


Array

Liste

feste Größe

wächst automatisch

Anzahl vorher festlegen

beliebig viele Elemente

Zugriff mit [ ]

Zugriff mit get()

primitive Datentypen und Objekte

meist Objekte

keine Komfortmethoden

viele Komfortmethoden

 

Beispiele: gewichte[2] und noten.get(2).

 

21.3 Aufgabe 4c - Bidirektional

Bidirektionale 1: n-Beziehung zwischen Studierenden und Noten

21.3.1 Aufgabenstellung

Nun wird, im Vergleich zur vorigen Aufgabe, die Gegenrichtung ergänzt: Von einer Note soll auch auf den zugehörigen Studierenden geschlossen werden. Erweitern Sie dazu die Lösung aus Aufgabe 4b. Bisher "kennt" ein Studierender seine Noten. Nun soll zusätzlich jede Note "wissen", zu welchem Studierenden sie gehört. Ergänzen Sie dazu die Klasse Note um ein geeignetes Attribut, das auf ein Objekt der Klasse Stud verweist. Richten Sie die Beziehungen beim Erzeugen der Objekte entsprechend ein. Realisieren Sie anschließend folgende Ausgaben:

  • Geben Sie für alle Studierenden ihre Noten aus.
  • Geben Sie alle Studierenden aus, die im Fach Informatik die Note 1 erhalten haben.

21.3.2 Lernziele

In dieser Aufgabe lernen Sie, eine bidirektionale 1:n-Beziehung zwischen zwei Klassen in Java zu realisieren. Sie verstehen, wie sich verknüpfte Objekte gegenseitig referenzieren und wie dadurch der Zugriff auf die Beziehung von beiden Seiten aus möglich wird. Dadurch vertiefen Sie Ihr Verständnis für bidirektionale Objektbeziehungen in der objektorientierten Programmierung.

 

21.3.3 Lösung

Klasse Stud

package studierende;

import java.util.ArrayList;

import java.util.List;

class Stud {

  int matrNr;

  String name;

  String vName;

===== VERKNÜPFUNG =====
Stud -> Note
Ein Studierender "kennt" seine Noten.

  List<Note> noten = new ArrayList<>();

  Stud(int matrNr, String name, String vName) {

    this.matrNr = matrNr;

    this.name = name;

    this.vName = vName;

  }

===== VERKNÜPFUNG =====
Fügt eine Note zur Notenliste hinzu.

  void addNote(Note n) {

    noten.add(n);

  }

  void ausgeben() {

    System.out.println(vName + " " + name);

    

    for (Note n : noten) {

      System.out.println(n.fach + ": " + n.note);

    }

  }

}

Klasse Note

package studierende;

class Note {

  String fach;

  int note;

  

===== VERKNÜPFUNG =====
Note -> Stud

  Stud student;

  Note(String fach, int note, Stud student) {

      this.fach = fach;

      this.note = note;

===== VERKNÜPFUNG =====

      this.student = student;

  }

}

Hauptprogramm

package studierende;

public class Aufgabe04c {

  public static void main(String[] args) {

  

  Stud s1 = new Stud(1001, "Maier", "Paul");

  Stud s2 = new Stud(1002, "Schmidt", "Anna");

  Stud s3 = new Stud(1003, "Huber", "Lisa");

  

===== VERKNÜPFUNG =====
Beim Erzeugen wird der oder die Studierende "mit der Note gespeichert".

  Note n1 = new Note("Mathe", 2, s1);

  Note n2 = new Note("Informatik", 1, s1);

  

  Note n3 = new Note("Mathe", 3, s2);

  Note n4 = new Note("Informatik", 2, s2);

  

  Note n5 = new Note("Informatik", 1, s3);

  Note n6 = new Note("Datenbanken", 2, s3);

  

===== VERKNÜPFUNG =====
Zusätzlich werden die Noten beim Studierenden gespeichert.

  s1.addNote(n1);

  s1.addNote(n2);

  

  s2.addNote(n3);

  s2.addNote(n4);

  

  s3.addNote(n5);

  s3.addNote(n6);

  

  Stud[] alleStudierenden = { s1, s2, s3 };

  Note[] alleNoten = { n1, n2, n3, n4, n5, n6 };

  

  System.out.println("Alle Studierenden mit ihren Noten:\n");

  

  for (Stud s : alleStudierenden) {

    s.ausgeben();

    System.out.println();

  }

    

    System.out.println("Studierende mit Note 1 in Informatik:\n");

    

    for (Note n : alleNoten) {

    

      if (n.fach.equals("Informatik") amp;amp; n.note == 1) {

      System.out.println(

        n.student.vName + " " + n.student.name);

      }

    }

  }

}

21.3.4 Ergebnis Programmlauf

Alle Studierenden mit ihren Noten:

Paul Maier

Mathe: 2

Informatik: 1

Anna Schmidt

Mathe: 3

Informatik: 2

Lisa Huber

Informatik: 1

Datenbanken: 2

Studierende mit Note 1 in Informatik:

Paul Maier

Lisa Huber

21.3.5 Betrachtete Themen

  • 1:n-Beziehungen
  • Assoziationen
  • Bidirektionale Beziehungen
  • ForEach-Schleifen
  • Generics (List<Note>)
  • Kombinierte Bedingungen mit amp;amp;
  • Konsistenz von Beziehungen
  • Listen (ArrayList)
  • Mehrere Klassen
  • Navigation in beide Richtungen
  • Objektarrays
  • Objekte
  • Objekte als Attribute
  • Objektorientierte Modellierung
  • Objektreferenzen
  • Suche in Objektbeständen
  • Vergleich von Zeichenketten mit equals()

21.3.6 Theorieaspekt

Eine Beziehung kann in Java in beiden Klassen hinterlegt werden. In der Klasse Stud wird gespeichert, welche Noten ein Studierender besitzt:

class Stud {

  List<Note> noten;

}

In der Klasse Note wird gespeichert, zu welchem Studierenden die Note gehört:

class Note {

  Stud student;

}

Damit kann man in beide Richtungen navigieren:

stud.noten

und

note.student

Die Beziehung ist damit bidirektional.

Diese Aufgabe zeigt den Unterschied zwischen einer einseitigen und einer zweiseitigen Objektbeziehung. In Aufgabe 4b war nur diese Navigation möglich:

Stud -> Note

Nun ist zusätzlich möglich:

Note -> Stud

Dadurch kann man nicht nur alle Noten eines Studierenden ausgeben, sondern auch von einer bestimmten Note zurück zum zugehörigen Studierenden navigieren.

Besonders wichtig ist dabei die Konsistenz der Beziehung: Die Note muss beim Studierenden eingetragen sein, und zugleich muss die Note den Studierenden "kennen".

 

21.4 Aufgabe 4d - n:m-Beziehung

n:m-Beziehung zwischen Angestellten und Projekten

21.4.1 Aufgabe

Erstellen Sie die Klassen Angestellter, Projekt und Aufgabe04d (Hauptprogramm). Die Klasse Angestellter erhält folgende Attribute:

  • Personalnummer (persNr, int)
  • Name (name, String)
  • Abteilung (abteilung, String)

Festgehalten wird auch, in welchen Projekten die oder der Angestellte mitarbeitet.

Die Klasse Projekt erhält folgende Attribute:

  • Projektnummer (projNr, int)
  • Projektbezeichnung (projBez, String)

Hier wird festgehalten, wer im jeweiligen Projekt mitarbeitet. Damit ist dann die m:n-Beziehung erfasst.

Legen Sie im Hauptprogramm mehrere Angestellte und mehrere Projekte an. Stellen Sie anschließend Beziehungen zwischen den Objekten her. Realisieren Sie die Erfassung der Projekte bei den Angestellten und der Angestellten bei den Projekten durch geeignete Methoden.

Geben Sie danach

  • alle Projekte eines ausgewählten Angestellten und
  • alle Angestellten eines ausgewählten Projekts

aus.

21.4.2 Lernziele

In dieser Aufgabe lernen Sie, eine bidirektionale n:m-Beziehung zwischen zwei Klassen in Java zu realisieren. Sie verstehen, wie Objekte beider Klassen mehrere Verknüpfungen zu Objekten der jeweils anderen Klasse verwalten und wie diese Beziehungen von beiden Seiten aus genutzt werden können. Dadurch vertiefen Sie Ihr Verständnis für die Modellierung komplexerer Objektbeziehungen in der objektorientierten Programmierung.

21.4.3 Lösung

Klasse Angestellter

package firma;

import java.util.ArrayList;

import java.util.List;

class Angestellter {

    int persNr;

    String name;

    String abteilung;

Liste der Projekte des Angestellten

    List<Projekt> projekte = new ArrayList<>();

    Angestellter(int persNr, String name, String abteilung) {

      this.persNr = persNr;

      this.name = name;

      this.abteilung = abteilung;

    }

    void addProjekt(Projekt p) {

      projekte.add(p);

    }

}

Klasse Projekt

package firma;

import java.util.ArrayList;

import java.util.List;

class Projekt {

    int projNr;

    String projBez;

Liste der beteiligten Angestellten

    List<Angestellter> angestellte = new ArrayList<>();

    Projekt(int projNr, String projBez) {

      this.projNr = projNr;

      this.projBez = projBez;

    }

    void addAngestellter(Angestellter a) {

      angestellte.add(a);

    }

}

Hauptprogramm Aufgabe04d

package firma;

public class Aufgabe04d {

    public static void main(String[] args) {

      Angestellter a1 =

        new Angestellter(1001, "Müller", "Entwicklung");

      Angestellter a2 =

        new Angestellter(1002, "Schneider", "Vertrieb");

      Projekt p1 =

        new Projekt(2001, "BPMN");

      Projekt p2 =

        new Projekt(2002, "Prozessoptimierung");

Verknüpfungen herstellen

      a1.addProjekt(p1);

      p1.addAngestellter(a1);

      a1.addProjekt(p2);

      p2.addAngestellter(a1);

      a2.addProjekt(p1);

      p1.addAngestellter(a2);

Projekte von a1 ausgeben

      System.out.println("Projekte von "

        + a1.name + ":");

      for (Projekt p : a1.projekte) {

        System.out.println(p.projBez);

      }

      System.out.println();

Angestellte von p1 ausgeben

      System.out.println("Mitarbeiter im Projekt "

        + p1.projBez + ":");

      for (Angestellter a : p1.angestellte) {

        System.out.println(a.name);

      }

  }

}

21.4.4 Ergebnis Programmlauf

Projekte von Müller:

BPMN

Prozessoptimierung

Mitarbeiter im Projekt BPMN:

Müller

Schneider

21.4.5 Betrachtete Themen

  • ArrayList
  • Ausgabe verknüpfter Objekte
  • Bidirektionale Beziehung
  • foreach-Schleife
  • List
  • Mehrere Klassen
  • Mehrwertige Attribute
  • Methoden zum Herstellen von Beziehungen
  • n:m-Beziehung
  • Navigation in beide Richtungen
  • Objekte als Listenelemente
  • Objekte und Objektreferenzen

21.4.6 Anmerkung

Besonders wichtig ist hier:

List<Projekt> projekte = new ArrayList<>();

und

List<Angestellter> angestellte = new ArrayList<>();

Damit wird die n:m-Beziehung in beiden Klassen gespeichert:

  • Ein Angestellter kann in mehreren Projekten mitarbeiten.
  • Ein Projekt kann mehrere Angestellte haben.

Die Methoden

void addProjekt(Projekt p)

und

void addAngestellter(Angestellter a)

stellen die Beziehung zwischen den Objekten her.

21.5 Aufgabe 4e - Assoziationsklasse

n:m-Beziehung mit Assoziationsklasse und Beziehungsatttribut

In dieser Aufgabe 4e wird dieselbe inhaltliche Situation wie in der vorigen Aufgabe mit einer Assoziationsklasse modelliert. Dann gibt es keine direkten Listen List<Projekt> bzw. List<Angestellter> mehr, sondern Objekte der Assoziationsklasse (z. B. Mitarbeit), die die Beziehung repräsentieren. Dadurch können auch Beziehungsattribute wie Rolle, Eintrittsdatum oder Arbeitsstunden gespeichert werden.

Assoziationsklasse

21.5.1 Aufgabe

Erfasst werden soll die Mitarbeit von Angestellten in Projekten. Ein Angestellter kann an mehreren Projekten beteiligt sein. Gleichzeitig arbeiten in jedem Projekt mehrere Angestellte mit. Es liegt also eine n:m-Bezuiehung vor.

Zusätzlich soll durch ein Attribut Mitarbeit gespeichert werden, wie viele Stunden ein Angestellter bereits für ein bestimmtes Projekt geleistet hat.

Erstellen Sie die folgenden Klassen:

Angestellter

  • persNr (int)
  • name (String)
  • abteilung (String)

Projekt

  • projNr (int)
  • projBez (String)

Mitarbeit

  • angestellter (Angestellter)
  • projekt (Projekt)
  • stunden (int)

Legen Sie im Hauptprogramm mindestens zwei Angestellte und zwei Projekte an. Nutzen Sie dafür Konstruktoren mit Parameterliste. Erzeugen Sie anschließend mehrere Objekte für die Klasse Mitarbeit, um die Beteiligung der Angestellten an den Projekten abzubilden, etwa so wie im Beispielsergebnis.

Geben Sie alle Einträge in Mitarbeit aus, z.B. so:

Müller arbeitet im Projekt BPMN mit.

Geleistete Stunden: 120

Müller arbeitet im Projekt Prozessoptimierung mit.

Geleistete Stunden: 40

Meier arbeitet im Projekt BPMN mit.

Geleistete Stunden: 60

Meier arbeitet im Projekt Prozessoptimierung mit.

Geleistete Stunden: 75

21.5.2 Lernziele

In dieser Aufgabe lernen Sie, eine n:m-Beziehung mit einer Assoziationsklasse zu realisieren. Sie verstehen, wie zusätzliche Informationen zur Beziehung - beispielsweise die Anzahl der geleisteten Stunden eines Mitarbeiters in einem Projekt - in einer eigenen Klasse gespeichert werden. Dadurch vertiefen Sie Ihr Verständnis für die Modellierung komplexer Objektbeziehungen in Java und UML.

Dies ist wiederum eine datenbankorientierte Lösung des Problems, Daten miteinander Zu verknüpfen, die in einer n:m-Beziehung stehen und deren Beziehung auch Attribute aufweist. Vgl. meine Texte zu Relationalen Datenbanken auf diesen Web-Seiten und in [Staud 2021]

21.5.3 Lösung

Klasse Angestellter

package firma;

class Angestellter {

    int persNr;

    String name;

    String abteilung;

    Angestellter(int persNr, String name, String abteilung) {

      this.persNr = persNr;

      this.name = name;

      this.abteilung = abteilung;

    }

}

Klasse Projekt

package firma;

class Projekt {

    int projNr;

    String projBez;

    Projekt(int projNr, String projBez) {

      this.projNr = projNr;

      this.projBez = projBez;

    }

}

Klasse Mitarbeit

package firma;

 

class Mitarbeit {

    Angestellter angestellter;

    Projekt projekt;

    int stunden;

 

    Mitarbeit(Angestellter angestellter, Projekt projekt,

         int stunden) {

      this.angestellter = angestellter;

      this.projekt = projekt;

      this.stunden = stunden;

    }

}

Hauptprogramm Aufgabe04d

package firma;

public class Aufgabe04d {

  public static void main(String[] args) {

Angestellte erzeugen

    Angestellter a1 = new Angestellter(1001, "Müller", "IT");

    Angestellter a2 = new Angestellter(1002, "Meier", "Controlling");

Projekte erzeugen

    Projekt p1 = new Projekt(2001, "BPMN");

    Projekt p2 = new Projekt(2002, "Prozessoptimierung");

Mitarbeiten erzeugen

    Mitarbeit m1 = new Mitarbeit(a1, p1, 120);

    Mitarbeit m2 = new Mitarbeit(a1, p2, 40);

    Mitarbeit m3 = new Mitarbeit(a2, p1, 60);

    Mitarbeit m4 = new Mitarbeit(a2, p2, 75);

Ausgabe

    Mitarbeit[] liste = { m1, m2, m3, m4 };

    for (Mitarbeit m : liste) {

      System.out.println(m.angestellter.name + " arbeitet im Projekt " + m.projekt.projBez + " mit.");

      System.out.println("Geleistete Stunden: " + m.stunden);

      System.out.println();

    }

  }

}

21.5.4 Betrachtete Themen

  • Array zur Verwaltung mehrerer Beziehungsobjekte
  • Assoziationsklasse
  • Attribute
  • Ausgabe verknüpfter Objekte
  • Beziehung als eigenes Objekt
  • Beziehungsattribute
  • foreach-Schleife
  • Konstruktoren mit Parameterliste
  • mehrere Klassen
  • n:m-Beziehung
  • Navigation über die Assoziationsklasse
  • Objekte als Attribute
  • Objekte und Objektreferenzen
  • objektorientierte Umsetzung einer n:m-Beziehung
  • Referenz von Mitarbeit auf Angestellter
  • Referenz von Mitarbeit auf Projekt
  • Trennung von Objektklassen und Beziehungsklasse
  • Vergleich mit direkter Listenlösung aus Aufgabe 4d
  • Vergleich mit einer junction table
  • Zugriff auf Attribute verknüpfter Objekte
  • Zusammenhang zum relationalen Datenbankmodell
  • Zusammenhang zur UML-Assoziationsklasse

21.5.5 Anmerkung

Der zentrale Unterschied zu Aufgabe 4d ist: In 4d wird die n:m-Beziehung durch direkte Listen in beiden Klassen gespeichert: List<Projekt> in Angestellter bzw. List<Angestellter> in Projekt. In 4e wird die Beziehung selbst zu einem eigenen Objekt:

class Mitarbeit {

    Angestellter angestellter;

    Projekt projekt;

    int stunden;

}

Damit kann die Beziehung eigene Attribute erhalten, hier: int stunden;

Das ist der eigentliche Schwerpunkt dieser Aufgabe: Die Mitarbeit ist nicht nur eine Verknüpfung zwischen Angestelltem und Projekt, sondern ein eigenständiges Objekt mit zusätzlichen Attributen.

Damit unterscheidet sich diese Aufgabe von den bisherigen in 4a bis 4d:

  • Bei 1:n-Beziehungen genügt meist eine Referenz oder eine Liste.
  • Bei einer n:m-Beziehung wird häufig eine zusätzliche Klasse benötigt.
  • Die Klasse Mitarbeit beschreibt die Beziehung zwischen Angestellten und Projekten.
  • Sie enthält zudem das Beziehungsattribut stunden.
  • Im relationalen Datenbankmodell würde die Klasse Mitarbeit einer Verbindungsrelation (junction table) entsprechen.

Kein Link-Array

Dies ist zugleich ein einfaches Beispiel für eine Assoziationsklasse der UML. Bei einer solchen wird die Verknüpfung selbst als eigenes Objekt modelliert. Deshalb benötigt man normalerweise kein zusätzliches Link-Array oder eine separate Liste von Zuordnungen.

22 Übungsaufgaben Teil 3

22.1 Aufgabe 5 - Eingaben

Benutzereingabe mit Scanner

22.1.1 Aufgabenstellung

Ausgangspunkt ist die Klasse Stud wie oben, also z.B. so:

class Stud {

  int mNr;

  String name;

  String vName;

}

Mit dem Programm sollen Namen und Matrikelnummer eines Studierenden über die Konsole eingelesen, in einem Objekt gespeichert und auf der Konsole ausgegeben werden. Etwa so:

22.1.2 Gewünschte Ausgabe

Matrikelnummer eingeben: 1007

Vorname eingeben: Pierre

Name eingeben: Picard

--- Daten des Studierenden ---

Matrikelnummer: 1007

Vorname: Pierre

Name: Picard

22.1.3 Lernziele

In dieser Aufgabe lernen Sie, Benutzereingaben mit der Klasse Scanner einzulesen und zur Erzeugung eines Objekts zu verwenden. Sie verstehen, wie Eingabedaten verarbeitet, Attributen zugewiesen und anschließend ausgegeben werden.

 

22.1.4 Lösung

Klasse Aufgabe05

package studierende;

import java.util.Scanner;

public class Aufgabe05 {

  public static void main(String[] args) {

  

Scanner-Objekt für Eingaben erzeugen

    Scanner sc = new Scanner(System.in);

    

Studierenden-Objekt erzeugen

    Stud s = new Stud();

    

Eingaben. Die Eingabe über die Tastatur liefert zunächst einen String. Mit Integer.parseInt() wird dieser String in einen Integer-Wert umgewandelt.

    System.out.print("Matrikelnummer eingeben: ");

    s.mNr = Integer.parseInt(sc.nextLine());

    System.out.print("Vornamen eingeben: ");

    s.vName = sc.nextLine();

    System.out.print("Namen eingeben: ");

    s.name = sc.nextLine();

    

Ausgabe

    s.ausgeben();

    

Scanner schließen

    sc.close();

  }

}

Klasse Stud

package studierende;

class Stud {

  int mNr;

  String name;

  String vName;

  

  void ausgeben() {

    System.out.println("\n--- Daten des Studierenden ---");

    System.out.println("Matrikelnummer: " + mNr);

    System.out.println("Vorname: " + vName);

    System.out.println("Name: " + name);

  }

}

22.1.5 Betrachtete Themen

  • Attribute
  • Benutzereingaben über die Konsole
  • Datentypumwandlung mit Integer.parseInt()
  • Einlesen von Zeichenketten (nextLine())
  • Interaktive Programme
  • Klasse Scanner
  • Konsolenausgaben mit System.out.print()
  • Konsolenausgaben mit System.out.println()
  • Methoden
  • Methodenaufruf
  • Objekte
  • Objekterzeugung mit new
  • Objektorientierte Datenhaltung
  • Referenzvariablen
  • Ressourcen freigeben mit close()
  • Speicherung von Eingaben in Objektattributen
  • Trennung von Hauptprogramm und Fachklasse
  • Verwendung externer Klassen (import java.util.Scanner)
  • Zugriff auf Attribute eines Objekts

Theorieaspekte

Der Schwerpunkt dieser Aufgabe liegt nicht auf Klassenbeziehungen, sondern auf dem Einlesen von Daten und deren Speicherung in Objekten. Gezeigt wird:

1. Ein Objekt erzeugen

Stud s = new Stud();

2. Daten vom Benutzer einlesen

s.name = sc.nextLine();

3. Die Daten im Objekt speichern

s.mNr = ...

s.vName = ...

s.name = ...

4. Die gespeicherten Daten über eine Methode ausgeben

s.ausgeben();

Damit entsteht ein vollständiger Ablauf aus:

Eingabe -> Speicherung im Objekt -> Verarbeitung -> Ausgabe

Dieser Ablauf bildet die Grundlage vieler Anwendungen, beispielsweise für Datenbanken, grafische Oberflächen oder Dateiverarbeitung.

22.2 Aufgabe 6 - Record

"Datensätze"

22.2.1 Aufgabenstellung

Erstellen Sie ein Java-Programm zur Verwaltung einfacher Studierendendaten mit einem record. Der record soll folgende Attribute besitzen:

name vom Typ String,

vName vom Typ String,

plz vom Typ int,

ort vom Typ String,

anzSem vom Typ int,

gebTag vom Typ String.

Führen Sie folgende Aufgaben aus:

  • Erzeugen Sie drei Objekte des Typs Stud in der main()-Methode.
  • Weisen Sie zwei Objekten identische Werte zu.
  • Geben Sie einzelne Komponenten über die automatisch erzeugten Zugriffsmethoden aus.
  • Geben Sie anschließend die gesamten Objekte mit toString() aus.
  • Vergleichen Sie die Objekte mit equals() und geben Sie das Ergebnis aus.
  • Ergänzen Sie im record Stud eine eigene Methode ausgeben(), die die Werte klassisch zeilenweise ausgibt.

22.2.2 Lernziele

In dieser Aufgabe lernen Sie Record-Klassen in Java kennen. Sie verstehen, wie sich einfache Datenobjekte mit einem Record kompakt definieren lassen und wie Java automatisch Zugriffsmethoden sowie weitere Standardmethoden bereitstellt. Dadurch erwerben Sie eine moderne und übersichtliche Möglichkeit zur Beschreibung unveränderlicher Datenobjekte.

Vgl. zu Records auch Projekt HS6 und Aufgabe 41.

 

22.2.3 Lösung

Klasse HP

package hochschule;

public class HP {

  public static void main(String[] args) {

Drei Record-Objekte werden erzeugt. Auch hier wird mit new der Konstruktor augerufen. Vgl. Record Stud unten. Die records werden per Parameterübergabe beschrieben.

    Stud s1 = new Stud("Müller", "Franz", 88888, "Überall", 5, "20.1.2000");

    Stud s2 = new Stud("Müller", "Franz", 99999, "Nirgendwo", 13, "1.1.2001");

    Stud s3 = new Stud("Müller", "Franz", 99999, "Nirgendwo", 13, "1.1.2001");

    

Für jedes Attribut wird eine gleichnamige Abfragemethode bereitgestellt. Für name also name(), wie hier gezeigt.

    System.out.println("\nAusgabe einzelner Attributsausprägungen:\n");

    System.out.println("Name: " + s1.name());

    System.out.println("Name: " + s2.name());

Die Methode toString() liefert Ausgaben eines ganzen Record-Objekts mit Attributsbezeichnung und Attributsausprägung. Vgl. unten.

    System.out.println("\nAusgabe records s1 ... s3:\n");

    System.out.println(s1.toString());

    System.out.println(s2.toString());

    System.out.println(s3.toString());

    System.out.println("\nVergleich der Objekte:\n");

Der Vergleich von Record-Objekten erfolgt mit der Methode equals(). Eine Abfrage mittels einfacher Gleichsetzung klappt nicht. Vgl. die in Abschnitt 4.3 angegebene Literatur.

    if (s1.equals(s2))

      System.out.println("s1 und s2: Wertgleiche Studierende.");

    else

      System.out.println("s1 und s2: Nicht wertgleiche Studierende.");

    if (s3.equals(s2))

      System.out.println("s3 und s2: Wertgleiche Studierende.");

    else

      System.out.println("s3 und s2: Nicht wertgleiche Studierende.");

    if (s3.equals(s1))

      System.out.println("s3 und s1: Wertgleiche Studierende.");

    else

      System.out.println("s3 und s1: Nicht wertgleiche Studierende.");

Zur Veranschaulichung hier noch die "klassische" Ausgabe auf der Konsole.

    System.out.println("\nStudierende/r s1:");

    s1.ausgeben();

    System.out.println("\nStudierende/r s2:");

    s2.ausgeben();

    System.out.println("\nStudierende/r s3:");

    s3.ausgeben();

  }

}

Klasse Stud

package hochschule;

public record Stud(String name, String vName, int plz, String ort, int anzSem, String gebTag) {

  void ausgeben() {

    System.out.println("Name: " + name);

    System.out.println("Vorname: " + vName);

    System.out.println("PLZ: " + plz);

    System.out.println("Ort: " + ort);

    System.out.println("Anzahl Semester: " + anzSem);

    System.out.println("Geburtstag: " + gebTag);

  }

}

22.2.4 Ergebnis Programmlauf

Ausgabe einzelner Attributsausprägungen:

Name: Müller

Name: Müller

Ausgabe records s1 ... s3:

Stud[name=Müller, vName=Franz, plz=88888, ort=Überall, anzSem=5, gebTag=20.1.2000]

Stud[name=Müller, vName=Franz, plz=99999, ort=Nirgendwo, anzSem=13, gebTag=1.1.2001]

Stud[name=Müller, vName=Franz, plz=99999, ort=Nirgendwo, anzSem=13, gebTag=1.1.2001]

Vergleich der Objekte:

s1 und s2: Nicht wertgleiche Studierende.

s3 und s2: Wertgleiche Studierende.

s3 und s1: Nicht wertgleiche Studierende.

Studierende/r s1:

Name: Müller

Vorname: Franz

PLZ: 88888

Ort: Überall

Anzahl Semester: 5

Geburtstag: 20.1.2000

Studierende/r s2:

Name: Müller

Vorname: Franz

PLZ: 99999

Ort: Nirgendwo

Anzahl Semester: 13

Geburtstag: 1.1.2001

Studierende/r s3:

Name: Müller

Vorname: Franz

PLZ: 99999

Ort: Nirgendwo

Anzahl Semester: 13

Geburtstag: 1.1.2001

22.2.5 Betrachtete Themen

  • automatisch erzeugte equals()-Methode
  • automatisch erzeugte hashCode()-Methode
  • automatisch erzeugte toString()-Methode
  • automatisch erzeugte Zugriffsmethoden
  • automatisch erzeugter Konstruktor
  • automatische Implementierung von equals(), hashCode() und toString()
  • Datenträgerklassen
  • eigene Methoden in Records
  • Konsolenausgaben
  • Konstruktoren
  • Methodenaufrufe
  • Objektdarstellung mit toString()
  • Objekterzeugung mit new
  • Objektvergleich mit equals()
  • Record-Komponenten
  • Record-Komponenten als Parameter des Record-Headers
  • Records
  • Referenzvariablen
  • Unterschied zwischen Referenzgleichheit und Wertgleichheit
  • Unterschiede zwischen Record und Klasse
  • unveränderliche Objekte (immutable objects)
  • Vergleich von == und equals()
  • Wertgleichheit von Objekten
  • Zugriffsmethoden ohne get

22.2.6 Exkurs: Wiederholung zu records

Oben wird mit folgendem Befehl ein Record eingerichtet:

record Stud(String name, int sem) {}

Er besitzt somit zwei Attribute:

  • name (String)
  • sem (int)

Daraus erzeugt Java automatisch:

  • private final-Attribute
  • einen Konstruktor
  • Getter-Methoden (name() und sem())
  • eine toString()-Methode
  • equals() und hashCode() (Methoden der Klasse Object, die jede Java-Klasse automatisch erbt.)

Beispielhafte Verwendung in einem Hauptprogramm:

public class Test

  public static void main(String[] args) {

    Stud s = new Stud("Maier", 3);{

Erklärung
new Stud("Maier", 3) ruft den automatisch erzeugten Konstruktor auf.
Das Objekt wird in der Variablen s gespeichert.

Die Ausgabe mit toString() wird wie folgt realisiert. Der Ausdruck println(s) in der Anweisung ...

System.out.println(s);

... ruft automatisch s.toString() auf. Dies liefert dann hier z.B. Stud[name=Maier, sem=3].

 

22.3 Aufgabe 7 - Vererbung

Effizienter programmieren und modellieren mit Vererbung

22.3.1 Aufgabenstellung

Erstellen Sie ein Java-Programm zur Verwaltung von Fahrzeugen.

Superklasse Fahrzeug

Legen Sie eine Klasse Fahrzeug an. Die Klasse soll folgende Attribute besitzen:

  • fzNr (Fahrzeugnummer)
  • hersteller
  • baujahr

Erstellen Sie außerdem:

  • einen Konstruktor zur Übergabe aller Attribute
  • eine Methode anzeigen(), die alle Fahrzeugdaten ausgibt

Subklasse PKW

Legen Sie eine Klasse PKW an, die von Fahrzeug erbt und folgende zusätzlichen Attribute hat:

  • sitze (Anzahl der ...)
  • typ (Fahrzeugtyp)

Erstellen Sie:

  • einen Konstruktor
  • einen Aufruf des Superklassen-Konstruktors mit super(...)
  • eine überschreibende Methode anzeigen()

Die Methode soll zunächst die Ausgabe der Superklasse nutzen und anschließend die zusätzlichen PKW-Daten ausgeben.

Klasse Aufgabe07

Erstellen Sie eine Klasse Aufgabe07 mit einem Hauptprogramm (main()). Darin werden ...

  • ein Objekt der Klasse Fahrzeug und
  • ein Objekt der Klasse PKW

erzeugt. Geben Sie anschließend die Daten beider Objekte mit der Methode anzeigen() aus. Die Ausgabe soll so aussehen:

--- Fahrzeug ---

Fahrzeugnummer: 1

Hersteller: MAN

Baujahr: 2015

--- PKW ---

Fahrzeugnummer: 2

Hersteller: VW

Baujahr: 2022

Sitze: 5

Typ: Kombi

22.3.2 Lernziele

In dieser Aufgabe lernen Sie das Grundprinzip der Vererbung in Java kennen. Sie verstehen, wie eine Subklasse Attribute und Methoden einer Oberklasse übernimmt und um eigene Attribute erweitert. Dadurch erwerben Sie eine wichtige Grundlage der objektorientierten Programmierung und bereiten sich auf weiterführende Konzepte wie Überschreiben und Polymorphie vor.

Vgl. auch Aufgabe 14 für eine Vertiefung der Vererbung in einem umfangreicheren Anwendungsbeispiel.

 

22.3.3 Lösung

Klasse Aufgabe07

package fahrzeuge;

public class Aufgabe07 {

  public static void main(String[] args) {

Objekt der Superklasse

    Fahrzeug f = new Fahrzeug(1, "MAN", 2015);

    System.out.println("--- Fahrzeug ---");

    f.anzeigen();

Objekt der Subklasse

    PKW p = new PKW(2, "VW", 2022, 5, "Kombi");

    System.out.println("\n--- PKW ---");

    p.anzeigen();

  }

}

Klasse Fahrzeug

package fahrzeuge;

public class Fahrzeug {

  int fzNr;

  String hersteller;

  int baujahr;

Konstruktor mit Parameterübergabe.

  public Fahrzeug(int fzNr, String hersteller, int baujahr) {

    this.fzNr = fzNr;

    this.hersteller = hersteller;

    this.baujahr = baujahr;

  }

Methode zur Ausgabe

  void anzeigen() {

    System.out.println("Fahrzeugnummer: " + fzNr);

    System.out.println("Hersteller: " + hersteller);

    System.out.println("Baujahr: " + baujahr);

  }

}

Klasse PKW

package fahrzeuge;

public class PKW extends Fahrzeug {

  int sitze;

  String typ;

Konstruktor in der Subklasse. Mit Hilfe des Schlüsselworts super kann der Konstruktor der Superklasse integriert werden

  public PKW(int fzNr, String hersteller, int baujahr, int sitze, String typ) {

    super(fzNr, hersteller, baujahr);

      this.sitze = sitze;

      this.typ = typ;

  }

Methode überschreiben (Override). Die Attribute der Superklasse werden wieder mit super hinzugenommen.

  @Override

  void anzeigen() {

    super.anzeigen();

    System.out.println("Sitze: " + sitze);

    System.out.println("Typ: " + typ);

  }

}

22.3.4 Ergebnis Programmlauf

--- Fahrzeug ---

Fahrzeugnummer: 1

Hersteller: MAN

Baujahr: 2015

--- PKW ---

Fahrzeugnummer: 2

Hersteller: VW

Baujahr: 2022

Sitze: 5

Typ: Kombi

22.3.5 Betrachtete Themen

  • Annotation @Override
  • Attribute
  • Aufruf des Superklassen-Konstruktors mit super(...)
  • Aufruf einer überschriebenen Superklassenmethode mit super.anzeigen()
  • geerbte Attribute
  • geerbte Methoden
  • Generalisierung
  • Klassen und Objekte
  • Konsolenausgabe mit System.out.println()
  • Konstruktor der Superklasse
  • Konstruktoren mit Parameterliste
  • Methoden
  • Methodenaufruf
  • Superklasse
  • Objekterzeugung mit new
  • Schlüsselwort extends
  • Spezialisierung
  • Überschreiben von Methoden
  • Subklasse
  • Vererbung
  • Wiederverwendung von Programmcode
  • zusätzliche Attribute in Subklassen

22.3.6 Erläuterung

Diese Aufgabe zeigt ein einfaches Beispiel für das Konzept Vererbung der objektorientierten Theorie. Die Idee hinter dem Vererbungskonzept ist: Gemeinsame Attribute und Methoden werden in einer Superklasse gespeichert. Spezielle Subklassen erweitern diese Superklasse um zusätzliche Eigenschaften und Methoden.

Im Beispiel bedeutet das:

  • Ein PKW ist ebenfalls ein Fahrzeug.
  • Deshalb übernimmt die Klasse PKW die Eigenschaften der Klasse Fahrzeug.

Die Methode void anzeigen() in Fahrzeug gibt die Fahrzeugdaten aus. Aufruf in Aufgabe07: f.anzeigen(); Das Objekt führt dann seine eigene Methode aus.

Die Subklasse PKW entsteht durch diese Anweisung: public class PKW extends Fahrzeug. Das Schlüsselwort extends bedeutet, dass die Klasse PKW von der Klasse Fahrzeug Attribute und Methoden erbt. Die Klasse PKW hat zusätzlich die Attrribute int sitze und String typ.

Im Konstruktor der Subklasse ...

public PKW(int fzNr, String hersteller,

  int baujahr, int sitze, String typ)

... werden sowohl die geerbten Attribute als auch die neuen Attribute übergeben.

Mit super(...) wird der Konstruktor der Superklasse aufgerufen. Dies bedeutet, die Superklasse übernimmt selbst die Initialisierung ihrer Attribute. Danach werden die zusätzlichen PKW-Attribute gesetzt.

Methoden überschreiben (Override)

Die Methode anzeigen() existiert bereits in der Superklasse. In PKW wird sie erneut definiert. Das ist entlang einer Vererbungshierachie möglich und wird Überschreiben bzw. Override genannt. Die Subklasse ersetzt damit die geerbte Methode durch eine eigene Version. Mit super.anzeigen() in der Klasse PKW wird zuerst die Methode der Superklasse ausgeführt.

Zusammenfassung

Die Klasse PKW erweitert die Klasse Fahrzeug. Dadurch können gemeinsame Eigenschaften zentral gespeichert werden. Die Vererbung vermeidet doppelte Programmierung und macht Programme übersichtlicher und leichter erweiterbar.

22.4 Aufgabe 8 - Kapselung

Kapselung durch Zugriffsmodifizierer

22.4.1 Aufgabenstellung

Richte ein Klasse Stud mit den privaten Attributen name, matrNr, semester und studiengang ein und demonstriere Getter- und Setter-Zugriffe. Stelle sicher, dass bei einer Semesterzahl > 15 die Fehlermeldung "Ungültiges Semester" folgt.

Erstelle für die Attribute passende Getter- und Setter-Methoden. Der Setter für semester soll überprüfen, ob das Semester gültig ist. Es soll gelten:

  • Semester muss größer als 0 sein
  • Semester darf maximal 15 sein

Falls ein ungültiger Wert eingegeben wird, soll eine Fehlermeldung erscheinen.

Erstelle eine Klasse Aufgabe08 mit einer main()-Methode. Darin soll:

  • ein Objekt der Klasse Stud erzeugt werden
  • die Attribute über Setter beschrieben werden
  • die Werte über Getter ausgegeben werden

So soll die Ausgabe aussehen:

Name: Müller

Matrikelnummer: 12345

Semester: 3

Studiengang: Informatik

22.4.2 Lernziele

In dieser Aufgabe lernen Sie, Attribute und Methoden mithilfe von Zugriffsmodifizierern zu schützen. Sie verstehen, wie die Sichtbarkeit von Klassenbestandteilen gesteuert wird und warum Kapselung ein wichtiges Prinzip der objektorientierten Programmierung ist. Dadurch vertiefen Sie Ihr Verständnis für den sicheren und kontrollierten Zugriff auf Objektdaten.

Vgl. zur Vertiefung Aufgabe 13.

 

22.4.3 Lösung

Klasse Stud

class Stud {

Private Attribute, die damit geschützt sind (->Sichtbarkeitsmodifizierer)

  private String name;

  private int matrNr;

  private int semester;

  private String studiengang;

Getter und Setter für das Attribut name.

  public String getName() {

    return name;

  }

  public void setName(String name) {

    this.name = name;

  }

Getter und Setter für das Attribut matrNr (Matrikelnummer).

  public int getMatrNr() {

    return matrNr;

  }

  public void setMatrNr(int matrNr) {

    this.matrNr = matrNr;

  }

Getter und Setter für das Attribut semester. Mit Prüfung, ob Semesteranzahl korrekt.

  public int getSemester() {

    return semester;

  }

  public void setSemester(int semester) {

    if (semester > 0 amp;amp; semester <= 15) {

      this.semester = semester;

    } else {

      System.out.println("Ungültiges Semester!");

    }

  }

Getter und Setter für das Attribut studiengang.

  public String getStudiengang() {

    return studiengang;

  }

  public void setStudiengang(String studiengang) {

    this.studiengang = studiengang;

  }

}

Klasse Aufgabe08 (HP)

public class Aufgabe08 {

  public static void main(String[] args) {

    Stud s = new Stud();

    

    s.setName("Müller");

    s.setMatrNr(12345);

    s.setSemester(3);

    s.setStudiengang("Informatik");

    

    System.out.println("Name: " + s.getName());

    System.out.println("Matrikelnummer: " + s.getMatrNr());

    System.out.println("Semester: " + s.getSemester());

    System.out.println("Studiengang: " + s.getStudiengang());

  }

}

22.4.4 Betrachtete Themen

  • Attribute
  • Datenzugriff über Methoden
  • Getter-Methoden
  • if-Anweisung
  • Informationsverbergung
  • Kapselung
  • Klassen
  • Konsolenausgaben
  • logische Operatoren (amp;amp;)
  • Methoden mit Rückgabewert
  • Methoden ohne Rückgabewert (void)
  • Methodenaufrufe
  • Objekte
  • Objekterzeugung mit new
  • Objektorientierte Datenverwaltung
  • Plausibilitätsprüfung
  • private
  • public
  • Schutz von Attributen
  • Setter-Methoden
  • Sichtbarkeitsmodifizierer
  • Validierung von Eingabewerten

22.4.5 Erläuterung

In dieser Aufgabe wird gezeigt, wie in Java mit privaten Attributen, Gettern und Settern gearbeitet wird. Alle Attribute der Klasse Stud sind mit dem Modifizierer private geschützt. Dadurch kann von außen nicht direkt auf sie zugegriffen werden. Der Zugriff erfolgt ausschließlich über die sogenannten Getter- und Setter-Methoden.

Im Hauptprogramm wird ein Objekt der Klasse Stud erzeugt. Anschließend werden die Attribute mit Setter-Methoden beschrieben und mit Getter-Methoden wieder ausgegeben.

Dabei können auch Kontrollen der Eingaben einbebaut werden. Dies wird bei der Methode setSemester() gezeigt. Dort wird geprüft, ob das angegebene Semester zwischen 1 und 15 liegt. Wird ein ungültiger Wert eingegeben, erscheint die Meldung Ungültiges Semester.

Insgesamt zeigt damit das Projekt an einem einfachen Beispiel:

  • Datenkapselung
  • Zugriffsschutz mit private
  • Verwendung von Getter- und Setter-Methoden
  • einfache Plausibilitätsprüfungen in Methoden
  • Objekterzeugung und Methodennutzung in Java

22.5 Aufgabe 9 - Overload

Methoden überladen

22.5.1 Aufgabenstellung

Erstellen Sie ein Java-Programm zur Verwaltung von Studierendendaten. Legen Sie eine Klasse Stud mit folgenden Attributen des Typs private an:

  • name
  • matrNr
  • semester
  • studiengang

Erstellen Sie für alle Attribute passende Getter- und Setter-Methoden. Zum Beispiel:

public String getName()

public void setName(String name)

Erstellen Sie mehrere Methoden mit der Bezeichnung anzeigen(), die unterschiedliche Parameterlisten besitzen, womit eine Methodenüberladung entsteht:

  • Variante 1 - Keine Parameter. Die Methode soll alle Daten des Studierenden ausgeben.
  • Variante 2 - Boolean-Parameter, z.B. anzeigen(boolean nurName). Ist der Parameter true, soll nur der Name ausgegeben werden. Ist der Parameter false, sollen alle Daten ausgegeben werden. Hinweis: Die Methode darf die parameterlose Methode anzeigen() erneut aufrufen.
  • Variante 3 - Textparameter, z.B. anzeigen(String text). Die Methode soll einen frei wählbaren Text zusammen mit dem Namen ausgeben. Beispiel: "Student: Müller".
  • Variante 4 - Zwei Parameter, z.B. anzeigen(String text, int semester). Die Methode soll einen Text, den Namen und das Semester ausgeben. Beispiel: "Info: Müller (Semester 3)"

Erstellen Sie zusätzlich eine Klasse Aufgabe09 mit einer main()-Methode. Im Hauptprogramm erzeugen Sie ein Objekt der Klasse Stud, beschreiben das Objekt mit Hilfe der Setter-Methoden und rufen anschließend alle Varianten der Methode anzeigen() auf. Zum Beispiel mit folgendem Ergebnis:

22.5.2 Ergebnis Programmlauf:

Strolch, 54321, 3, Informatik

Name: Strolch

Student: Strolch

Info: Strolch (Semester 3)

22.5.3 Lernziele

In dieser Aufgabe lernen Sie, Methoden zu überladen (Overload), indem Sie mehrere Methoden mit demselben Namen, aber unterschiedlichen Parameterlisten definieren. Sie verstehen, wie dadurch verschiedene Varianten einer Funktion bereitgestellt werden können.

22.5.4 Lösung

Klasse Stud

package studierende;

class Stud {

  private String name;

  private int matrNr;

  private int semester;

  private String studiengang;

Getter und Setter

  public String getName() { return name; }

  public void setName(String name) { this.name = name; }

  

  public int getMatrNr() { return matrNr; }

  public void setMatrNr(int matrNr) { this.matrNr = matrNr; }

  

  public int getSemester() { return semester; }

  public void setSemester(int semester) { this.semester = semester; }

  

  public String getStudiengang() { return studiengang; }

  public void setStudiengang(String studiengang) { this.studiengang = studiengang; }

Überladene Methoden anzeigen()
1. keine Parameter -> alle Daten anzeigen

  public void anzeigen() {

    System.out.println(name + ", " + matrNr + ", " + semester + ", " + studiengang);

  }

2. nur Name anzeigen

  public void anzeigen(boolean nurName) {

    if (nurName) {

      System.out.println("Name: " + name);

    } else {

    anzeigen(); // ruft Methode 1 auf

    }

  }

3. frei wählbarer Text + Name

  public void anzeigen(String text) {

    System.out.println(text + ": " + name);

  }

4. zwei Parameter -> Name und Semester

  public void anzeigen(String text, int semester) {

    System.out.println(text + ": " + name + " (Semester " + semester + ")");

  }

}

Klasse Aufgabe09

Package studierende;

public class Aufgabe09 {

  public static void main(String[] args) {

Werte eintragen

  Stud s = new Stud();

  s.setName("Strolch");

  s.setMatrNr(54321);

  s.setSemester(3);

  s.setStudiengang("Informatik");

  

Aufrufe der überladenen Methoden

  s.anzeigen(); // alle Daten

  s.anzeigen(true); // nur Name

  s.anzeigen("Student"); // Text + Name

  s.anzeigen("Info", s.getSemester()); // Text + Name + Semester

  }

}

22.5.5 Betrachtete Themen

  • boolean-Parameter
  • Getter-Methoden
  • gleiche Methodennamen
  • if-Anweisung
  • int-Parameter
  • Kapselung
  • Klassen
  • Konsolenausgabe mit System.out.println()
  • Methoden
  • Methodenaufrufe
  • Methodensignatur
  • Methodenüberladung
  • Objekte
  • Overload
  • Parameter
  • private Attribute
  • Setter-Methoden
  • String-Parameter
  • unterschiedliche Parameterlisten
  • Wiederverwendung einer Methode innerhalb derselben Klasse
  • Zugriff auf Objektattribute innerhalb von Methoden

22.5.6 Erläuterung

Mehrere Methoden dürfen denselben Namen besitzen, wenn sich ihre Parameterlisten unterscheiden. Beispiele:

anzeigen()

anzeigen(boolean nurName)

anzeigen(String text)

anzeigen(String text, int semester)

Java erkennt beim Aufruf automatisch, welche Methode gemeint ist.

23 Übungsaufgaben Teil 4

Jetzt nochmals einführende Aufgaben, wieder inhaltlich von vorne beginnend.

23.1 Aufgabe 10 - Einstieg

Erste Klasse und erstes Objekt.

23.1.1 Aufgabenstellung

Erstellen Sie eine Klasse Student mit den Attributen:

  • name vom Typ String
  • alter vom Typ int

Implementieren Sie außerdem eine Methode vorstellen(), die den Namen und das Alter auf der Konsole ausgibt. Schreiben Sie danach ein Hauptprogramm mit der Bezeichnung Test, in dem:

  • ein Objekt der Klasse Student erzeugt wird
  • die Attribute gesetzt werden
  • die Methode vorstellen() aufgerufen wird

23.1.2 Lernziele

In dieser Aufgabe lernen Sie, eine eigene Klasse mit Attributen und Methoden zu erstellen und Objekte dieser Klasse zu erzeugen. Sie verstehen, wie Objekte Daten speichern und über Methoden auf diese Daten zugreifen können. Dadurch erwerben Sie die Grundlagen für die objektorientierte Programmierung in Java.

 

23.1.3 Lösung

package studierende;

class Student {

  String name;

  int alter;

  

  void vorstellen() {

    System.out.println("Ich heiße " + name + " und bin " + alter + " Jahre alt.");

  }

}

public class Test {

  public static void main(String[] args) {

    Student s1 = new Student();

    s1.name = "Anna";

    s1.alter = 20;

    s1.vorstellen();

  }

}

Anmerkung: In Java dürfen mehrere Klassen in einer Datei stehen. Dabei darf jedoch nur eine Klasse als public deklariert werden. Testen Sie deshalb hin und wieder die Unterbringung aller Klassen in einer einzigen Datei.

23.1.4 Betrachtete Themen

  • Attributbelegung
  • Attribute
  • Datentypen (String, int)
  • Hauptprogramm (main())
  • Instanziierung
  • Klassen
  • Klassen als Baupläne
  • Konsolenausgabe mit System.out.println()
  • Methoden
  • Methodenaufrufe
  • Objekte
  • Objekte als Instanzen einer Klasse
  • Objekterzeugung
  • Objektzugriff mit dem Punktoperator
  • Referenzvariablen
  • Verwendung von new

23.1.5 Ergebnis Programmlauf

Ich heiße Anna und bin 20 Jahre alt.

 

23.2 Aufgabe 11 - Konstruktoren

Konstruktoren mit Parametern

23.2.1 Aufgabenstellung

Erweitern Sie die Klasse Student

class Student {

  String name;

  int alter;

...

so, dass Name und Alter bereits beim Erzeugen des Objekts übergeben werden. Dazu soll ein Konstruktor implementiert werden.

Erzeugen Sie im Hauptprogramm ein Objekt der Klasse Student mit Hilfe des Konstruktors und geben Sie die Daten mit einer Methode vorstellen() aus.

23.2.2 Lernziele

In dieser Aufgabe lernen Sie, Konstruktoren mit Parametern zu implementieren und Objekte bereits beim Erzeugen mit Anfangswerten zu initialisieren. Sie verstehen, wie Werte an einen Konstruktor übergeben und den Attributen eines Objekts zugewiesen werden. Dadurch vertiefen Sie Ihr Verständnis für die strukturierte Initialisierung von Objekten.

 

23.2.3 Lösung

package studierende;

class Student {

    String name;

    int alter;

  Student(String name, int alter) {

    this.name = name;

    this.alter = alter;

  }

  void vorstellen() {

    System.out.println("Ich heiße " + name + " und bin " + alter + " Jahre alt.");

  }

}

public class Test {

  public static void main(String[] args) {

    Student s1 = new Student("Max", 22);

    s1.vorstellen();

  }

}

23.2.4 Ergebnis Programmlauf

Ich heiße Max und bin 22 Jahre alt.

23.2.5 Betrachtete Themen

  • Attribute
  • Hauptprogramm (main())
  • Initialisierung von Objekten
  • Instanziierung
  • Klassen
  • Konsolenausgabe
  • Konstruktoraufruf
  • Konstruktoren
  • Konstruktorparameter
  • Methoden
  • Methodenaufruf
  • Objekte
  • Objekterzeugung mit new
  • Parameterübergabe
  • Referenzvariablen
  • Unterschied zwischen Attributen und Parametern
  • Verwendung von this

 

23.3 Aufgabe 12 - Array

Array von Objekten

23.3.1 Aufgabenstellung

Erstellen Sie mehrere Objekte der Klasse Student und speichern Sie diese in einem Array. Legen Sie ein Array für drei Studierende an. Erzeugen Sie anschließend drei Objekte der Klasse Student, speichern Sie diese im Array und vergeben Sie die Namen:

  • Anna
  • Ben
  • Clara

Geben Sie danach alle Studierenden mit einer Schleife aus.

23.3.2 Lernziele

In dieser Aufgabe lernen Sie, mehrere Objekte in einem Array zu verwalten. Sie verstehen, wie Objektreferenzen in einem Array gespeichert, auf die einzelnen Elemente zugegriffen und deren Methoden aufgerufen werden können.

23.3.3 Ergebnis Programmlauf

Student: Anna

Student: Ben

Student: Clara

 

23.3.4 Lösung

package studierende;

class Student {

  String name;

  Student(String name) {

    this.name = name;

  }

  void anzeigen() {

    System.out.println("Student: " + name);

  }

}

public class Test {

  public static void main(String[] args) {

    Student[] gruppe = new Student[3];

    

    gruppe[0] = new Student("Anna");

    gruppe[1] = new Student("Ben");

    gruppe[2] = new Student("Clara");

    

    for (Student s : gruppe) {

      s.anzeigen();

    }

  }

}

23.3.5 Betrachtete Themen

  • Array-Indizes
  • Arrays
  • Attribute
  • ForEach-Schleife
  • Instanziierung mit new
  • Iteration über Datenstrukturen
  • Klassen
  • Konstruktoren
  • Methoden
  • Objektarrays
  • Objekte
  • Objektverwaltung mit Arrays
  • Punktnotation
  • Referenzen
  • Referenzen in Arrays
  • Speicherung von Objekten in Datenstrukturen

23.3.6 Erläuterung

Die Anweisung

Student[] gruppe = new Student[3];

erzeugt ein Array für drei Objekte der Klasse Student. Wichtig ist: Zu diesem Zeitpunkt existieren die drei Studierenden noch nicht. Das Array enthält zunächst:

null

null

null

Erst durch die Anweisungen

gruppe[0] = new Student("Anna");

gruppe[1] = new Student("Ben");

gruppe[2] = new Student("Clara");

werden die Objekte erzeugt und im Array gespeichert. Vereinfacht dargestellt:

gruppe[0] --> Student("Anna")

gruppe[1] --> Student("Ben")

gruppe[2] --> Student("Clara")

Das Array enthält also nicht die Objekte selbst, sondern Referenzen auf diese Objekte.

Theorieaspekte

Diese Aufgabe verbindet zwei wichtige Konzepte:

  • Objekte
  • Arrays

Sie zeigt, wie man mehrere Objekte gemeinsam in einer Datenstruktur verwaltet und mit einer Schleife verarbeitet. Das ist eine wichtige Grundlage für spätere Themen wie:

  • Objektverknüpfungen
  • Listen (ArrayList)
  • Sammlungen (Collections)
  • 1:n-Beziehungen
  • n:m-Beziehungen

Und stellt damit den Übergang von einzelnen Objekten zur Verwaltung größerer Objektmengen dar.

 

23.4 Aufgabe 13 - Kapselung

Encapsulation

23.4.1 Aufgabenstellung

Erstellen Sie eine Klasse Konto mit einem privaten Attribut kontostand vom Typ double. Implementieren Sie dazu:

  • eine Methode einzahlen(double betrag), die den Kontostand erhöht
  • eine Methode getKontostand(), die den aktuellen Kontostand zurückgibt

Testen Sie die Klasse in einer main-Methode. Führen Sie mehrere Einzahlungen durch und geben Sie anschließend den aktuellen Kontostand aus.

23.4.2 Lernziele

In dieser Aufgabe lernen Sie das Konzept der Kapselung in Java kennen. Sie verstehen, wie Attribute durch geeignete Zugriffsrechte geschützt und über Methoden kontrolliert zugänglich gemacht werden. Dadurch vertiefen Sie Ihr Verständnis für den sicheren und übersichtlichen Aufbau von Klassen.

 

23.4.3 Lösung

package banken;

class Konto {

  private double kontostand;

  public void einzahlen(double betrag) {

    kontostand = kontostand + betrag;

  }

  public double getKontostand() {

    return kontostand;

  }

}

public class Aufgabe13 {

  public static void main(String[] args) {

    Konto k = new Konto();

    k.einzahlen(100.0);

    k.einzahlen(50.0);

    System.out.println("Kontostand: " + k.getKontostand());

  }

}

23.4.4 Ergebnis Programmlauf

Kontostand: 150.0

23.4.5 Betrachtete Themen

  • Attribute
  • Datenverbergung (Information Hiding)
  • Getter-Methode
  • Kapselung
  • Klassen
  • Methoden
  • Methoden mit Parametern
  • Methoden mit Rückgabewert
  • Objekte
  • Objektorientierte Modellierung
  • private
  • public
  • Sichtbarkeitsmodifizierer
  • Zugriffsschutz
  • Zustandsänderung von Objekten

23.4.6 Erläuterung

Das Attribut private double kontostand; ist private. Es kann deshalb nicht direkt von außen angesprochen werden. Nicht möglich wäre:

k.kontostand = 1000.0; // nicht erlaubt!

Der Zugriff erfolgt ausschließlich über die öffentlichen Methoden:

k.einzahlen(100.0);

und

k.getKontostand();

Dadurch kontrolliert die Klasse selbst, wie ihr Zustand verändert werden darf.

Theorieaspekte

Diese Aufgabe führt ein wichtiges Prinzip der objektorientierten Programmierung ein: Kapselung (Encapsulation). Mit ihm werden Daten eines Objekts vor direktem Zugriff geschützt und können nur über definierte Methoden gelesen oder verändert werden. Dadurch

  • bleiben Objekte konsistent,
  • werden Fehlbedienungen verhindert,
  • kann die interne Implementierung später geändert werden, ohne andere Programmteile anzupassen.

23.5 Aufgabe 14 - Vererbung

Attribute und Methoden weitergebe

23.5.1 Aufgabenstellung

Erstellen Sie eine Superklasse Person mit dem Attribut String name; sowie der Methode void vorstellen(). Die Methode soll den Namen der Person ausgeben.

Leiten Sie davon eine Klasse Student ab. Diese soll zusätzlich das Attribut String studiengang; besitzen. Implementieren Sie außerdem eine Methode

void anzeigen()

zur Ausgabe des Studiengangs.

Schreiben Sie ein Hauptprogramm Aufgabe14, in dem ein Objekt der Klasse Student erzeugt, mit Daten versehen und getestet wird.

23.5.2 Lernziele

In dieser Aufgabe lernen Sie das Konzept der Vererbung in Java kennen. Sie verstehen, wie eine Unterklasse Eigenschaften und Methoden einer Oberklasse übernimmt und diese um eigene Merkmale erweitern kann.

 

23.5.3 Lösung

package personen;

class Person {

  String name;

  void vorstellen() {

    System.out.println("Ich heiße " + name + ".");

  }

}

class Student extends Person {

  String studiengang;

  void anzeigen() {

    System.out.println(name + " studiert " + studiengang + ".");

  }

}

public class Aufgabe14 {

  public static void main(String[] args) {

    Student s = new Student();

    s.name = "Paula";

    s.studiengang = "Informatik";

    

    s.vorstellen();

    s.anzeigen();

  }

}

23.5.4 Ergebnis Programmlauf

Ich heiße Paula.

Paula studiert Informatik.

23.5.5 Betrachtete Themen

  • Basisklasse
  • Geerbte Attribute
  • Geerbte Methoden
  • Generalisierung
  • Instanziierung mit new
  • Klassenhierarchien
  • Methodenaufrufe
  • Superklasse
  • Objekte
  • Objektorientierte Modellierung
  • Schlüsselwort extends
  • Spezialisierung
  • Subklasse
  • Vererbung
  • Wiederverwendung von Programmcode

23.5.6 Erläuterung

Die Klasse Student wird von der Klasse Person abgeleitet:

class Student extends Person

Dadurch übernimmt Student automatisch alle Attribute und Methoden der Superklasse. Die Klasse Person definiert:

String name; und void vorstellen()

Diese Elemente stehen deshalb auch einem Objekt der Klasse Student zur Verfügung. Deshalb ist folgende Anweisung möglich:

s.name = "Paula";

obwohl das Attribut name nicht in der Klasse Student definiert wurde. Ebenso kann die Methode s.vorstellen(); aufgerufen werden, obwohl sie nur in der Klasse Person implementiert ist.

Theorieaspekte

Diese Aufgabe demonstriert das Konzept der Vererbung. Es wird gezeigt, dass gemeinsame Eigenschaften in einer Superklasse zusammengefasst werden können. Spezielle Eigenschaften werden dann in Subklassen ergänzt. Dadurch wird:

  • Programmcode wiederverwendet,
  • Redundanz vermieden,
  • eine Klassenhierarchie aufgebaut.

 

23.6 Aufgabe 15 - Override

Methoden überschreiben

23.6.1 Aufgabenstellung

Verwenden Sie wieder die Klassen Person und Student. Die Methode vorstellen() soll in der Klasse Student überschrieben werden, sodass zusätzlich der Studiengang ausgegeben wird.

Verwenden Sie dabei die Annotation @Override. Testen Sie das Verhalten im Hauptprogramm Aufgabe15.

23.6.2 Lernziele

In dieser Aufgabe lernen Sie, Methoden einer Oberklasse in einer Unterklasse zu überschreiben (Override). Sie verstehen, wie das Verhalten geerbter Methoden an die Anforderungen einer Unterklasse angepasst werden kann und wie dadurch die Wiederverwendung und Erweiterung von Klassen unterstützt wird.

 

23.6.3 Lösung

package personen;

class Person {

  String name;

  void vorstellen() {

    System.out.println("Ich heiße " + name + ".");

  }

}

class Student extends Person {

  String studiengang;

  

  @Override

  void vorstellen() {

    System.out.println("Ich heiße " + name + " und studiere " + studiengang + ".");

  }

}

public class Aufgabe15 {

  public static void main(String[] args) {

    Student s = new Student();

    s.name = "Tom";

    s.studiengang = "Wirtschaftsinformatik";

    s.vorstellen();

  }

}

23.6.4 Ergebnis Programmlauf

Ich heiße Tom und studiere Wirtschaftsinformatik.

23.6.5 Betrachtete Themen

  • Annotation @Override
  • Dynamische Bindung
  • Methodenaufrufe
  • Methodenüberschreibung (Override)
  • Superklasse
  • Objektorientierte Modellierung
  • Polymorphes Verhalten
  • Spezialisierung von Verhalten
  • Überschreiben geerbter Methoden
  • Subklasse
  • Vererbung
  • Wiederverwendung von Programmcode

23.6.6 Erläuterung

Die Klasse Student erbt die Methode void vorstellen() zunächst von der Klasse Person. Durch die eigene Implementierung

@Override

void vorstellen() {

  ...

}

wird die geerbte Methode ersetzt. Man spricht vom Überschreiben einer Methode oder von Override. Die neue Implementierung wird immer dann ausgeführt, wenn die Methode für ein Objekt der Klasse Student aufgerufen wird.

Die Annotation @Override

Die Annotation @Override ist nicht zwingend erforderlich, wird aber empfohlen. Sie informiert den Compiler darüber, dass eine geerbte Methode überschrieben werden soll. Wird dabei versehentlich eine falsche Methodensignatur verwendet, erkennt der Compiler den Fehler sofort.

Beispiel:

@Override

void vorstelllen() {

}

Der Schreibfehler würde vom Compiler erkannt werden.

Theorieaspekte

Diese Aufgabe führt erstmals das Überschreiben von Methoden ein. Es wird gezeigt, dass eine Subklasse nicht nur neue Attribute und Methoden hinzufügen kann, sondern auch das Verhalten geerbter Methoden verändern darf. Dadurch können allgemeine Eigenschaften in einer Superklasse definiert und in den Subklassen spezialisiert werden.

 

23.7 Aufgabe 16 - Polymorphie

Zur Laufzeit die passende Methode

23.7.1 Aufgabenstellung

Erstellen Sie eine Superklasse Tier mit der Methode lautGeben(). Leiten Sie davon zwei Klassen ab: Hund und Katze. Überschreiben Sie die Methode jeweils passend. Katzen sagen Miau, Hunde sagen Wuff.

Speichern Sie mehrere Tier-Objekte in einem Array vom Typ Tier und lassen Sie alle Tiere ihre Laute ausgeben.

23.7.2 Ergebnis Programmlauf

Wuff

Miau

Wuff

23.7.3 Lernziele

In dieser Aufgabe lernen Sie das Konzept der Polymorphie in Java kennen. Sie verstehen, wie eine Referenzvariable der Oberklasse auf Objekte verschiedener Unterklassen verweisen kann und wie dabei zur Laufzeit automatisch die jeweils passende Methode aufgerufen wird.

 

23.7.4 Lösung

package tiere;

class Tier {

  void lautGeben() {

    System.out.println("Unbekanntes Tiergeräusch");

    }

  }

  class Hund extends Tier {

    @Override

    void lautGeben() {

    System.out.println("Wuff");

    }

  }

  class Katze extends Tier {

  @Override

  void lautGeben() {

    System.out.println("Miau");

    }

  }

  public class Aufgabe16 {

  public static void main(String[] args) {

    Tier[] tiere = new Tier[3];

    tiere[0] = new Hund();

    tiere[1] = new Katze();

    tiere[2] = new Hund();

    

    for (Tier t : tiere) {

      t.lautGeben();

    }

  }

}

23.7.5 Betrachtete Themen

  • Austauschbarkeit von Objekten
  • Dynamische Bindung
  • ForEach-Schleife
  • Laufzeitbindung (Late Binding)
  • Auswahl der passenden Methode zur Laufzeit
  • Methodenüberschreibung (Override)
  • Superklasse
  • Superklassenreferenzen
  • Objektarrays
  • Objektorientierte Modellierung
  • Polymorphie
  • Subklassen
  • Vererbung

23.7.6 Erläuterung

Die zentrale Anweisung lautet:

Tier[] tiere = new Tier[3];

Das Array besitzt den Typ Tier. Trotzdem werden darin unterschiedliche Objekte gespeichert:

tiere[0] = new Hund();

tiere[1] = new Katze();

tiere[2] = new Hund();

Dies ist möglich, weil sowohl Hund als auch Katze von Tier erben. Beim Durchlaufen des Arrays wird immer dieselbe Anweisung verwendet:

t.lautGeben();

Trotzdem entstehen unterschiedliche Ausgaben:

Wuff

Miau

Wuff

Java entscheidet erst zur Laufzeit, welche Methode tatsächlich ausgeführt werden muss.

Theorieaspekte

Diese Aufgabe zeigt erstmals die eigentliche Stärke der Vererbung. Bei Aufgabe 15 wurde zwar bereits eine Methode überschrieben, die Methode wurde jedoch direkt über ein Objekt der Klasse Student aufgerufen:

Student s = new Student();

s.vorstellen();

In der vorliegenden Aufgabe erfolgt der Aufruf dagegen über eine Referenz der Superklasse:

Tier t;

beziehungsweise

Tier[] tiere;

Dadurch wird sichtbar, dass dieselbe Referenzvariableunterschiedliche Objekttypen verwalten kann. Genau dies bezeichnet man als Polymorphie.

23.7.7 Exkurs Polymorphie

Polymorphie bedeutet in der objektorientierten Programmierung:

Eine Referenzvariable vom Typ der Superklasse kann auf Objekte verschiedener Subklassen zeigen.

Beim Methodenaufruf entscheidet Java nicht anhand des Typs der Referenzvariablen, sondern anhand des tatsächlichen Objekts, welche überschriebene Methode ausgeführt wird. Diese Entscheidung erfolgt erst zur Laufzeit und wird als dynamische Bindung bezeichnet.

Beispielsweise:

Fahrzeug f;

f = new PKW();

f.fahren();

f = new LKW();

f.fahren();

Obwohl immer dieselbe Referenzvariable verwendet wird, entstehen unterschiedliche Ergebnisse. Dies ist nur möglich, weil die Methode fahren() in den Subklassen überschrieben wurde.

Ein weiteres Beispiel:

class Fahrzeug {

    void fahren() {

      System.out.println("Ein Fahrzeug fährt.");

    }

}

class PKW extends Fahrzeug {

    @Override

    void fahren() {

      System.out.println("Der PKW fährt.");

    }

}

class LKW extends Fahrzeug {

    @Override

    void fahren() {

      System.out.println("Der LKW fährt.");

  }

}

Dies erfordert im Programm:

Fahrzeug f;

f = new PKW();

f.fahren();

f = new LKW();

f.fahren();

Ausgabe:

Der PKW fährt.

Der LKW fährt.

Die Polymorphie steckt in dieser Zeile:

Fahrzeug f;

Die Variable f hat den Typ Fahrzeug. Später verweist sie aber einmal auf ein PKW-Objekt und einmal auf ein LKW-Objekt:

f = new PKW();

f = new LKW();

Dieselbe Variable kann also Objekte unterschiedlicher Klassen verwalten.

Was passiert beim Methodenaufruf?

f.fahren();

Der Compiler sieht nur:

Fahrzeug f

Zur Laufzeit schaut Java aber nach: Auf welches Objekt zeigt f tatsächlich? Zeigt f auf einen PKW?

f = new PKW();

Dann wird ausgeführt:

PKW.fahren()

Zeigt f auf einen LKW?

f = new LKW();

Dann wird ausgeführt:

LKW.fahren()

Das nennt man dynamische Bindung oder spätes Binden (late binding).

Beispiel mit dem Interface-Thema

In einer der folgenden Aufgaben wird dieses Beispiel verwendet:

interface Beweglich {

  void bewegen();

}

class Mensch implements Beweglich {

  public void bewegen() {

    System.out.println("Der Mensch läuft.");

  }

}

Wird dann programmiert ...

Beweglich b = new Mensch();

b.bewegen();

... liegt ebenfalls Polymorphie vor. Die Variable besitzt den Typ Beweglich, verweist aber auf ein Objekt der Klasse Mensch.

Zusammenhang mit Override

Polymorphie wird besonders nützlich, wenn Methoden überschrieben werden:

@Override

void fahren() {

  ...

}

Denn dann kann derselbe Aufruf f.fahren(); je nach tatsächlichem Objekt unterschiedliche Ergebnisse liefern.

Zusammenfassung

Bei einer Vererbung, z.B. wenn PKW und LKW von Fahrzeug erben, kann man schreiben:

Fahrzeug f;

und später

f = new PKW();

oder

f = new LKW();

Wenn dieselbe Referenzvariable Objekte verschiedener Subklassen aufnehmen kann und der passende Methodenrumpf erst zur Laufzeit ausgewählt wird, spricht man von Polymorphie.

24 Übungsaufgaben Teil 5

24.1 Aufgabe 17 - Abstrakte Klasse

Gemeinsame Grundlage mehrerer Klassen

24.1.1 Aufgabenstellung

Erstellen Sie eine abstrakte Klasse Fahrzeug mit der abstrakten Methode fahren(). Leiten Sie davon eine Klasse Auto ab und implementieren Sie die Methode.

Testen Sie die Klassen in einem Hauptprogramm Aufgabe17. Verwenden Sie dabei eine Referenz vom Typ Fahrzeug.

24.1.2 Lernziele

In dieser Aufgabe lernen Sie, eine abstrakte Klasse in Java zu erstellen und von ihr konkrete Klassen abzuleiten. Sie verstehen den Zweck abstrakter Klassen als gemeinsame Grundlage für mehrere Klassen und lernen, wie abstrakte Methoden in den Unterklassen implementiert werden.

 

24.1.3 Lösung

Klasse Fahrzeug

package fahrzeuge;

abstract class Fahrzeug {

  abstract void fahren();

}

Klasse Auto

package fahrzeuge;

class Auto extends Fahrzeug {

  @Override

  void fahren() {

    System.out.println("Das Auto fährt.");

  }

}

Klasse Aufgabe17

package fahrzeuge;

public class Aufgabe17 {

  public static void main(String[] args) {

    Fahrzeug f = new Auto();

    f.fahren();

  }

}

24.1.4 Ergebnis Programmlauf

Das Auto fährt.

24.1.5 Betrachtete Themen

  • Abstrakte Klassen
  • Abstrakte Methoden
  • Vererbung
  • Superklasse
  • Subklasse
  • Spezialisierung
  • Generalisierung
  • Gemeinsame Superklasse
  • Methodenüberschreibung (Override)
  • Polymorphie
  • Superklassenreferenzen
  • Dynamische Bindung
  • Instanziierung von Subklassen
  • Objektorientierte Modellierung

24.1.6 Exkurs - Abstrakte Klasse

Eine abstrakte Klasse ist eine Klasse, von der normalerweise keine direkten Objekte erzeugt werden. Sie dient als gemeinsame Grundlage für andere Klassen.

Beispiel:

abstract class Fahrzeug {

    String marke;

}

Von Fahrzeug soll nur geerbt werden:

class PKW extends Fahrzeug {

}

Der folgende Aufruf wäre nicht erlaubt:

Fahrzeug f = new Fahrzeug(); // Fehler!

Eine abstrakte Methode ist eine Methode ohne Programmcode. Sie legt nur fest, dass eine Methode vorhanden sein muss.

Beispiel:

abstract class Fahrzeug {

    abstract void fahren();

}

Die erbende Klasse muss die Methode dann konkret ausprogrammieren:

class PKW extends Fahrzeug {

    @Override

    void fahren() {

      System.out.println("Der PKW fährt.");

    }

}

Es gilt:

  • Eine abstrakte Klasse kann normale und abstrakte Methoden enthalten.
  • Enthält eine Klasse mindestens eine abstrakte Methode, muss auch die Klasse selbst als abstract gekennzeichnet werden.

 

24.2 Aufgabe 18 - Interface

Beschreibung gemeinsamer Fähigkeiten

24.2.1 Aufgabenstellung

Erstellen Sie ein Interface Beweglich mit der Methode bewegen(). Implementieren Sie dieses Interface in einer Klasse Mensch. Die Methode soll ausgeben:

Der Mensch läuft.

Testen Sie die Implementierung im Hauptprogramm Aufgabe18.

24.2.2 Lernziele

In dieser Aufgabe lernen Sie, ein Interface in Java zu definieren und von einer Klasse implementieren zu lassen. Sie verstehen den Zweck von Interfaces als Beschreibung gemeinsamer Fähigkeiten unabhängig von der Klassenhierarchie und lernen, wie dadurch einheitliche Schnittstellen für unterschiedliche Klassen geschaffen werden.

Vgl. auch die Aufgaben zu mehreren Interfaces (Aufgabe 38) sowie zur Polymorphie (Aufgabe 39).

 

24.2.3 Lösung

package menschen;

interface Beweglich {

  void bewegen();

  }

  

  class Mensch implements Beweglich {

    @Override

    public void bewegen() {

      System.out.println("Der Mensch läuft.");

    }

  }

  

  public class Aufgabe18 {

    public static void main(String[] args) {

    Beweglich b = new Mensch();

    b.bewegen();

  }

}

24.2.4 Betrachtete Themen

  • Abstraktion
  • Definition von Interfaces
  • Dynamische Bindung
  • Implementierung von Interfaces
  • Interface-Referenzen
  • Interfaces
  • Mehrfachimplementierung von Interfaces
  • Methodenaufrufe über Interface-Referenzen
  • Methodensignaturen
  • Objektorientierte Modellierung
  • Polymorphie
  • Schlüsselwort implements
  • Trennung von Schnittstelle und Implementierung
  • Vertrag (Contract) zwischen Interface und Klasse
  • Wiederverwendbare Schnittstellen

24.2.5 Erläuterung

Das Programm zeigt, wie ein Interface verwendet wird. Mit ...

interface Beweglich {

  void bewegen();

  }

... wird das Interface Beweglich definiert. Es enthält die Methode void bewegen(); Die Methode besitzt noch keinen Programmcode. Das Interface legt also nur fest: "Jede Klasse, die Beweglich sein will, muss eine Methode bewegen() besitzen."

Die Klasse Mensch

class Mensch implements Beweglich {

Die Klasse Mensch übernimmt das Interface mit:

implements Beweglich

Dadurch verpflichtet sich die Klasse, die Methode bewegen() bereitzustellen.

Umsetzung der Methode

@Override

public void bewegen() {

  System.out.println("Der Mensch läuft.");

}

Hier wird die im Interface verlangte Methode konkret programmiert. Die Annotation @Override bedeutet: Diese Methode überschreibt bzw. erfüllt eine Vorgabe aus dem Interface.

Das Hauptprogramm

Beweglich b = new Mensch();

Hier passiert folgendes:

  • rechts wird ein Objekt der Klasse Mensch erzeugt
  • links wird es in einer Variablen vom Typ Beweglich gespeichert

Die Variable kennt also nur das Verhalten des Interfaces. Der Methodenaufruf b.bewegen() funktioniert, weil das Interface die Methode bewegen() definiert.

Zur Laufzeit wird die Methode der Klasse Mensch ausgeführt:

Der Mensch läuft.

Bedeutung des Beispiels

Das Interface beschreibt eine Fähigkeit: "Kann sich bewegen". Andere Klassen könnten ebenfalls Beweglich implementieren:

class Auto implements Beweglich

class Hund implements Beweglich

class Roboter implements Beweglich

Alle müssten eine Methode bewegen() besitzen, aber jeweils mit unterschiedlichem Verhalten. Das ist ein wichtiger Gedanke der objektorientierten Programmierung:

  • gemeinsames Verhalten definieren
  • konkrete Umsetzung den Klassen überlassen

24.2.6 Exkurs - Interface

Ein Interface legt fest, welche Methoden eine Klasse haben muss, aber nicht wie diese Methoden genau funktionieren. Einfach erklärt

  • Ein Interface enthält Methoden ohne Implementierung (nur die Signatur, d.h. Bezeichnung und Parameterliste).
  • Eine Klasse, die dieses Interface benutzt ("implementiert"), muss diese Methoden definieren.
  • So kann man sicherstellen, dass verschiedene Klassen sich gleich "verhalten", auch wenn sie intern unterschiedlich arbeiten.

Beispiel

interface Fahrzeug {

    void starten();

    void fahren();

}

Das Interface sagt nur: Jedes Fahrzeug muss starten() und fahren() können.

class Auto implements Fahrzeug {

    public void starten() {

      System.out.println("Auto startet");

    }

    public void fahren() {

      System.out.println("Auto fährt");

    }

}

Wofür braucht man Interfaces?

  • Einheitliche Struktur: Verschiedene Klassen können gleich angesprochen werden
  • Flexibilität: Man kann später leicht neue Klassen hinzufügen
  • Mehrfachvererbung (von Verhalten): Eine Klasse kann mehrere Interfaces implementieren

Kurz gesagt:

  • Ein Interface beschreibt was gemacht werden soll,
  • die Klasse beschreibt wie es gemacht wird.

24.2.7 Interface vs. abstrakte Klasse

Interfaces und abstrakte Klassen verfolgen ein ähnliches Ziel: Sie dienen als Vorlage für andere Klassen. Es gibt aber wichtige Unterschiede.

 


Interface

Abstrakte Klasse

Beschreibt vor allem ein Verhalten ("kann etwas")

Beschreibt eine gemeinsame Superklasse ("ist etwas")

Eine Klasse kann mehrere Interfaces implementieren

Eine Klasse kann nur von einer abstrakten Klasse erben

Enthält hauptsächlich Methodensignaturen

Kann Attribute, Konstruktoren und vollständige Methoden enthalten

Schlüsselwort interface

Schlüsselwort abstract class

Keine Objekte direkt erzeugbar

Keine Objekte direkt erzeugbar


Beispiel mit Interface

Hier wird nur festgelegt, dass sich etwas bewegen kann:

interface Beweglich {

    void bewegen();

}

Verschiedene Klassen können dieses Verhalten unterschiedlich umsetzen:

class Mensch implements Beweglich {

    public void bewegen() {

      System.out.println("Der Mensch läuft.");

    }

}

class Auto implements Beweglich {

    public void bewegen() {

      System.out.println("Das Auto fährt.");

    }

}

Das Interface beschreibt also nur die Fähigkeit, dass Menschen und Autos sich bewegen können.

 

Beispiel mit abstrakter Klasse

Hier haben alle Fahrzeuge gemeinsame Eigenschaften:

abstract class Fahrzeug {

    String kennzeichen;

    public void tanken() {

      System.out.println("Fahrzeug wird betankt.");

    }

    public abstract void fahren();

}

Abgeleitete Klassen erben die gemeinsamen Bestandteile:

class Pkw extends Fahrzeug {

    public void fahren() {

      System.out.println("PKW fährt.");

    }

}

class Lkw extends Fahrzeug {

    public void fahren() {

      System.out.println("LKW fährt.");

    }

}

Hier gilt:

  • Ein PKW ist ein Fahrzeug.
  • Ein LKW ist ein Fahrzeug.

Wann verwendet man was?

Interface

Wenn verschiedene Klassen eine gemeinsame Fähigkeit besitzen:

interface Druckbar

interface Beweglich

interface Speicherbar

Beispiele:

  • Dokumente sind druckbar.
  • Rechnungen sind druckbar.
  • Fotos sind druckbar.

Diese Klassen haben sonst möglicherweise gar nichts miteinander zu tun.

Abstrakte Klasse

Wenn mehrere Klassen viele gemeinsame Attribute und Methoden besitzen:

abstract class Person

abstract class Fahrzeug

abstract class Tier

Beispiele:

  • Studierende und Dozierende sind Personen.
  • PKW und LKW sind Fahrzeuge.

Hier gibt es gemeinsame Attribute und Methoden, die vererbt werden können.

Zusammenfassung

Abstrakte Klasse: Beschreibt, was Objekte gemeinsam haben. Beispiel: PKW und LKW sind Fahrzeuge.

Interface: Beschreibt, was Objekte gemeinsam können. Beispiel: Mensch, Auto und Fahrrad können sich bewegen.

Deshalb lautet eine häufige Faustregel:

  • "ist ein" -> abstrakte Klasse
  • "kann" -> Interface

Beispiele:

class Pkw extends Fahrzeug

Ein PKW ist ein Fahrzeug.

class Pkw implements Beweglich

Ein PKW kann sich bewegen.

 

24.3 Aufgabe 19 - 1:1

Einführung der einseitigen 1:1-Beziehung

24.3.1 Aufgabenstellung

Erstellen Sie die beiden Klassen Stud und Adresse. Eine Adresse soll folgende Attribute besitzen:

int adrNr;

String strasse;

String ort;

Ein Studierender soll folgende Attribute besitzen:

int matrNr;

String name;

Zwischen den Klassen soll eine 1:1-Beziehung eingerichtet werden. D.h., jedem Objekt der Klasse Stud soll genau ein Objekt der Klasse Adresse zugeordnet werden. Implementieren Sie geeignete Konstruktoren und eine Methode anzeigen(), die die Daten des Studierenden zusammen mit seiner Adresse ausgibt. Testen Sie das Zusammenspiel der Klassen im Hauptprogramm.

Es genügt diese eine Richtung.

Exkurs: Lesen Sie [Staud 2019] bzw. [OO2025]. Dort wird die Unterscheidung zwischen Kardinalitäten und Min-/Max-Angaben dargestellt. Eine Beziehung 1:1 (eigentlich: Kardinalität) kann, ausgedrückt in Min-/Max-Angaben folgendes bedeuten: 0,1 : 0,1, 1,1 : 0,1, 1,1 : 1,1. Welche Lösung ist hier realisiert?

24.3.2 Lernziele

In dieser Aufgabe lernen Sie, eine 1:1-Beziehung zwischen zwei Klassen in Java zu realisieren. Sie verstehen, wie ein Objekt über eine Referenz mit genau einem anderen Objekt verknüpft wird und wie auf die Daten des verknüpften Objekts zugegriffen werden kann.

 

24.3.3 Lösung

Klasse Adresse

package hochschule;

class Adresse {int adrNr; String strasse; String ort;

  Adresse(int adrNr, String strasse, String ort) {

    this.adrNr = adrNr;

    this.strasse = strasse;

    this.ort = ort;

  }

}

Klasse Stud

package hochschule;

class Stud {

   int matrNr;

   String name;

  

Verknüpfung zur Adresse (1:1)

  Adresse adresse;

  Stud(int matrNr, String name, Adresse adresse) {

    this.matrNr = matrNr;

    this.name = name;

    this.adresse = adresse;

  }

  void anzeigen() {

    System.out.println("Matrikelnummer: " + matrNr);

    System.out.println("Name: " + name);

    

    System.out.println("Adresse:");

    System.out.println("AdrNr: " + adresse.adrNr);

    System.out.println(adresse.strasse);

    System.out.println(adresse.ort);

  }

}

Hauptprogramm

package hochschule;

public class HP {

  public static void main(String[] args) {

    Adresse adr1 =

      new Adresse(501, "Musterstraße 12", "Passau");

    Stud s1 =

      new Stud(1001, "Eva Müller", adr1);

    s1.anzeigen();

  }

}

Lösung zur Frage im obigen Exkurs
Durch die Hinterlegung des Adressenobjekts beim Studierenden muss jeder Studierende genau eine Adresse besitzen. Auf der Seite der Studierenden liegt daher eine Min-/Max-Angabe von 1,1 vor.
In der Klasse Adresse ist kein Verweis auf einen Studierenden vorhanden. Die Beziehung ist daher nur in einer Richtung modelliert (unidirektional). Aus dem Programm allein lässt sich deshalb keine eindeutige Min-/Max-Angabe für die Adressseite ableiten. Inhaltlich ist jedoch beabsichtigt, eine 1:1-Beziehung abzubilden. Eine vollständige Umsetzung der 1:1-Beziehung erfolgt erst in der nächsten Aufgabe durch die Gegenreferenz in der Klasse Adresse.

24.3.4 Ergebnis Programmlauf

Matrikelnummer: 1001

Name: Eva Müller

Adresse:

AdrNr: 501

Musterstraße 12

Passau

24.3.5 Betrachtete Themen

  • Attribute
  • Bildschirmausgabe mit System.out.println()
  • Einfache 1:1-Beziehung
  • Klassen
  • Klassenverknüpfungen
  • Konstruktoren
  • Methoden
  • Navigation von Stud zu Adresse
  • Objekt als Attribut
  • Objektattribute
  • Objekte
  • Objekterzeugung mit new
  • Objektreferenzen
  • Übergabe von Objekten als Parameter
  • Unidirektionale Beziehung

24.3.6 Erläuterung

Die eigentliche 1:1-Verknüpfung befindet sich in der Klasse Stud:

Adresse adresse;

Hier wird keine Straße und kein Ort gespeichert, sondern eine Referenz auf ein Objekt der Klasse Adresse. Beim Erzeugen des Studierenden wird die Adresse übergeben:

Stud s1 = new Stud(1001,

  "Eva Müller",

  adr1);

Der Studierende "kennt" dadurch seine Adresse und kann über die Referenz auf deren Attribute zugreifen:

adresse.strasse

adresse.ort

adresse.adrNr

Theorieaspekte

Diese Aufgabe zeigt die direkte Objektverknüpfung zwischen zwei Klassen. Im Unterschied zu einer Zuordnung über Schlüsselwerte wird hier nicht nur eine Nummer gespeichert, sondern ein Objekt verweist direkt auf ein anderes Objekt. Damit wird deutlich:

Beziehungen zwischen Objekten werden in Java durch Referenzen realisiert.

 

24.4 Aufgabe 19b - Bidirektional

Erweiterung von Aufgabe 19 zur bidirektionalen 1:1-Beziehung

24.4.1 Aufgabenstellung

Erweitern Sie die Lösung aus Aufgabe 19 so, dass die Beziehung zwischen den Klassen Stud und Adresse bidirektional wird. Bisher "kennt" jeder Studierende seine Adresse. Nun soll zusätzlich jede Adresse den zugehörigen Studierenden "kennen".

Ergänzen Sie dazu in der Klasse Adresse ein Attribut vom Typ Stud. Setzen Sie im Hauptprogramm beide Verweise so, dass von einem Studierenden auf seine Adresse und von einer Adresse auf ihren Studierenden zugegriffen werden kann.

Erstellen Sie außerdem eine Methode

void anzeigenStud()

in der Klasse Adresse, welche die Daten des zugehörigen Studierenden ausgibt. Testen Sie die Navigation in beide Richtungen im Hauptprogramm.

Frage: Welche Lösung ist hier bzgl. Studierenden und Adressen tatsächlich realisiert?

24.4.2 Lernziele

In dieser Aufgabe lernen Sie, eine bidirektionale 1:1-Beziehung zwischen zwei Klassen zu realisieren. Sie verstehen, wie sich zwei Objekte gegenseitig referenzieren und wie dadurch der Zugriff von beiden Seiten der Beziehung möglich wird. Dabei vertiefen Sie Ihr Verständnis von Objektverknüpfungen und Referenzen in der objektorientierten Programmierung.

24.4.3 Lösung

Klasse Adresse

package hochschule;

class Adresse {

  int adrNr;

  String strasse;

  String ort;

Rückverweis zum Studierenden (1:1)

  Stud stud;

  Adresse(int adrNr, String strasse, String ort) {

    this.adrNr = adrNr;

    this.strasse = strasse;

    this.ort = ort;

  }

  void anzeigenStud() {

    System.out.println("Zugehöriger Studierender:");

    System.out.println("Matrikelnummer: " + stud.matrNr);

    System.out.println("Name: " + stud.name);

  }

}

Klasse Stud

package hochschule;

class Stud {

  int matrNr;

  String name;

  

Verknüpfung zur Adresse (1:1)

  Adresse adresse;

  

  Stud(int matrNr, String name, Adresse adresse) {

    this.matrNr = matrNr;

    this.name = name;

    this.adresse = adresse;

  }

  void anzeigen() {

    System.out.println("Matrikelnummer: " + matrNr);

    System.out.println("Name: " + name);

    

    System.out.println("Adresse:");

    System.out.println("AdrNr: " + adresse.adrNr);

    System.out.println(adresse.strasse);

    System.out.println(adresse.ort);

  }

}

Hauptprogramm

package hochschule;

Natürlich kann man das jeweilige Hauptprogramm auch HP nennen.

public class HP {

  public static void main(String[] args) {

    Adresse adr1 =

      new Adresse(501, "Musterstraße 12", "Passau");

    

    Stud s1 =

      new Stud(1001, "Eva Müller", adr1);

    

Zweite Richtung der Beziehung setzen

      adr1.stud = s1;

    

    System.out.println("Ausgabe vom Studierenden aus:");

      s1.anzeigen();

    System.out.println("\n---------------------\n");

    

    System.out.println("Ausgabe von der Adresse aus:");

    adr1.anzeigenStud();

  }

}

Lösung zur Frage oben
Im Gegensatz zur vorherigen Aufgabe existieren nun Referenzen in beide Richtungen. Damit wird die Min-/Max-Kombination 1,1 : 1,1 modelliert. Das Programm überprüft diese Bedingung allerdings nicht automatisch, sondern setzt voraus, dass die Objekte korrekt miteinander verknüpft werden.

24.4.4 Ergebnis Programmlauf

Ausgabe vom Studierenden aus:

Matrikelnummer: 1001

Name: Eva Müller

Adresse:

AdrNr: 501

Musterstraße 12

Passau

---------------------

Ausgabe von der Adresse aus:

Zugehöriger Studierender:

Matrikelnummer: 1001

Name: Eva Müller

24.4.5 Betrachtete Themen

  • bidirektionale Beziehung
  • einfache 1:1-Beziehung
  • Konsistenz von Objektverknüpfungen
  • Konstruktoren
  • Mehrere Klassen
  • Methodenaufrufe
  • nachträgliches Setzen einer Beziehung
  • Navigation in beide Richtungen
  • Objekte als Attribute
  • Objekte und Objektreferenzen
  • objektorientierte Modellierung
  • Referenzvariablen
  • Rückverweis

24.4.6 Erläuterungen

Die eigentlichen Programmelemente für die bidirektionale Beziehung sind:

  • In Stud: Adresse adresse;
  • In Adresse: Stud stud;

und im Hauptprogramm:

Stud s1 = new Stud(1001, "Eva Müller", adr1);

adr1.stud = s1;

Damit gilt: Von beiden Objekten kann nun auf das andere geschlossen werden, womit die bidirektionale 1:1-Beziehung realisiert ist.

Das ist aber nur der Anfang. Beim Einrichten einer solchen Beziehung müsste überprüft werden, ob wirklich alle Objekte an der Beziehung teilhaben (dies bedeutet 1,1 : 1,1), ob also nicht

  • 0,1 : 1,1
  • 0,1 : 0,1 oder
  • 1,1 : 0,1

vorliegen. Das müsste abgefangen werden.

 

24.5 Aufgabe 20 - n:m

N:m-Beziehung mit den Klassen Stud und Kurs.

24.5.1 Aufgabenstellung

Erstelle ein Programm, das die n:m-Beziehung zwischen Studierenden und Kursen vollumfänglich erfasst:

  • Ein Studierender besucht mehrer Kurse
  • Ein Kurs wird von mehreren Studierenden besucht.

Lege die beiden Klassen Stud und Kurs so an, dass die Daten des Programmlaufs sich wie folgt ergeben:

Eva besucht folgende Kurse:

  Programmierung 1

  Datenbanken

Paul besucht folgende Kurse:

  Programmierung 1

Kurs: Programmierung 1

  Eva

  Paul

Kurs: Datenbanken

Eva

Guiseppe

Nutzen Sie für die Verknüpfung jeweils eine ArrayList.

24.5.2 Lernziele

Hier wird gezeigt, wie eine n:m-Beziehung zwischen zwei Klassen in Java umgesetzt wird. Sie verstehen, wie mehrere Objekte einer Klasse mit mehreren Objekten einer anderen Klasse verknüpft werden können und wie diese Beziehungen mithilfe von Listen verwaltet werden.

 

24.5.3 Lösung

Klasse Student

package studierende;

import java.util.ArrayList;

import java.util.List;

class Student {

    String name;

Verknüpfung zu mehreren Kursen

    List<Kurs> kurse = new ArrayList<>();

    Student(String name) {

      this.name = name;

    }

    void addKurs(Kurs kurs) {

      kurse.add(kurs);

    }

    void anzeigen() {

      System.out.println(name + " besucht folgende Kurse:");

      for (Kurs k : kurse) {

        System.out.println(" " + k.name);

      }

    }

}

Klasse Kurs

package studierende;

 

import java.util.ArrayList;

import java.util.List;

 

class Kurs {

    String name;

Verknüpfung zu mehreren Studierenden

    List<Student> studenten = new ArrayList<>();

    Kurs(String name) {

      this.name = name;

    }

    void addStudent(Student student) {

      studenten.add(student);

    }

    void anzeigen() {

      System.out.println("Kurs: " + name);

      for (Student s : studenten) {

        System.out.println(" " + s.name);

      }

    }

}

HP (Hauptprogramm)

package studierende;

public class HP {

    public static void main(String[] args) {

      Kurs k1 = new Kurs("Programmierung 1");

      Kurs k2 = new Kurs("Datenbanken");

      Student s1 = new Student("Eva");

      Student s2 = new Student("Paul");

      Student s3 = new Student("Guiseppe");

Beziehungen herstellen

      s1.addKurs(k1);

      s1.addKurs(k2);

      s2.addKurs(k1);

      k1.addStudent(s1);

      k1.addStudent(s2);

      k2.addStudent(s1);

      k2.addStudent(s3);

Ausgaben

      s1.anzeigen();

      System.out.println();

      s2.anzeigen();

      System.out.println();

      k1.anzeigen();

      System.out.println();

      k2.anzeigen();

    }

}

24.5.4 Betrachtete Themen

  • ArrayList
  • Ausgabe verknüpfter Objekte
  • Bidirektionale Beziehung
  • foreach-Schleife
  • Generics (List<Kurs>, List<Student>)
  • Konsistenz von Beziehungen
  • List
  • Mehrere Klassen
  • Methoden zum Herstellen von Beziehungen
  • n:m-Beziehung
  • Navigation in beide Richtungen
  • Objekte als Listenelemente
  • Objekte und Objektreferenzen
  • Objektorientierte Modellierung

24.5.5 Erläuterung

Diese Lösung entspricht einer bidirektionalen n:m-Beziehung. Die Beziehung wird zweimal gespeichert:

  • Student "kennt" seine Kurse.
  • Kurs "kennt" seine Studierenden.

Das ist sehr anschaulich, führt aber dazu, dass beim Anlegen einer Beziehung beide Listen gepflegt werden müssen:

s1.addKurs(k1);

k1.addStudent(s1);

Als elegantere Lösung kann eine Assoziationsklasse genutzt werden (vgl. Aufgabe4e).

 

24.6 Aufgabe 21 - Klassendiagramm HS

Vom Klassendiagramm zu Java

24.6.1 Aufgabe

Das folgende Klassendiagramm stammt aus [Staud 2019]. Es ist die Abbildung 3.8-1. Vgl. dort für eine Beschreibung.

Erstellen Sie ein Java-Programm, das dieses Klassendiagramm in wesentlichen Teilen umsetzt. Die Beziehungen werden nicht datenbankartig durch Fremdschlüssel realisiert, sondern objektorientiert durch Objektreferenzen und Listen.

Legen Sie Beispieldaten an (zwei Studierende) und demonstrieren Sie den Methodeneinsatz.

Quelle: [Staud 2019, Abbildung 3.8-1]

 

24.6.2 Lernziele

Sie sehen, wie ein UML-Klassendiagramm systematisch in Java-Klassen umgesetzt wird, wie Klassen, Attribute, Methoden und Beziehungen aus einem Modell in Programmcode übertragen werden. Dadurch vertiefen sie ihr Verständnis der objektorientierten Modellierung und erkennen den Zusammenhang zwischen der inhaltlichen Beschreibung eines Anwendungsbereichs und seiner Implementierung in Java.

24.6.3 Lösung

Eine einfache Java-Umsetzung des UML-Diagramms sieht so aus, dabei werden die n:m-Beziehungen mit List<> umgesetzt. Das Prüfungsdreieck wird als eigene Klasse Pruefungsgeschehen modelliert.

Die einzelnen Klassen und Programme sind in einer Datei HochschuleDemo.java zusammengefasst. Das dient hier der vereinfachten Darstellung und Demonstrationszwecken, ist aber i.d.R. nicht empfehlenswert.

Aufpassen: Bei der Übernahme des Programms per Copy/Paste können Trennstriche die Java-Entwicklungsumgebung irritieren.

HochschuleDemo.java

package hochschule;

import java.time.LocalDate;

import java.util.ArrayList;

import java.util.List;

class Dozent {

  int dozNr;

  String anrede, name, vorname, ort, plz, strasse, email;

  

  List<Lehrveranstaltung> lehrveranstaltungen = new ArrayList<>();

  List<Studierender> betreuteStudierende = new ArrayList<>();

  Dozent(int dozNr, String name, String vorname) {

    this.dozNr = dozNr;

    this.name = name;

    this.vorname = vorname;

  }

  void haltenVorlesung(Lehrveranstaltung lv) {

    lehrveranstaltungen.add(lv);

    lv.dozenten.add(this);

  }

  void betreuen(Studierender s) {

    betreuteStudierende.add(s);

    s.betreuer = this;

  }

}

class Studierender {

  String matrikelNr, name, vname, plz, ort, strasse, studiengang;

  LocalDate beginnStudium, endeStudium;

  int fachsem;

  

  Dozent betreuer;

  

  List<Lehrveranstaltung> besuchteLehrveranstaltungen = new ArrayList<>();

  List<Pruefungsgeschehen> pruefungen = new ArrayList<>();

  Studierender(String matrikelNr, String name, String vname) {

    this.matrikelNr = matrikelNr;

    this.name = name;

    this.vname = vname;

  }

  void immatrikulieren() {

    beginnStudium = LocalDate.now();

    System.out.println(vname + " " + name + " wurde immatrikuliert.");

  }

  void exmatrikulieren() {

    endeStudium = LocalDate.now();

    System.out.println(vname + " " + name + " wurde exmatrikuliert.");

  }

  void besucht(Lehrveranstaltung lv) {

    besuchteLehrveranstaltungen.add(lv);

    lv.studierende.add(this);

  }

  

  int anzahl() {

    return pruefungen.size();

  }

  }

  

class Lehrveranstaltung {

  String bezLV;

  String studiengang;

  int semesterSPO;

  

  List<Dozent> dozenten = new ArrayList<>();

  List<Studierender> studierende = new ArrayList<>();

  List<Pruefungsgeschehen> pruefungen = new ArrayList<>();

  

  Lehrveranstaltung(String bezLV, String studiengang, int semesterSPO) {

    this.bezLV = bezLV;

    this.studiengang = studiengang;

    this.semesterSPO = semesterSPO;

    }

}

  

class Pruefungstyp {

  int prTypNr;

  String bezPruef;

  int laenge;

  

  Pruefungstyp(int prTypNr, String bezPruef, int laenge) {

    this.prTypNr = prTypNr;

    this.bezPruef = bezPruef;

    this.laenge = laenge;

  }

  }

  

  class Pruefungsgeschehen {

    Studierender pruefungsteilnehmer;

    Lehrveranstaltung pruefungsgegenstand;

    Pruefungstyp pruefungstyp;

    int note;

  

  Pruefungsgeschehen(

      Studierender pruefungsteilnehmer,

      Lehrveranstaltung pruefungsgegenstand,

      Pruefungstyp pruefungstyp,

      int note) {

    

    this.pruefungsteilnehmer = pruefungsteilnehmer;

    this.pruefungsgegenstand = pruefungsgegenstand;

    this.pruefungstyp = pruefungstyp;

    this.note = note;

    

    pruefungsteilnehmer.pruefungen.add(this);

    pruefungsgegenstand.pruefungen.add(this);

  }

  

  void anzeigen() {

    System.out.println(

      pruefungsteilnehmer.vname + " " + pruefungsteilnehmer.name

      + " | " + pruefungsgegenstand.bezLV

      + " | " + pruefungstyp.bezPruef

      + " | Note: " + note);

    }

  }

  

  public class HochschuleDemo {

    public static void main(String[] args) {

  

    Dozent d1 = new Dozent(1, "Müller", "Anna");

  

    Studierender s1 = new Studierender("1001", "Maier", "Paul");

    Studierender s2 = new Studierender("1002", "Schulz", "Eva");

    

    Lehrveranstaltung lv1 =

      new Lehrveranstaltung("Objektorientierung", "Wirtschaftsinformatik", 2);

  

  Pruefungstyp pt1 = new Pruefungstyp(1, "Klausur", 90);

    

    d1.haltenVorlesung(lv1);

    

    s1.immatrikulieren();

    s2.immatrikulieren();

    

    s1.besucht(lv1);

    s2.besucht(lv1);

    

    d1.betreuen(s1);

  

    Pruefungsgeschehen p1 = new Pruefungsgeschehen(s1, lv1, pt1, 2);

  

    p1.anzeigen();

  

    System.out.println("Anzahl Prüfungen von "

      + s1.vname + " " + s1.name + ": " + s1.anzahl());

    }

  }

}

24.6.4 Ergebnis Programmlauf

Paul Maier wurde immatrikuliert.

Eva Schulz wurde immatrikuliert.

Paul Maier | Objektorientierung | Klausur | Note: 2

Anzahl Prüfungen von Paul Maier: 1

24.6.5 Betrachtete Themen

  • Assoziationen
  • Assoziationsklasse / Beziehungsklasse
  • Attribute
  • bidirektionale Verknüpfungen
  • Klassen
  • Konstruktoren
  • List und ArrayList
  • Listen zur Umsetzung mehrerer Beziehungen
  • LocalDate
  • Methoden
  • Methodenaufrufe
  • n:m-Beziehungen
  • Objekte
  • Objekte als Listenelemente
  • objektorientierter Entwurf
  • Objektreferenzen
  • UML-Klassendiagramme
  • Umsetzung von Modellen in Java

 

25 Übungsaufgaben Teil 6

25.1 Aufgabe 22 - Liste

Einfach verkettete Liste

25.1.1 Aufgabenstellung

Ein Zug besteht aus mehreren Waggons. Jeder Waggon "kennt" den jeweils nächsten. Dadurch entsteht eine einfach verkettete Liste.

Erstellen Sie ein Java-Programm mit den Klassen Waggon und AufgabeWaggons. Das Programm soll zunächst einen Zug mit drei Waggons erzeugen:

1 Lokomotive -> 2 Personenwagen -> 3 Speisewagen -> null

Danach soll über eine grafische Eingabemaske ausgewählt werden können:

1. Zug anzeigen

2. Waggon am Ende anhängen

3. Waggon nach einer bestimmten Waggonnummer einfügen

4. Waggon suchen

5. Waggon entfernen

6. Programm beenden

 

 

 

 

usw.

25.1.2 Lernziele

Sie lernen das Prinzip der einfach verketteten Liste kennen, verstehen, wie Objekte über Referenzen zu einer Kette verbunden werden und wie sich Elemente am Ende oder in der Mitte einfügen, suchen und entfernen lassen. Dadurch vertiefen sie ihr Verständnis dynamischer Datenstrukturen und erkennen den Unterschied zwischen verketteten Listen und Arrays.

25.1.3 Lösung

Klasse Waggon

package zug;

class Waggon {

    int nummer;

    String typ;

Verweis auf den nächsten Waggon

    Waggon naechster;

    Waggon(int nummer, String typ) {

      this.nummer = nummer;

      this.typ = typ;

      this.naechster = null;

    }

}

Hauptprogramm

package zug;

import javax.swing.JOptionPane;

public class AufgabeWaggons {

    public static void main(String[] args) {

Anfangsliste erstellen

      Waggon w1 = new Waggon(1, "Lokomotive");

      Waggon w2 = new Waggon(2, "Personenwagen");

      Waggon w3 = new Waggon(3, "Speisewagen");

      w1.naechster = w2;

      w2.naechster = w3;

      w3.naechster = null;

Kopf der Liste

      Waggon erster = w1;

      boolean weiter = true;

      while (weiter) {

        String auswahl = JOptionPane.showInputDialog(

        "Verkettete Liste: Zug mit Waggons\n\n"

         + "1 - Zug anzeigen\n"

         + "2 - Waggon am Ende anhängen\n"

         + "3 - Waggon nach einer bestimmten Waggonnummer einfügen\n"

         + "4 - Waggon suchen\n"

         + "5 - Waggon entfernen\n"

         + "6 - Programm beenden\n\n"

         + "Bitte Auswahl eingeben:"

        );

        if (auswahl == null) {

        weiter = false;

        } else if (auswahl.equals("1")) {

        zugAnzeigen(erster);

        } else if (auswahl.equals("2")) {

        erster = waggonAnhaengen(erster);

        } else if (auswahl.equals("3")) {

        erster = waggonEinfuegen(erster);

        } else if (auswahl.equals("4")) {

        waggonSuchen(erster);

        } else if (auswahl.equals("5")) {

        erster = waggonEntfernen(erster);

        } else if (auswahl.equals("6")) {

        weiter = false;

        } else {

        JOptionPane.showMessageDialog(null, "Ungültige Eingabe.");

        }

      }

    }

    static void zugAnzeigen(Waggon erster) {

      String ausgabe = "Aktueller Zug:\n\n";

      Waggon aktuell = erster;

      while (aktuell != null) {

        ausgabe = ausgabe

        + aktuell.nummer + " "

        + aktuell.typ + "\n";

        aktuell = aktuell.naechster;

      }

      JOptionPane.showMessageDialog(null, ausgabe);

    }

    static Waggon waggonAnhaengen(Waggon erster) {

      int nummer = Integer.parseInt(

        JOptionPane.showInputDialog("Nummer des neuen Waggons:")

      );

      String typ = JOptionPane.showInputDialog("Typ des neuen Waggons:");

      Waggon neuer = new Waggon(nummer, typ);

      if (erster == null) {

        erster = neuer;

      } else {

        Waggon aktuell = erster;

        while (aktuell.naechster != null) {

        aktuell = aktuell.naechster;

        }

        aktuell.naechster = neuer;

      }

      return erster;

    }

    static Waggon waggonEinfuegen(Waggon erster) {

      int nachNummer = Integer.parseInt(

        JOptionPane.showInputDialog(

        "Nach welcher Waggonnummer soll eingefügt werden?"

        )

      );

      int neueNummer = Integer.parseInt(

        JOptionPane.showInputDialog("Nummer des neuen Waggons:")

      );

      String typ = JOptionPane.showInputDialog("Typ des neuen Waggons:");

      Waggon aktuell = erster;

      while (aktuell != null amp;amp; aktuell.nummer != nachNummer) {

        aktuell = aktuell.naechster;

      }

      if (aktuell == null) {

        JOptionPane.showMessageDialog(null, "Waggon nicht gefunden.");

      } else {

        Waggon neuer = new Waggon(neueNummer, typ);

        neuer.naechster = aktuell.naechster;

        aktuell.naechster = neuer;

      }

      return erster;

    }

    static void waggonSuchen(Waggon erster) {

      int suchNummer = Integer.parseInt(

        JOptionPane.showInputDialog("Welche Waggonnummer wird gesucht?")

      );

      Waggon aktuell = erster;

      while (aktuell != null amp;amp; aktuell.nummer != suchNummer) {

        aktuell = aktuell.naechster;

      }

      if (aktuell == null) {

        JOptionPane.showMessageDialog(null, "Waggon nicht gefunden.");

      } else {

        JOptionPane.showMessageDialog(

        null,

        "Gefunden:\n"

        + aktuell.nummer + " "

        + aktuell.typ

        );

      }

    }

    static Waggon waggonEntfernen(Waggon erster) {

      int suchNummer = Integer.parseInt(

        JOptionPane.showInputDialog("Welche Waggonnummer soll entfernt werden?")

      );

      if (erster == null) {

        JOptionPane.showMessageDialog(null, "Der Zug ist leer.");

        return erster;

      }

      // Sonderfall: erster Waggon wird entfernt

      if (erster.nummer == suchNummer) {

        erster = erster.naechster;

        JOptionPane.showMessageDialog(null, "Erster Waggon wurde entfernt.");

        return erster;

      }

      Waggon aktuell = erster;

      while (aktuell.naechster != null

        amp;amp; aktuell.naechster.nummer != suchNummer) {

        aktuell = aktuell.naechster;

      }

      if (aktuell.naechster == null) {

        JOptionPane.showMessageDialog(null, "Waggon nicht gefunden.");

      } else {

        aktuell.naechster = aktuell.naechster.naechster;

        JOptionPane.showMessageDialog(null, "Waggon wurde entfernt.");

      }

      return erster;

    }

}

25.1.4 Betrachtete Themen

  • Einfach verkettete Liste
  • Selbstreferenzierende Klasse
  • Objektreferenzen
  • Verweis auf das nächste Objekt
  • Kopf der Liste
  • null als Listenende
  • Durchlaufen einer Liste
  • while-Schleife
  • Anhängen am Ende
  • Einfügen nach einem bestimmten Element
  • Suchen in einer Liste
  • Entfernen von Listenelementen
  • Sonderfall: erstes Element entfernen
  • Methoden mit Rückgabewert
  • Statische Hilfsmethoden
  • Benutzereingaben mit JOptionPane
  • Menüsteuerung
  • Objektorientierte Modellierung einer Datenstruktur

25.1.5 Anmerkungen

Eine wichtige Stelle in der Klasse Waggon ist Waggon naechster; Damit zeigt jedes Objekt der Klasse Waggon auf den nächsten Waggon. Das letzte Objekt verweist auf null:

w3.naechster = null;

Der Anfang der Liste wird durch eine eigene Referenz festgehalten:

Waggon erster = w1;

Didaktisch besonders wichtig sind außerdem diese beiden Fälle:

aktuell = aktuell.naechster;

zum Weitergehen in der Liste und

aktuell.naechster = aktuell.naechster.naechster;

zum Entfernen eines Waggons aus der Liste.

Zentrale Idee

Die Liste wird nicht durch ein Array gebildet, sondern durch Objektverweise:

Waggon naechster;

Ein Waggon zeigt auf den nächsten:

Lokomotive -> Personenwagen -> Speisewagen -> null

Beim Einfügen oder Entfernen werden nur die Verweise geändert. Genau das ist der Kern einer einfach verketteten Liste.

25.1.6 Exkurs - Verkettete Listen

Einfach verkettete Liste

Mit einfach verketteter Liste (singly linked list) meint man eine Liste, bei der jedes Element nur seinen Nachfolger kennt, siehe oben:

class Waggon {

    int nummer;

    Waggon naechster;

}

Graphisch:

Lok -> Personenwagen -> Speisewagen -> null

Jeder Waggon besitzt also einen Verweis auf den nächsten Waggon. Das letzte Element verweist auf null. Dadurch wird das Ende der Liste markiert.

Vorteil:

  • einfacher Aufbau
  • geringer Speicherbedarf

Nachteil:

  • man kann die Liste nur in Vorwärtsrichtung durchlaufen
  • der Vorgänger eines Elements ist nicht direkt bekannt

Die in Aufgabe 22 entwickelte Lösung stellt eine einfach verkettete Liste dar.

Doppelt verkettete Liste

Bei einer doppelt verketteten Liste (doubly linked list) kennt jedes Element sowohl seinen Nachfolger als auch seinen Vorgänger.

class Waggon {

    int nummer;

    Waggon vorheriger;

    Waggon naechster;

}

Graphisch:

null <- Lok <-> Personenwagen <-> Speisewagen -> null

Jeder Waggon "weiß" nun:

  • Wer vor ihm ist
  • Wer nach ihm ist

Man kann die Liste in beide Richtungen durchlaufen.

Ringliste (zyklische Liste)

Eine weitere Variante ist die zyklisch verkettete Liste. Hier zeigt das letzte Element wieder auf das erste:

Es gibt also kein null.

Doppelt verkettete Ringliste

Diese stellt eine Kombination aus beidem obigen dar: Ringliste und doppelt verkettet:

 

Von jedem Element kann auf den Vorgänger und Nachfolger geschlossen werden und die Liste bildet einen Kreis.

Wichtig: Bei verketteten Listen werden die Elemente nicht durch ein Array, sondern durch Objektreferenzen miteinander verbunden. Die Struktur der Liste entsteht ausschließlich durch die Verweise zwischen den einzelnen Objekten. Dies ist eines der grundlegenden Konzepte dynamischer Datenstrukturen.

25.2 Aufgabe 23 - Unternehmen

Klassendiagramm Unternehmen

25.2.1 Aufgabenstellung

Setzen Sie das unten angegebene Klassendiagramm in Java um. Fügen Sie beispielhafte Daten hinzu, sodass folgender Programmlauf möglich ist:

  • Ausgabe der Angestellten mit ihren Daten, dem zugeordneten PC, den Projekten, in denen sie mitarbeiten, sowie Vorgesetzten und Untergebenen.
  • Ausgabe der Abteilungen mit Abteilungsbezeichnung, Standort, Abteilungsleitung und Mitarbeitern.
  • Ausgabe der Projekte mit Projektbezeichnung, Standort und Mitarbeitern.

Das Klassendiagramm stammt aus [Staud 2019] und ist dort Abbildung 3.8-3. Siehe die Erläuterungen dort.

Abbildung 25.2-1: Klassendiagramm zum Anwendungsbereich Angestellte

Quelle: [Staud 2019, Abbildung 3.8-3] bzw. [OO2025]

25.2.2 Lernziele

Sie lernen, wie ein UML-Klassendiagramm in ein Java-Programm umgesetzt wird. Dabei wird deutlich, wie Klassen, Attribute und Beziehungen schrittweise in Java-Klassen, Referenzen und Objekte übertragen werden.

25.2.3 Lösung

Folgende Java-Programme richten das Klassendiagramm mit beispielhaften Daten ein.

Klasse HP (Hauptprogramm; main)

package firma;

import java.time.LocalDate;

import java.util.ArrayList;

import java.util.List;

public class HP {

    public static void main(String[] args) {

      Abteilung entwicklung = new Abteilung(

        "Entwicklung", "Dr. Weber", "Hannover"

      );

      Abteilung verwaltung = new Abteilung(

        "Verwaltung", "Frau König", "Bremen"

      );

      PC pc1 = new PC(1001, "Dell Optiplex", "Desktop");

      PC pc2 = new PC(1002, "Lenovo ThinkPad", "Notebook");

      Angestellter a1 = new Angestellter(

        1, "Müller", "Anna", "30159", "Hannover",

        "Marktstraße", LocalDate.of(2020, 3, 1),

        LocalDate.of(1995, 5, 12), 2500.00

      );

      Angestellter a2 = new Angestellter(

        2, "Schmidt", "Bernd", "28195", "Bremen",

        "Bahnhofstraße", LocalDate.of(2019, 7, 15),

        LocalDate.of(1990, 9, 3), 2800.00

      );

      Angestellter a3 = new Angestellter(

        3, "Meier", "Clara", "30161", "Hannover",

        "Parkweg", LocalDate.of(2021, 1, 10),

        LocalDate.of(1998, 2, 20), 2300.00

      );

      Projekt p1 = new Projekt("BPMN", "Hannover",

        LocalDate.of(2024, 1, 1));

      Projekt p2 = new Projekt("Prozessoptimierung", "Bremen",

        LocalDate.of(2024, 4, 1));

Angestellte einer Abteilung zuordnen

      entwicklung.addMitarbeiter(a1);

      entwicklung.addMitarbeiter(a2);

      verwaltung.addMitarbeiter(a3);

PC zuordnen

      a1.setPc(pc1);

      a2.setPc(pc2);

Projektzuordnungen

      a1.addProjekt(p1);

      a1.addProjekt(p2);

      a2.addProjekt(p1);

      a3.addProjekt(p2);

Vorgesetztenbeziehung

      a1.addUntergebener(a2);

      a1.addUntergebener(a3);

      a1.anzeigen();

      a2.anzeigen();

      a3.anzeigen();

      System.out.println("\nAbteilungen:");

      entwicklung.anzeigen();

      verwaltung.anzeigen();

      System.out.println("\nProjekte:");

      p1.anzeigen();

      p2.anzeigen();

    }

}

Klasse Abteilung

package firma;

import java.util.ArrayList;

import java.util.List;

class Abteilung {

    String abtBez;

    String abtLeitNa;

    String standort;

    List<Angestellter> mitarbeiter = new ArrayList<>();

    Abteilung(String abtBez, String abtLeitNa, String standort) {

      this.abtBez = abtBez;

      this.abtLeitNa = abtLeitNa;

      this.standort = standort;

    }

    void addMitarbeiter(Angestellter angestellter) {

      if (!mitarbeiter.contains(angestellter)) {

        mitarbeiter.add(angestellter);

        angestellter.setAbteilung(this);

      }

    }

    void anzeigen() {

      System.out.println(abtBez + " (" + standort + ")");

      System.out.println("Leitung: " + abtLeitNa);

      System.out.println("Mitarbeiter:");

      for (Angestellter a : mitarbeiter) {

        System.out.println("- " + a.vname + " " + a.name);

      }

    }

}

Klasse Angestellter

package firma;

import java.time.LocalDate;

import java.util.ArrayList;

import java.util.List;

class Angestellter {

    int persNr;

    String name;

    String vname;

    String plz;

    String ort;

    String strasse;

    LocalDate datumEinst;

    LocalDate gebTag;

    double gehalt;

    Abteilung abteilung;

    PC pc;

    List<Projekt> projekte = new ArrayList<>();

    Angestellter vorgesetzter;

    List<Angestellter> untergebene = new ArrayList<>();

    Angestellter(int persNr, String name, String vname,

        String plz, String ort, String strasse,

        LocalDate datumEinst, LocalDate gebTag,

        double gehalt) {

      this.persNr = persNr;

      this.name = name;

      this.vname = vname;

      this.plz = plz;

      this.ort = ort;

      this.strasse = strasse;

      this.datumEinst = datumEinst;

      this.gebTag = gebTag;

      this.gehalt = gehalt;

    }

    void addProjekt(Projekt projekt) {

      if (!projekte.contains(projekt)) {

        projekte.add(projekt);

        projekt.addMitarbeiter(this);

      }

    }

    void setAbteilung(Abteilung abteilung) {

      this.abteilung = abteilung;

    }

    void setPc(PC pc) {

      this.pc = pc;

    }

    void addUntergebener(Angestellter angestellter) {

      untergebene.add(angestellter);

      angestellter.vorgesetzter = this;

    }

    void anzeigen() {

      System.out.println("\nAngestellter: " + vname + " " + name);

      System.out.println("Personalnummer: " + persNr);

      System.out.println("Gehalt: " + gehalt);

      if (abteilung != null) {

        System.out.println("Abteilung: " + abteilung.abtBez);

      }

      if (pc != null) {

        System.out.println("PC: " + pc.bez);

      }

      System.out.println("Projekte:");

      for (Projekt p : projekte) {

        System.out.println("- " + p.bez);

      }

      if (vorgesetzter != null) {

        System.out.println("Vorgesetzter: "

    + vorgesetzter.vname + " " + vorgesetzter.name);

      }

      if (!untergebene.isEmpty()) {

        System.out.println("Untergebene:");

        for (Angestellter u : untergebene) {

        System.out.println("- " + u.vname + " " + u.name);

        }

      }

    }

}

Klasse Projekt

package firma;

import java.time.LocalDate;

import java.util.ArrayList;

import java.util.List;

class Projekt {

    String bez;

    String standort;

    LocalDate tagEinrichtung;

    List<Angestellter> mitarbeiter = new ArrayList<>();

    Projekt(String bez, String standort, LocalDate tagEinrichtung) {

      this.bez = bez;

      this.standort = standort;

      this.tagEinrichtung = tagEinrichtung;

    }

    void addMitarbeiter(Angestellter angestellter) {

      if (!mitarbeiter.contains(angestellter)) {

        mitarbeiter.add(angestellter);

      }

    }

    void anzeigen() {

      System.out.println(bez + " (" + standort + ")");

      System.out.println("Mitarbeiter:");

      for (Angestellter a : mitarbeiter) {

        System.out.println("- " + a.vname + " " + a.name);

      }

    }

}

Klasse PC

package firma;

class PC {

    int invNr;

    String bez;

    String typ;

    PC(int invNr, String bez, String typ) {

      this.invNr = invNr;

      this.bez = bez;

      this.typ = typ;

    }

}

25.2.4 Ergebnis Programmlauf

Angestellter: Anna Müller

Personalnummer: 1

Gehalt: 2500.0

Abteilung: Entwicklung

PC: Dell Optiplex

Projekte:

- BPMN

- Prozessoptimierung

Untergebene:

- Bernd Schmidt

- Clara Meier

Angestellter: Bernd Schmidt

Personalnummer: 2

Gehalt: 2800.0

Abteilung: Entwicklung

PC: Lenovo ThinkPad

Projekte:

- BPMN

Vorgesetzter: Anna Müller

Angestellter: Clara Meier

Personalnummer: 3

Gehalt: 2300.0

Abteilung: Verwaltung

Projekte:

- Prozessoptimierung

Vorgesetzter: Anna Müller

Abteilungen:

Entwicklung (Hannover)

Leitung: Dr. Weber

Mitarbeiter:

- Anna Müller

- Bernd Schmidt

Verwaltung (Bremen)

Leitung: Frau König

Mitarbeiter:

- Clara Meier

Projekte:

BPMN (Hannover)

Mitarbeiter:

- Anna Müller

- Bernd Schmidt

Prozessoptimierung (Bremen)

Mitarbeiter:

- Anna Müller

- Clara Meier

25.2.5 Betrachtete Themen

  • 1:1-Beziehung
  • 1:n-Beziehung
  • Attribute und Methoden
  • bidirektionale Beziehungen
  • foreach-Schleife
  • Konstruktoren
  • List als Schnittstellentyp
  • Listen mit ArrayList
  • LocalDate
  • mehrere Klassen
  • n:m-Beziehung
  • Objekte als Listenelemente
  • Objekte und Objektreferenzen
  • objektorientierte Modellierung
  • Pflege konsistenter Objektverknüpfungen
  • Selbstbeziehung
  • Umsetzung eines UML-Klassendiagramms in Java
  • Vermeidung doppelter Listeneinträge mit contains()

25.2.6 Anmerkung

In dieser Aufgabe werden mehrere Beziehungstypen kombiniert. Eine Abteilung besitzt mehrere Angestellte. Ein Angestellter gehört zu einer Abteilung. Zwischen Angestellten und Projekten besteht eine n:m-Beziehung. Außerdem gibt es eine rekursive Beziehung bei Angestellten: Ein Angestellter kann Vorgesetzter anderer Angestellter sein.

 

Aufgabe 24 - Beschäftigung

Klassen verknüpfen

25.2.7 Aufgabenstellung

Hier geht es um Abteilungen und Angestellte und um die 1:n-Beziehung zwischen ihnen. Erstellen Sie die Klassen:

Angestellter mit den Attributen:

  • persNr
  • name
  • vName

und Abteilung mit den Attributen:

  • abtBez
  • standort

Legen Sie zwei Abteilungen und drei Angestellte an. Ordnen Sie jeden Angestellten genau einer Abteilung zu. Lassen Sie vom Programm die Angestellten mit ihrer Abteilungszugehörigkeit ausgeben. Etwa so:

Anna Müller arbeitet in der Abteilung Entwicklung.

25.2.8 Lernziele

Hier wird gezeigt, wie Klassen miteinander verknüpft werden, um einen realitätsnahen Anwendungsbereich abzubilden, wie also Beziehungen zwischen Objekten mithilfe von Referenzen und Sammlungen modelliert und verwaltet werden.

 

25.2.9 Lösung

package firma;

class Abteilung {

    String abtBez;

    String standort;

    Abteilung(String abtBez, String standort) {

      this.abtBez = abtBez;

      this.standort = standort;

    }

}

package firma;

class Angestellter {

    int persNr;

    String name;

    String vName;

    Abteilung abteilung;

    Angestellter(int persNr, String name, String vName, Abteilung abteilung) {

      this.persNr = persNr;

      this.name = name;

      this.vName = vName;

      this.abteilung = abteilung;

    }

    void anzeigen() {

      System.out.println(vName + " " + name

        + " arbeitet in der Abteilung "

        + abteilung.abtBez + ".");

    }

}

package firma;

public class Aufgabe24 {

    public static void main(String[] args) {

      Abteilung a1 = new Abteilung("Entwicklung", "Hannover");

      Abteilung a2 = new Abteilung("Verwaltung", "Bremen");

      Angestellter m1 = new Angestellter(1, "Müller", "Anna", a1);

      Angestellter m2 = new Angestellter(2, "Schmidt", "Bernd", a1);

      Angestellter m3 = new Angestellter(3, "Meier", "Clara", a2);

      m1.anzeigen();

      m2.anzeigen();

      m3.anzeigen();

    }

}

25.2.10 Ergebnis Programmlauf

Anna Müller arbeitet in der Abteilung Entwicklung.

Bernd Schmidt arbeitet in der Abteilung Entwicklung.

Clara Meier arbeitet in der Abteilung Verwaltung.

25.2.11 Betrachtete Themen

  • Assoziationen
  • Attribute
  • Bildschirmausgabe
  • Einfache 1:n-Beziehung
  • Klassen
  • Klassenverknüpfungen
  • Konstruktoren
  • Methoden
  • Navigation zwischen Objekten
  • Objektattribute
  • Objekte
  • Objekte als Attribute
  • Objekterzeugung mit new
  • Objektorientierte Modellierung
  • Objektreferenzen
  • Übergabe von Objekten an Konstruktoren
  • Unidirektionale Beziehungen

25.2.12 Erläuterung

Die eigentliche Verknüpfung befindet sich in der Klasse Angestellter:

Abteilung abteilung;

Hier wird keine Abteilungsbezeichnung gespeichert, sondern eine Referenz auf ein Objekt der Klasse Abteilung. Beim Erzeugen eines Angestellten wird das Abteilungsobjekt übergeben:

Angestellter m1 =

new Angestellter(1, "Müller", "Anna", a1);

Der Angestellte "kennt" dadurch seine Abteilung und kann auf deren Attribute zugreifen:

abteilung.abtBez

abteilung.standort

Beziehungen

Zu einer Abteilung können mehrere Angestellte gehören. Ein Angestellter gehört genau einer Abteilung. Im Programm wird die Beziehung bisher nur in einer Richtung gespeichert:

Angestellter -> Abteilung

Von einem Angestellten kann also auf seine Abteilung zugegriffen werden. Von einer Abteilung aus kann dagegen noch nicht festgestellt werden, welche Angestellten dort arbeiten. Dies wird in der folgenden Aufgabe ergänzt.

 

25.3 Aufgabe 25 - Abteilungszugehörigkeit

Mehrere Mitarbeiter pro Abteilung

25.3.1 Aufgabenstellung

Erweitern Sie Aufgabe 24. Eine Abteilung soll nun mehrere Angestellte enthalten. Verwenden Sie dazu eine Liste:

List<Angestellter>

Legen Sie mindestens zwei Abteilungen und drei Angestellte an. Ordnen Sie die Angestellten den Abteilungen zu und geben Sie anschließend alle Mitarbeiter einer Abteilung aus, in etwa so wie hier unten gezeigt.

25.3.2 Ergebnis Programmlauf

Abteilung Entwicklung

Standort: Hannover

- Anna Müller

- Bernd Schmidt

Abteilung Verwaltung

Standort: Bremen

- Clara Meier

25.3.3 Lernziele

Aufbauend auf Aufgabe 24 erweitern Sie das dort entwickelte Klassenmodell zu einer umfassenderen objektorientierten Anwendung. Sie lernen, bestehende Klassen und Beziehungen sinnvoll zu ergänzen, mehrere Objekte miteinander zu verknüpfen und die Zusammenarbeit der Klassen in einer realitätsnahen Anwendung zu verstehen und umzusetzen.

 

25.3.4 Lösung

package firma;

import java.util.ArrayList;

import java.util.List;

class Abteilung {

    String abtBez;

    String standort;

    List<Angestellter> mitarbeiter = new ArrayList<>();

    Abteilung(String abtBez, String standort) {

      this.abtBez = abtBez;

      this.standort = standort;

    }

    void addMitarbeiter(Angestellter a) {

      mitarbeiter.add(a);

      a.abteilung = this;

    }

    void anzeigen() {

      System.out.println("\nAbteilung " + abtBez);

      System.out.println("Standort: " + standort);

      for (Angestellter a : mitarbeiter) {

        System.out.println("- " + a.vName + " " + a.name);

      }

    }

}

package firma;

class Angestellter {

    int persNr;

    String name;

    String vName;

    Abteilung abteilung;

    Angestellter(int persNr, String name, String vName) {

      this.persNr = persNr;

      this.name = name;

      this.vName = vName;

    }

}

package firma;

public class Aufgabe25 {

    public static void main(String[] args) {

      Abteilung entwicklung = new Abteilung("Entwicklung", "Hannover");

      Abteilung verwaltung = new Abteilung("Verwaltung", "Bremen");

      Angestellter a1 = new Angestellter(1, "Müller", "Anna");

      Angestellter a2 = new Angestellter(2, "Schmidt", "Bernd");

      Angestellter a3 = new Angestellter(3, "Meier", "Clara");

      entwicklung.addMitarbeiter(a1);

      entwicklung.addMitarbeiter(a2);

      verwaltung.addMitarbeiter(a3);

      entwicklung.anzeigen();

      verwaltung.anzeigen();

  }

}

25.3.5 Ergebnis Programmlauf

Abteilung Entwicklung

Standort: Hannover

- Anna Müller

- Bernd Schmidt

Abteilung Verwaltung

Standort: Bremen

- Clara Meier

25.3.6 Betrachtete Themen

  • 1:n-Beziehungen
  • ArrayList
  • Assoziationen
  • Bidirektionale Beziehung
  • ForEach-Schleife
  • Generics (List<Angestellter>)
  • Hinzufügen von Objekten zu Listen
  • Iteration über Objektmengen
  • Klassen
  • Klassenverknüpfungen
  • Konstruktoren
  • Listen (List)
  • Methoden zur Verwaltung von Beziehungen
  • Objektattribute
  • Objekte
  • Objekte als Attribute
  • Objekterzeugung mit new
  • Objektorientierte Modellierung
  • Objektreferenzen

25.3.7 Erläuterung

Die wesentliche Erweiterung gegenüber Aufgabe 24 befindet sich in der Klasse Abteilung:

List<Angestellter> mitarbeiter = new ArrayList<>();

Dadurch kann eine Abteilung beliebig viele Angestellte verwalten. Neue Mitarbeiter werden über die Methode

void addMitarbeiter(Angestellter a)

eingefügt:

entwicklung.addMitarbeiter(a1);

entwicklung.addMitarbeiter(a2);

Die Liste wächst dabei automatisch.

Die Beziehung

Die Methode

void addMitarbeiter(Angestellter a) {

  mitarbeiter.add(a);

  a.abteilung = this;

}

erledigt zwei Dinge:

mitarbeiter.add(a);

fügt den Mitarbeiter in die Mitarbeiterliste der Abteilung ein.

a.abteilung = this;

trägt zusätzlich die Abteilung beim Mitarbeiter ein. Dadurch entsteht eine Beziehung in beide Richtungen:

Abteilung <------> Angestellter

Warum List<Angestellter>?

Früher hätte man beispielsweise schreiben können:

Angestellter[] mitarbeiter = new Angestellter[100];

Das hätte einige Nachteile:

  • maximale Größe fest vorgegeben
  • eventuell viele ungenutzte Plätze
  • Verwaltung der freien Plätze erforderlich

Mit

List<Angestellter> mitarbeiter = new ArrayList<>();

wächst die Liste automatisch. Deshalb werden in modernen Java-Programmen für 1:n-Beziehungen meist Listen verwendet.

Theorieaspekte

Diese Aufgabe stellt einen wichtigen Schritt in Richtung realistischer Objektmodelle dar. In Aufgabe 24 wurde lediglich gespeichert:

Angestellter -> Abteilung

Nun wird zusätzlich gespeichert:

Abteilung -> Angestellter

Damit entsteht eine bidirektionale 1:n-Beziehung.

 

25.4 Aufgabe 26 - PC

PC-Zuordnung zu Angestellten

25.4.1 Aufgabenstellung

Erstellen Sie zusätzlich zu den Klassen der vorigen Aufgabe eine Klasse PC mit den Attributen:

  • invNr
  • bez
  • typ

Ordnen Sie einigen Angestellten einen PC zu. Geben Sie anschließend aus, welcher Angestellte welchen PC nutzt, etwa so:

Anna Müller benutzt Dell Optiplex.

Hinweis: Ein PC ist nur einem einzigen Angestellten zugeordnet.

25.4.2 Lernziele

Hier wird gezeigt, wie Objekte verschiedener Klassen miteinander verknüpft werden. Sie verstehen, wie Referenzen genutzt werden, um Beziehungen zwischen Objekten herzustellen und Informationen über verknüpfte Objekte auszugeben. Dadurch vertiefen sie ihr Verständnis für Klassenbeziehungen und die Zusammenarbeit mehrerer Klassen in einem objektorientierten Programm.

 

25.4.3 Lösung

package firma;

class PC {

    int invNr;

    String bez;

    String typ;

    PC(int invNr, String bez, String typ) {

      this.invNr = invNr;

      this.bez = bez;

      this.typ = typ;

    }

}

package firma;

class Angestellter {

    int persNr;

    String name;

    String vName;

    PC pc;

    Angestellter(int persNr, String name, String vName) {

      this.persNr = persNr;

      this.name = name;

      this.vName = vName;

    }

    void setPc(PC pc) {

      this.pc = pc;

    }

    void anzeigenPc() {

      if (pc != null) {

        System.out.println(vName + " " + name + " benutzt " + pc.bez + ".");

      } else {

        System.out.println(vName + " " + name + " hat keinen PC.");

      }

    }

}

package firma;

public class Aufgabe26 {

    public static void main(String[] args) {

      PC pc1 = new PC(1001, "Dell Optiplex", "Desktop");

      PC pc2 = new PC(1002, "Lenovo ThinkPad", "Notebook");

      Angestellter a1 = new Angestellter(1, "Müller", "Anna");

      Angestellter a2 = new Angestellter(2, "Schmidt", "Bernd");

      Angestellter a3 = new Angestellter(3, "Meier", "Clara");

      a1.setPc(pc1);

      a2.setPc(pc2);

      a1.anzeigenPc();

      a2.anzeigenPc();

      a3.anzeigenPc();

    }

}

25.4.4 Ergebnis Programmlauf

Anna Müller benutzt Dell Optiplex.

Bernd Schmidt benutzt Lenovo ThinkPad.

Clara Meier hat keinen PC.

25.4.5 Betrachtete Themen

  • Assoziationen
  • Bedingte Anweisungen (if)
  • Einfache 1:1-Beziehung
  • Klassen
  • Klassenverknüpfungen
  • Konstruktoren
  • Methoden
  • Objektattribute
  • Objekte
  • Objekte als Attribute
  • Objekterzeugung mit new
  • Objektorientierte Modellierung
  • Objektreferenzen
  • Optionale Objektzuordnungen
  • Referenzattribute
  • Setter-Methode
  • Vergleich mit null

25.4.6 Erläuterung

Die eigentliche Verknüpfung befindet sich in der Klasse Angestellter: PC pc; Hier wird nicht die Inventarnummer oder die Bezeichnung gespeichert, sondern eine Referenz auf ein Objekt der Klasse PC. Durch a1.setPc(pc1); wird die Verbindung zwischen dem Angestellten und dem PC hergestellt. Danach kann über die Referenz auf die Attribute des PCs zugegriffen werden:

pc.bez

pc.typ

pc.invNr

Beispielsweise:

System.out.println(pc.bez);

liefert:

Dell Optiplex

Welche Kardinalität liegt vor?

Im Programm besitzt jeder Angestellte höchstens einen PC:

PC pc;

Es gibt also keine Liste von PCs. Damit gilt auf der Seite des Angestellten:

0,1

Ein Angestellter kann einen PC besitzen oder keinen. Auf der Seite des PCs existiert noch keine Gegenreferenz:

Angestellter angestellter;

ist nicht vorhanden. Deshalb ist die Beziehung zunächst unidirektional:

Angestellter -> PC

Inhaltlich ist jedoch beabsichtigt:

Angestellter 0,1 ----- 0,1 PC

also eine optionale 1:1-Beziehung.

Theorieaspekte

Diese Aufgabe ergänzt das bisherige Unternehmensmodell um eine weitere Objektbeziehung. Während die bisherigen Aufgaben hauptsächlich 1:n-Beziehungen behandelt haben, wird hier eine 1:1-Beziehung modelliert. Besonders wichtig ist dabei die Verwendung eines Referenzattributes PC pc;

 

25.5 Aufgabe 27 - Projekte

Angestellte und Projekte

25.5.1 Aufgabenstellung

Erstellen Sie eine Klasse Angestellter mit den Attributen

  • int persNr
  • String name
  • String vName

Außerdem eine Klasse Projekt mit den Attributen:

  • bez
  • standort

Ordnen Sie Projekte den Angestellten zu: Ein Angestellter kann in mehreren Projekten arbeiten. Beispiel für eine Auswertung:

Anna Müller:

- BPMN

- Prozessoptimierung

25.5.2 Lernziele

Sie lernen, wie 1:n-Beziehungen zwischen Objekten mit Listen umgesetzt werden, verstehen, wie einem Objekt mehrere andere Objekte zugeordnet und diese Beziehungen verwaltet sowie ausgegeben werden können.

 

25.5.3 Lösung

package firma;

class Projekt {

    String bez;

    String standort;

    Projekt(String bez, String standort) {

      this.bez = bez;

      this.standort = standort;

    }

}

package firma;

import java.util.ArrayList;

import java.util.List;

class Angestellter {

    int persNr;

    String name;

    String vName;

    List<Projekt> projekte = new ArrayList<>();

    Angestellter(int persNr, String name, String vName) {

      this.persNr = persNr;

      this.name = name;

      this.vName = vName;

    }

    void addProjekt(Projekt p) {

      projekte.add(p);

    }

    void zeigeProjekte() {

      System.out.println("\n" + vName + " " + name + " arbeitet in:");

      for (Projekt p : projekte) {

        System.out.println("- " + p.bez);

      }

    }

}

package firma;

public class Aufgabe27 {

    public static void main(String[] args) {

      Projekt p1 = new Projekt("BPMN", "Hannover");

      Projekt p2 = new Projekt("Prozessoptimierung", "Bremen");

      Angestellter a1 = new Angestellter(1, "Müller", "Anna");

      Angestellter a2 = new Angestellter(2, "Schmidt", "Bernd");

      a1.addProjekt(p1);

      a1.addProjekt(p2);

      a2.addProjekt(p1);

      a1.zeigeProjekte();

      a2.zeigeProjekte();

    }

}

25.5.4 Ergebnis Programmlauf

Anna Müller arbeitet in:

- BPMN

- Prozessoptimierung

Bernd Schmidt arbeitet in:

- BPMN

25.5.5 Betrachtete Themen

  • add()-Methode
  • ArrayList
  • Attribute einer Klasse
  • Ausgabe von Objektdaten
  • eigene Klasse Projekt
  • eigene Methode addProjekt()
  • einfache 1
  • foreach-Schleife
  • Konstruktor
  • List<Projekt>
  • mehrwertiges Attribut
  • Objekterzeugung mit new
  • Objektreferenzen
  • Zuordnung von Projekten zu Angestellten
  • Zusammenarbeit mehrerer Klassen

 

26 Übungsaufgaben Teil 7

26.1 Aufgabe 28 - n:m-Beziehung

26.1.1 Aufgabenstellung

Erweitern Sie Aufgabe 27. Nun soll auch von jedem Projekt auf die Angestellten geschlossen werden können, die in ihm mitarbeiten. Verwenden Sie:

List<Projekt>

in der Klasse Angestellter und

List<Angestellter>

in der Klasse Projekt.

Beispielhafte Ausgabe:

Projekt BPMN

Anna Müller

Bernd Schmidt

26.1.2 Lernziele

Hier kann man lernen, wie eine n:m-Beziehung zwischen zwei Klassen in Java umgesetzt wird. Sie verstehen, wie Objekte beider Klassen gegenseitig miteinander verknüpft werden und wie die Beziehungen mithilfe von Listen verwaltet werden können.

 

26.1.3 Lösung

package firma;

import java.util.ArrayList;

import java.util.List;

class Angestellter {

    int persNr;

    String name;

    String vName;

    List<Projekt> projekte = new ArrayList<>();

    Angestellter(int persNr, String name, String vName) {

      this.persNr = persNr;

      this.name = name;

      this.vName = vName;

    }

    void addProjekt(Projekt p) {

      if (!projekte.contains(p)) {

        projekte.add(p);

        p.addMitarbeiter(this);

      }

    }

    void zeigeProjekte() {

      System.out.println("\nProjekte von " + vName + " " + name + ":");

      for (Projekt p : projekte) {

        System.out.println("- " + p.bez);

      }

    }

}

package firma;

import java.util.ArrayList;

import java.util.List;

class Projekt {

    String bez;

    String standort;

    List<Angestellter> mitarbeiter = new ArrayList<>();

    Projekt(String bez, String standort) {

      this.bez = bez;

      this.standort = standort;

    }

    void addMitarbeiter(Angestellter a) {

      if (!mitarbeiter.contains(a)) {

        mitarbeiter.add(a);

      }

    }

    void zeigeMitarbeiter() {

      System.out.println("\nProjekt " + bez + ":");

      for (Angestellter a : mitarbeiter) {

        System.out.println("- " + a.vName + " " + a.name);

      }

    }

}

package firma;

public class Aufgabe28 {

    public static void main(String[] args) {

      Projekt p1 = new Projekt("BPMN", "Hannover");

      Projekt p2 = new Projekt("Prozessoptimierung", "Bremen");

      Angestellter a1 = new Angestellter(1, "Müller", "Anna");

      Angestellter a2 = new Angestellter(2, "Schmidt", "Bernd");

      Angestellter a3 = new Angestellter(3, "Meier", "Clara");

      a1.addProjekt(p1);

      a1.addProjekt(p2);

      a2.addProjekt(p1);

      a3.addProjekt(p2);

      a1.zeigeProjekte();

      a2.zeigeProjekte();

      p1.zeigeMitarbeiter();

      p2.zeigeMitarbeiter();

    }

}

26.1.4 Ergebnis Programmlauf

Projekte von Anna Müller:

- BPMN

- Prozessoptimierung

Projekte von Bernd Schmidt:

- BPMN

Projekt BPMN:

- Anna Müller

- Bernd Schmidt

Projekt Prozessoptimierung:

- Anna Müller

- Clara Meier

26.1.5 Betrachtete Themen

  • ArrayList
  • Ausgabe aus beiden Beziehungsrichtungen
  • bidirektionale Beziehung
  • bidirektionale n:m-Beziehung
  • contains()-Methode
  • eigene Methoden addProjekt() und addMitarbeiter()
  • foreach-Schleife
  • gegenseitige Aktualisierung von Beziehungen
  • Konsistenz von Verknüpfungen
  • List
  • mehrwertige Attribute
  • n:m-Beziehung
  • Objektreferenzen
  • this-Referenz
  • Verknüpfung von Objekten
  • Vermeidung doppelter Einträge
  • Zusammenarbeit mehrerer Klassen

 

26.2 Aufgabe 29 - Rekursive Beziehung

Rekursive Beziehung: Eine Beziehung, bei der Objekte einer Klasse mit anderen Objekten derselben Klasse verknüpft werden. Beispiele sind Vorgesetzte und Untergebene oder Eltern- und Kindknoten in einem Baum.

26.2.1 Aufgabenstellung

Ein Angestellter kann Vorgesetzter anderer Angestellter sein. Legen Sie eine Klasse Angestellter (wie oben) an und setzen Sie folgende Struktur um: Müller ist Vorgesetzte von Schmidt und Meier. Geben Sie dann alle Untergebenen von Müller aus.

Geben Sie mindestens die Daten ein, die im beispielhaften Programmlauf angeführt sind und erzeugen sie eine entsprechende Ausgabe.

26.2.2 Lernziele

Diese Aufgabe zeigt, wie hierarchische Beziehungen zwischen Objekten modelliert werden können. Sie verstehen, wie Vorgesetzte und Untergebene durch gegenseitige Referenzen miteinander verknüpft werden und wie sich solche Hierarchien in einem objektorientierten Programm darstellen und auswerten lassen. Dadurch vertiefen sie ihr Verständnis rekursiver Beziehungen innerhalb einer Klasse und deren praktische Umsetzung in Java.

26.2.3 Ergebnis Programmlauf

Untergebene von Anna Müller:

- Bernd Schmidt

- Clara Meier

Anna Müller hat keinen Vorgesetzten.

Bernd Schmidt ist untergeben von Anna Müller.

Clara Meier ist untergeben von Anna Müller.

 

26.2.4 Lösung

package firma;

import java.util.ArrayList;

import java.util.List;

class Angestellter {

    int persNr;

    String name;

    String vName;

    Angestellter vorgesetzter;

    List<Angestellter> untergebene = new ArrayList<>();

    Angestellter(int persNr, String name, String vName) {

      this.persNr = persNr;

      this.name = name;

      this.vName = vName;

    }

    void addUntergebener(Angestellter a) {

      untergebene.add(a);

      a.vorgesetzter = this;

    }

    void zeigeUntergebene() {

      System.out.println("\nUntergebene von " + vName + " " + name + ":");

      for (Angestellter a : untergebene) {

        System.out.println("- " + a.vName + " " + a.name);

      }

    }

    void zeigeVorgesetzten() {

      if (vorgesetzter != null) {

        System.out.println(vName + " " + name

    + " ist untergeben von "

    + vorgesetzter.vName + " " + vorgesetzter.name + ".");

      } else {

        System.out.println(vName + " " + name + " hat keinen Vorgesetzten.");

      }

    }

}

package firma;

public class Aufgabe29 {

    public static void main(String[] args) {

      Angestellter a1 = new Angestellter(1, "Müller", "Anna");

      Angestellter a2 = new Angestellter(2, "Schmidt", "Bernd");

      Angestellter a3 = new Angestellter(3, "Meier", "Clara");

      a1.addUntergebener(a2);

      a1.addUntergebener(a3);

      a1.zeigeUntergebene();

      a1.zeigeVorgesetzten();

      a2.zeigeVorgesetzten();

      a3.zeigeVorgesetzten();

    }

}

26.2.5 Betrachtete Themen

  • 1:n-Selbstbeziehung
  • ArrayList
  • Attribut vom Typ der eigenen Klasse
  • Aufbau einer Hierarchie
  • Ausgabe von Hierarchien
  • bidirektionale Beziehung innerhalb einer Klasse
  • eigene Methode addUntergebener()
  • foreach-Schleife
  • gegenseitige Aktualisierung von Beziehungen
  • List
  • Objektreferenzen auf Objekte derselben Klasse
  • Prüfung auf null
  • rekursive Beziehung (Selbstbeziehung)
  • this-Referenz
  • Verknüpfung von Objekten
  • Vorgesetzter-Untergebener-Beziehung

26.2.6 Erläuterung

Die entscheidenden Zeilen sind

Angestellter vorgesetzter;

und

List<Angestellter> untergebene = new ArrayList<>();;

Mit ihnen wird die rekursive Beziehung eingerichtet.

 

26.3 Aufgabe 30 - Gehaltsauswertung

26.3.1 Aufgabenstellung

Legen Sie eine Klasse Angestellter an mit den Attributen:

  • int persNr;
  • String name;
  • String vName;
  • double gehalt;

Außerdem eine Klasse Abteilung mit den Attributen

  • String abtBez;
  • String standort;

Geben Sie mindestens zwei Abteilungen und drei Angestellte ein und weisen Sie die Angestellten den Abteilungen so zu, dass die Daten in etwa so sind wie im beispielhaften Programmlauf.

26.3.2 Lernziele

Diese Aufgabe zeigt, wie sich Auswertungen über Objektsammlungen durchführen lassen. Sie verstehen, wie numerische Werte - beispielsweise Gehälter - aus mehreren Objekten ermittelt, aufsummiert und ausgewertet werden können. Damit vertiefen sie den Umgang mit Schleifen, Objektbeziehungen und Attributen und erkennen, wie betriebliche Kennzahlen in objektorientierten Programmen berechnet werden.

26.3.3 Ergebnis Programmlauf

Abteilung Entwicklung

Gehaltssumme: 6700.0 Euro

Abteilung Verwaltung

Gehaltssumme: 2800.0 Euro

 

26.3.4 Lösung

package firma;

class Angestellter {

    int persNr;

    String name;

    String vName;

    double gehalt;

    Angestellter(int persNr, String name, String vName, double gehalt) {

      this.persNr = persNr;

      this.name = name;

      this.vName = vName;

      this.gehalt = gehalt;

    }

}

package firma;

import java.util.ArrayList;

import java.util.List;

class Abteilung {

    String abtBez;

    String standort;

    List<Angestellter> mitarbeiter = new ArrayList<>();

    Abteilung(String abtBez, String standort) {

      this.abtBez = abtBez;

      this.standort = standort;

    }

    void addMitarbeiter(Angestellter a) {

      mitarbeiter.add(a);

    }

    double berechneGehaltssumme() {

      double summe = 0.0;

      for (Angestellter a : mitarbeiter) {

        summe = summe + a.gehalt;

      }

      return summe;

    }

    void anzeigen() {

      System.out.println("\nAbteilung " + abtBez);

      System.out.println("Gehaltssumme: " + berechneGehaltssumme() + " Euro");

    }

}

package firma;

public class Aufgabe30 {

    public static void main(String[] args) {

      Abteilung entwicklung = new Abteilung("Entwicklung", "Hannover");

      Abteilung verwaltung = new Abteilung("Verwaltung", "Bremen");

      Angestellter a1 = new Angestellter(1, "Müller", "Anna", 3200.00);

      Angestellter a2 = new Angestellter(2, "Schmidt", "Bernd", 3500.00);

      Angestellter a3 = new Angestellter(3, "Meier", "Clara", 2800.00);

      entwicklung.addMitarbeiter(a1);

      entwicklung.addMitarbeiter(a2);

      verwaltung.addMitarbeiter(a3);

      entwicklung.anzeigen();

      verwaltung.anzeigen();

    }

}

26.3.5 Betrachtete Themen

  • Aggregation von Daten
  • Aggregationsfunktion berechneGehaltssumme()
  • ArrayList
  • Auswertung von Objektdaten
  • foreach-Schleife
  • List
  • lokale Variable
  • numerische Attribute (double)
  • Objektlisten durchlaufen
  • Rückgabewert einer Methode (double)
  • Summenbildung über Objektattribute
  • Zugriff auf Objektattribute
  • Zusammenarbeit mehrerer Klassen

 

26.4 Aufgabe 31 - Projektstatistik

26.4.1 Aufgabenstellung

Richten Sie eine Klasse Projekt ein mit den Attributen

  • String bez;
  • String standort;

Ebenso eine Klasse Angestellter mit den Attributen

  • int persNr;
  • String name;
  • String vName;

Richten Sie die zwei Projekte "BPMN" und "Prozessoptimierung" und mindestens drei Angestellte ein. Jedes Projekt verwaltet die ihm zugeordneten Mitarbeiter in einer Liste, setzen Sie dies um. Erweitern Sie außederm die Klasse Projekt um eine Methode zur Ermittlung der Anzahl Mitarbeiter eines Projekts.

Weisen Sie die Angestellten den Projekten zu, etwa so, wie es der beispielhafte Programmlauf zeigt.

26.4.2 Lernziele

Hier wird gezeigt, wie Objekte gesucht und gezielt ausgewertet werden können. Sie verstehen, wie Sammlungen von Objekten durchlaufen werden, um anhand bestimmter Kriterien ein passendes Objekt zu finden und dessen Eigenschaften auszugeben. Dadurch vertiefen sie den Umgang mit Objektlisten, Schleifen und Bedingungen und erkennen, wie Suchfunktionen in objektorientierten Anwendungen umgesetzt werden können.

26.4.3 Ergebnis Programmlauf

Projekt BPMN: 2 Mitarbeiter

Projekt Prozessoptimierung: 2 Mitarbeiter

 

26.4.4 Lösung

Klasse Projekt

package firma;

import java.util.ArrayList;

import java.util.List;

class Projekt {

  String bez;

  String standort;

  

  List<Angestellter> mitarbeiter = new ArrayList<>();

  

  Projekt(String bez, String standort) {

    this.bez = bez;

    this.standort = standort;

  }

  void addMitarbeiter(Angestellter a) {

    

    if (!mitarbeiter.contains(a)) {

      mitarbeiter.add(a);

    }

  }

  int ermittleAnzahlMitarbeiter() {

    return mitarbeiter.size();

  }

  void anzeigenStatistik() {

    System.out.println(

      "Projekt " + bez + ": "

      + ermittleAnzahlMitarbeiter()

      + " Mitarbeiter");

  }

}

Klasse Angestellter

package firma;

class Angestellter {int persNr;String name;String vName;

  

  Angestellter(int persNr, String name, String vName) {

    this.persNr = persNr;

    this.name = name;

    this.vName = vName;

  }

}

Klasse Aufgabe31 (HP)

package firma;

public class Aufgabe31 {

  public static void main(String[] args) {

    Projekt p1 = new Projekt("BPMN", "Hannover");

    Projekt p2 = new Projekt("Prozessoptimierung","Bremen");

    Angestellter a1 = new Angestellter(1,"Müller","Anna");

    Angestellter a2 =new Angestellter(2,"Schmidt","Bernd");

    Angestellter a3 =new Angestellter(3,"Meier","Clara");

    p1.addMitarbeiter(a1);

    p1.addMitarbeiter(a2);

    

    p2.addMitarbeiter(a1);

    p2.addMitarbeiter(a3);

    

    p1.anzeigenStatistik();

    p2.anzeigenStatistik();

  }

}

26.4.5 Betrachtete Themen

  • Listen (ArrayList)
  • Generics (List<Angestellter>)
  • Hinzufügen von Objekten zu Listen
  • Methoden mit Rückgabewert
  • Rückgabewert vom Typ int
  • Aufruf von Methoden
  • Verwendung von Rückgabewerten
  • Auswertung von Objektlisten
  • Methode size()
  • Zählen von Objekten
  • Einfache Statistikfunktionen
  • Objekterzeugung mit new
  • n:m-Beziehungen (Vorstufe)

26.4.6 Erläuterung

Die Mitarbeiter eines Projekts werden in einer Liste gespeichert:

List<Angestellter> mitarbeiter = new ArrayList<>();

Die Methode

int ermittleAnzahlMitarbeiter()

liefert die Anzahl der in dieser Liste gespeicherten Mitarbeiter zurück. Dazu wird die Methode

mitarbeiter.size()

verwendet. size() gehört zur Klasse ArrayList und liefert die Anzahl der aktuell gespeicherten Elemente. Die Methode

int ermittleAnzahlMitarbeiter() {

  return mitarbeiter.size();

}

liefert somit die Anzahl der einem Projekt zugeordneten Mitarbeiter. Dieser Rückgabewert wird anschließend in der Methode anzeigenStatistik() zur Ausgabe verwendet.

 

26.5 Aufgabe 32 - Mini-Personalverwaltung

Diese Aufgabe fasst die vorherigen Aufgaben zum Unternehmensgeschehen zusammen.

26.5.1 Aufgabenstellung

Erstellen Sie, aufbauend auf den bisher entwickelten Klassen, ein Programm für eine kleine Personalverwaltung. Das Programm soll mindestens folgende Objekte anlegen:

  • 5 Angestellte
  • 2 Abteilungen
  • 3 Projekte
  • 3 PCs
  • Ordnen Sie die Angestellten den Abteilungen zu.
  • Ordnen Sie einzelnen Angestellten PCs zu.
  • Ordnen Sie Angestellte mehreren Projekten zu.
  • Richten Sie außerdem eine einfache Vorgesetztenstruktur ein.

Geben Sie anschließend aus:

  • alle Abteilungen mit ihren Mitarbeitern
  • die Gehaltssumme jeder Abteilung
  • alle Projekte mit ihren Mitarbeitern
  • alle Mitarbeiter mit ihren Projekten
  • alle Mitarbeiter mit ihren zugeordneten PCs
  • die Anzahl der Mitarbeiter pro Projekt
  • die Vorgesetztenstruktur

26.5.2 Lernziele

Hier werden die zuvor erlernten Konzepte der objektorientierten Programmierung in einem größeren Gesamtprojekt angewandt. Sie lernen, mehrere miteinander verknüpfte Klassen zu einem konsistenten Anwendungssystem zusammenzuführen, Beziehungen zwischen Objekten zu modellieren und umfangreichere Datenstrukturen zu verwalten. Dadurch vertiefen sie ihr Verständnis für die objektorientierte Analyse und Umsetzung eines realitätsnahen Anwendungsbereichs sowie für das Zusammenspiel der verschiedenen OOP-Konzepte.

 

26.5.3 Lösung

Klasse Aufgabe32 (HP)

package firma;

public class Aufgabe32 {

    public static void main(String[] args) {

      Abteilung entwicklung = new Abteilung("Entwicklung", "Hannover");

      Abteilung verwaltung = new Abteilung("Verwaltung", "Bremen");

      PC pc1 = new PC(1001, "Dell Optiplex", "Desktop");

      PC pc2 = new PC(1002, "Lenovo ThinkPad", "Notebook");

      PC pc3 = new PC(1003, "HP EliteBook", "Notebook");

      Projekt p1 = new Projekt("BPMN", "Hannover");

      Projekt p2 = new Projekt("Prozessoptimierung", "Bremen");

      Projekt p3 = new Projekt("Datenmodellierung", "Hannover");

      Angestellter a1 = new Angestellter(1, "Müller", "Anna", 4000.00);

      Angestellter a2 = new Angestellter(2, "Schmidt", "Bernd", 3600.00);

      Angestellter a3 = new Angestellter(3, "Meier", "Clara", 3100.00);

      Angestellter a4 = new Angestellter(4, "Fischer", "David", 3300.00);

      Angestellter a5 = new Angestellter(5, "Weber", "Eva", 2900.00);

      entwicklung.addMitarbeiter(a1);

      entwicklung.addMitarbeiter(a2);

      entwicklung.addMitarbeiter(a3);

      verwaltung.addMitarbeiter(a4);

      verwaltung.addMitarbeiter(a5);

      a1.setPc(pc1);

      a2.setPc(pc2);

      a4.setPc(pc3);

      a1.addProjekt(p1);

      a1.addProjekt(p2);

      a2.addProjekt(p1);

      a3.addProjekt(p3);

      a4.addProjekt(p2);

      a5.addProjekt(p3);

      a1.addUntergebener(a2);

      a1.addUntergebener(a3);

      System.out.println("Abteilungen:");

      entwicklung.anzeigen();

      verwaltung.anzeigen();

      System.out.println("\nProjekte:");

      p1.zeigeMitarbeiter();

      p2.zeigeMitarbeiter();

      p3.zeigeMitarbeiter();

      System.out.println("\nMitarbeiter mit Projekten:");

      a1.zeigeProjekte();

      a2.zeigeProjekte();

      a3.zeigeProjekte();

      a4.zeigeProjekte();

      a5.zeigeProjekte();

      System.out.println("\nProjektstatistik:");

      p1.anzeigenStatistik();

      p2.anzeigenStatistik();

      p3.anzeigenStatistik();

      System.out.println("\nVorgesetztenstruktur:");

      a1.zeigeUntergebene();

    }

}

Klasse Angestellter

package firma;

import java.util.ArrayList;

import java.util.List;

class Angestellter {

    int persNr;

    String name;

    String vName;

    double gehalt;

    Abteilung abteilung;

    PC pc;

    List<Projekt> projekte = new ArrayList<>();

    Angestellter vorgesetzter;

    List<Angestellter> untergebene = new ArrayList<>();

    Angestellter(int persNr, String name, String vName, double gehalt) {

      this.persNr = persNr;

      this.name = name;

      this.vName = vName;

      this.gehalt = gehalt;

    }

    void setPc(PC pc) {

      this.pc = pc;

    }

    void zeigePc() {

      System.out.println(vName + " " + name + ":");

    

      if (pc != null) {

        System.out.println("- PC: " + pc.bez + " (" + pc.typ + ")");

      } else {

        System.out.println("- kein PC zugeordnet");

      }

    }

    void addProjekt(Projekt p) {

      if (!projekte.contains(p)) {

        projekte.add(p);

        p.addMitarbeiter(this);

      }

    }

    void addUntergebener(Angestellter a) {

      untergebene.add(a);

      a.vorgesetzter = this;

    }

    void zeigeProjekte() {

      System.out.println("\n" + vName + " " + name + ":");

      for (Projekt p : projekte) {

        System.out.println("- " + p.bez);

      }

    }

    void zeigeUntergebene() {

      System.out.println(vName + " " + name + " ist Vorgesetzter von:");

      for (Angestellter a : untergebene) {

        System.out.println("- " + a.vName + " " + a.name);

      }

    }

}

Klasse Abteilung

package firma;

import java.util.ArrayList;

import java.util.List;

class Abteilung {

    String abtBez;

    String standort;

    List<Angestellter> mitarbeiter = new ArrayList<>();

    Abteilung(String abtBez, String standort) {

      this.abtBez = abtBez;

      this.standort = standort;

    }

    void addMitarbeiter(Angestellter a) {

      mitarbeiter.add(a);

      a.abteilung = this;

    }

    double berechneGehaltssumme() {

      double summe = 0.0;

      for (Angestellter a : mitarbeiter) {

        summe = summe + a.gehalt;

      }

      return summe;

    }

    void anzeigen() {

      System.out.println("\nAbteilung " + abtBez);

      for (Angestellter a : mitarbeiter) {

        System.out.println("- " + a.vName + " " + a.name);

      }

      System.out.println("Gehaltssumme: " + berechneGehaltssumme() + " Euro");

    }

}

Klasse Projekt

package firma;

import java.util.ArrayList;

import java.util.List;

class Projekt {

    String bez;

    String standort;

    List<Angestellter> mitarbeiter = new ArrayList<>();

    Projekt(String bez, String standort) {

      this.bez = bez;

      this.standort = standort;

    }

    void addMitarbeiter(Angestellter a) {

      if (!mitarbeiter.contains(a)) {

        mitarbeiter.add(a);

      }

    }

    int ermittleAnzahlMitarbeiter() {

      return mitarbeiter.size();

    }

    void zeigeMitarbeiter() {

      System.out.println("\nProjekt " + bez + ":");

      for (Angestellter a : mitarbeiter) {

        System.out.println("- " + a.vName + " " + a.name);

      }

    }

    void anzeigenStatistik() {

      System.out.println("Projekt " + bez + ": "

        + ermittleAnzahlMitarbeiter() + " Mitarbeiter");

    }

}

Klasse PC

package firma;

class PC {

    int invNr;

    String bez;

    String typ;

    PC(int invNr, String bez, String typ) {

      this.invNr = invNr;

      this.bez = bez;

      this.typ = typ;

    }

}

26.5.4 Ergebnis Programmlauf

Abteilungen:

Abteilung Entwicklung

- Anna Müller

- Bernd Schmidt

- Clara Meier

Gehaltssumme: 10700.0 Euro

Abteilung Verwaltung

- David Fischer

- Eva Weber

Gehaltssumme: 6200.0 Euro

Projekte:

Projekt BPMN:

- Anna Müller

- Bernd Schmidt

Projekt Prozessoptimierung:

- Anna Müller

- David Fischer

Projekt Datenmodellierung:

- Clara Meier

- Eva Weber

Mitarbeiter mit Projekten:

Anna Müller:

- BPMN

- Prozessoptimierung

Bernd Schmidt:

- BPMN

Clara Meier:

- Datenmodellierung

David Fischer:

- Prozessoptimierung

Eva Weber:

- Datenmodellierung

Mitarbeiter mit PCs:

Anna Müller:

- PC: Dell Optiplex (Desktop)

Bernd Schmidt:

- PC: Lenovo ThinkPad (Notebook)

Clara Meier:

- kein PC zugeordnet

David Fischer:

- PC: HP EliteBook (Notebook)

Eva Weber:

- kein PC zugeordnet

Projektstatistik:

Projekt BPMN: 2 Mitarbeiter

Projekt Prozessoptimierung: 2 Mitarbeiter

Projekt Datenmodellierung: 2 Mitarbeiter

Vorgesetztenstruktur:

Anna Müller ist Vorgesetzter von:

- Bernd Schmidt

- Clara Meier

26.5.5 Betrachtete Themen

Zusammenführung der bisher behandelten Konzepte:

  • 1:1-Beziehungen
  • 1:n-Beziehungen
  • Aggregation von Objekten
  • Attribute
  • Auswertungsmethoden
  • Bidirektionale Beziehungen
  • einfache Statistikfunktionen
  • ForEach-Schleifen
  • Generics (List<Angestellter>)
  • Klassen
  • Konstruktoren
  • Listen (ArrayList)
  • Methoden
  • Methoden mit Rückgabewert
  • n:m-Beziehungen
  • Objekte
  • Objekterzeugung mit new
  • Objektverweise
  • Referenzen zwischen Objekten
  • Schleifen über Listen
  • Selbstbeziehungen
  • strukturierte Bildschirmausgabe
  • Summenbildung
  • Zählung von Objekten

27 Übungsaufgaben Teil 8

Die folgenden Aufgaben vertiefen zentrale Konzepte der objektorientierten Programmierung in Java. Die Aufgaben bauen inhaltlich aufeinander auf. Ausgangspunkt ist jeweils das Beispiel einer einfachen Personalverwaltung mit Angestellten, Sachbearbeitern, Abteilungsleitern und Projekten.

27.1 Aufgabe 33 - Vererbung

27.1.1 Aufgabenstellung

Erstellen Sie eine einfache Klassenhierarchie für Angestellte. Die Klasse Angestellter soll als Superklasse dienen. Sie enthält gemeinsame Attribute, die für alle Angestellten gelten. Aus dieser Klasse sollen zwei Subklassen abgeleitet werden:

  • Sachbearbeiter
  • Abteilungsleiter

Die Klassen sollen folgende Attribute besitzen:

Angestellter

String name;

String vorname;

Sachbearbeiter

String fachgebiet;

Abteilungsleiter

double budget;

Legen Sie im Hauptprogramm je ein Objekt der Klassen Sachbearbeiter und Abteilungsleiter an. Weisen Sie den Attributen Werte zu und geben Sie die Daten auf dem Bildschirm aus. Beachten Sie dabei, dass die Subklassen sowohl ihre eigenen Attribute als auch die geerbten Attribute der Superklasse besitzen.

27.1.2 Lernziele

Hier wird wieder auf das Konzept der Vererbung eingegangen. Sie verstehen, wie gemeinsame Eigenschaften und Methoden in einer Oberklasse zusammengefasst und von Unterklassen übernommen werden können. Dadurch erkennen sie, wie Vererbung zur Wiederverwendung von Programmcode beiträgt und eine übersichtliche Modellierung verwandter Objekte ermöglicht.

27.1.3 Ergebnis Programmlauf

Sachbearbeiter:

Anna Müller

Fachgebiet: Personal

Abteilungsleiter:

Bernd Schmidt

Budget: 500000.0

 

27.1.4 Lösung

Klasse Angestellter, usw.

package firma;

class Angestellter {

    String name;

    String vorname;

}

class Sachbearbeiter extends Angestellter {

    String fachgebiet;

}

class Abteilungsleiter extends Angestellter {

    double budget;

}

Klasse Aufgabe33

package firma;

public class Aufgabe33 {

    public static void main(String[] args) {

      Sachbearbeiter s1 = new Sachbearbeiter();

      s1.name = "Müller";

      s1.vorname = "Anna";

      s1.fachgebiet = "Personal";

      Abteilungsleiter a1 = new Abteilungsleiter();

      a1.name = "Schmidt";

      a1.vorname = "Bernd";

      a1.budget = 500000.0;

      System.out.println("Sachbearbeiter:");

      System.out.println(s1.vorname + " " + s1.name);

      System.out.println("Fachgebiet: " + s1.fachgebiet);

      System.out.println();

      System.out.println("Abteilungsleiter:");

      System.out.println(a1.vorname + " " + a1.name);

      System.out.println("Budget: " + a1.budget);

    }

}

27.1.5 Betrachtete Themen

  • Bildschirmausgabe mit System.out.println()
  • Eigene Attribute von Subklassen
  • Geerbte Attribute
  • Gemeinsame und spezielle Eigenschaften
  • Klassenhierarchien
  • Superklasse
  • Objekterzeugung mit new
  • Schlüsselwort extends
  • Spezialisierung von Klassen
  • Subklasse
  • Vererbung
  • Zugriff auf geerbte Attribute

27.1.6 Erläuterung

Die Klasse Angestellter enthält die gemeinsamen Eigenschaften aller Angestellten. Sowohl Sachbearbeiter als auch Abteilungsleiter besitzen daher automatisch die Attribute name und vorname. Durch

class Sachbearbeiter extends Angestellter

wird festgelegt, dass Sachbearbeiter eine Subklasse von Angestellter ist. Das bedeutet: Ein Sachbearbeiter ist ein Angestellter, besitzt aber zusätzlich ein eigenes Attribut fachgebiet. Ein Abteilungsleiter ist ebenfalls ein Angestellter, besitzt aber zusätzlich das Attribut budget.

Theorieaspekte

Diese Aufgabe demonstriert nochmals das Konzept der Vererbung. Dabei werden gemeinsame Eigenschaften in einer Superklasse zusammengefasst und von mehreren Subklassen wiederverwendet. Damit wird vermieden, dass identische Attribute mehrfach definiert werden müssen. Die Klassenhierarchie beschreibt eine sogenannte ist-ein-Beziehung:

  • Ein Sachbearbeiter ist ein Angestellter.
  • Ein Abteilungsleiter ist ein Angestellter.

Vererbung ermöglicht somit eine übersichtliche und leicht erweiterbare Struktur objektorientierter Programme. Vgl. [Staud 2019] für eine ausführliche Darstellung.

27.2 Aufgabe 34 - Override

Überschreiben von Methoden

27.2.1 Aufgabenstellung

Erweitern Sie die Klassen aus der vorigen Aufgabe. Die Superklasse Angestellter soll eine Methode

void anzeigen()

enthalten.

Diese Methode soll in den Subklassen Sachbearbeiter und Abteilungsleiter überschrieben werden. Die Ausgabe soll je nach Objekt unterschiedlich sein:

  • Bei einem Sachbearbeiter sollen Name, Vorname und Fachgebiet ausgegeben werden.
  • Bei einem Abteilungsleiter sollen Name, Vorname und Budget ausgegeben werden.

Legen Sie im Hauptprogramm einen Sachbearbeiter und einen Abteilungsleiter an und rufen Sie für beide Objekte die Methode anzeigen() auf. Beachten Sie, dass dieselbe Methode je nach Klasse ein unterschiedliches Verhalten zeigt.

27.2.2 Lernziele

Es geht um das Konzept des Überschreibens von Methoden (Overriding). Sie verstehen, wie Unterklassen Methoden einer Oberklasse mit einer eigenen Implementierung versehen können und wie dadurch ein an die jeweilige Objektklasse angepasstes Verhalten entsteht. Damit vertiefen sie ihr Verständnis der Vererbung und schaffen die Grundlage für das Konzept der Polymorphie.

27.2.3 Ergebnis Programmlauf

Sachbearbeiter:

Anna Müller

Fachgebiet: Personal

Abteilungsleiter:

Bernd Schmidt

Budget: 500000.0

 

27.2.4 Lösung

package firma;

class Angestellter {

    String name;

    String vorname;

    void anzeigen() {

      System.out.println(vorname + " " + name);

    }

}

class Sachbearbeiter extends Angestellter {

    String fachgebiet;

    @Override

    void anzeigen() {

      System.out.println("Sachbearbeiter:");

      System.out.println(vorname + " " + name);

      System.out.println("Fachgebiet: " + fachgebiet);

    }

}

class Abteilungsleiter extends Angestellter {

    double budget;

    @Override

    void anzeigen() {

      System.out.println("Abteilungsleiter:");

      System.out.println(vorname + " " + name);

      System.out.println("Budget: " + budget);

    }

}

package firma;

public class Aufgabe34 {

    public static void main(String[] args) {

      Sachbearbeiter s1 = new Sachbearbeiter();

      s1.name = "Müller";

      s1.vorname = "Anna";

      s1.fachgebiet = "Personal";

      Abteilungsleiter a1 = new Abteilungsleiter();

      a1.name = "Schmidt";

      a1.vorname = "Bernd";

      a1.budget = 500000.0;

      s1.anzeigen();

      System.out.println();

      a1.anzeigen();

    }

}

27.2.5 Betrachtete Themen

  • Annotation @Override
  • Dynamische Methodenbindung
  • Methoden
  • Methoden überschreiben (Method Overriding)
  • Methodenaufrufe
  • Objektattribute
  • Objekterzeugung mit new
  • Polymorphes Verhalten (Vorstufe)
  • Spezialisierung von Klassen
  • Spezialisierung von Verhalten
  • Super- und Subklassen
  • Vererbung
  • Vererbung von Methoden

27.2.6 Erläuterung

Die Methode anzeigen() existiert bereits in der Superklasse Angestellter. In den Subklassen wird diese Methode neu definiert. Das nennt man Überschreiben oder Override. Die Annotation @Override ist nicht zwingend erforderlich, aber sehr empfehlenswert. Sie zeigt dem Compiler, dass eine Methode aus der Superklasse überschrieben werden soll. Dadurch kann Java prüfen, ob der Methodenname und die Parameter wirklich korrekt sind.

 

27.3 Aufgabe 35 - Overload

Überladen von Methoden

27.3.1 Aufgabenstellung

Erweitern Sie die Klasse Angestellter. Die Klasse soll drei Varianten der Methode anzeigen() enthalten:

void anzeigen()

void anzeigen(boolean kurzform)

void anzeigen(String titel)

Alle drei Methoden sollen denselben Namen besitzen, sich jedoch durch ihre Parameterlisten unterscheiden. Implementieren Sie die Methoden wie folgt:

  • anzeigen() gibt die vollständigen Personendaten aus.
  • anzeigen(true) gibt nur Vorname und Name in Kurzform aus.
  • anzeigen(String titel) gibt zunächst eine Überschrift aus und anschließend die vollständigen Personendaten.

Legen Sie im Hauptprogramm einen Angestellten an und testen Sie alle drei Varianten der Methode. Die Ausgabe soll wie im beispielhaften Programmlauf aussehen.

27.3.2 Lernziele

Diese Aufgabe demonstriert das Konzept der Methodenüberladung (Overloading) kennen. Sie verstehen, dass mehrere Methoden denselben Namen tragen können, sofern sie sich in ihrer Parameterliste unterscheiden. Dadurch erkennen sie, wie Methoden für unterschiedliche Anwendungsfälle bereitgestellt werden können, ohne neue Methodennamen einführen zu müssen.

27.3.3 Ergebnis Programmlauf

Name: Müller

Vorname: Anna

Anna Müller

Mitarbeiterdaten:

------------------

Name: Müller

Vorname: Anna

 

27.3.4 Lösung

package firma;

class Angestellter {

    String name;

    String vorname;

    void anzeigen() {

      System.out.println("Name: " + name);

      System.out.println("Vorname: " + vorname);

    }

    void anzeigen(boolean kurzform) {

      if (kurzform) {

        System.out.println(vorname + " " + name);

      } else {

        anzeigen();

      }

    }

    void anzeigen(String titel) {

      System.out.println(titel);

      System.out.println("--------------------");

      anzeigen();

    }

}

package firma;

public class Aufgabe35 {

    public static void main(String[] args) {

      Angestellter a1 = new Angestellter();

      a1.name = "Müller";

      a1.vorname = "Anna";

      a1.anzeigen();

      System.out.println();

      a1.anzeigen(true);

      System.out.println();

      a1.anzeigen("Mitarbeiterdaten");

    }

}

27.3.5 Betrachtete Themen

  • Mehrere Methoden mit gleichem Namen
  • Method Overloading (Methodenüberladung)
  • Methoden
  • Methodenaufrufe
  • Methodenaufrufe innerhalb einer Methode
  • Methodensignaturen
  • Objektattribute
  • Objekterzeugung mit new
  • Parameterlisten
  • Überladen von Methoden
  • Unterschiedliche Parameterdatentypen
  • Verzweigungen (if)
  • Wiederverwendung von Methoden

27.3.6 Erläuterung

In Java dürfen mehrere Methoden denselben Namen besitzen. Voraussetzung ist, dass sie sich in ihrer Parameterliste unterscheiden. Deshalb sind die Methoden oben zulässig. Der Compiler erkennt anhand der übergebenen Parameter, welche Methode aufgerufen werden soll.

Beispiele

  • a1.anzeigen(); ruft auf: void anzeigen()
  • a1.anzeigen(true); ruft auf: void anzeigen(boolean kurzform)
  • a1.anzeigen("Mitarbeiterdaten"); ruft auf: void anzeigen(String titel)

Dieses Verfahren wird als Methodenüberladung (Method Overloading) bezeichnet.

 

27.4 Aufgabe 36 - Abstraktes

Abstrakte Klassen

27.4.1 Aufgabenstellung

In einem Unternehmen gibt es verschiedene Gruppen von Angestellten. Die allgemeine Klasse Angestellter dient dabei lediglich als gemeinsame Superklasse und soll nicht direkt zur Erzeugung von Objekten verwendet werden. Erstellen Sie deshalb die Klasse Angestellter als abstrakte Klasse. Die Klasse soll die Attribute

  • name
  • vorname

sowie die Methoden

  • anzeigen()
  • enthalten.

Ergänzen Sie außerdem die abstrakte Methode

abstract double berechneBonus();

Die Subklassen Sachbearbeiter und Abteilungsleiter sollen diese Methode konkret implementieren. Verwenden Sie dabei folgende Regeln:

 


Klasse

Bonus

Sachbearbeiter

500.0

Abteilungsleiter

5000.0

 

Legen Sie im Hauptprogramm einen Sachbearbeiter und einen Abteilungsleiter an. Geben Sie für beide Objekte die Personendaten sowie den berechneten Bonus aus, wie im beispielhaften Programmlauf angegeben.

27.4.2 Lernziele

Hier geht es um abstrakte Klassen. Sie verstehen, dass abstrakte Klassen gemeinsame Eigenschaften und Methoden für mehrere Unterklassen bereitstellen können, ohne selbst instanziiert werden zu können. Außerdem lernen sie, abstrakte Methoden zu definieren, die von den Unterklassen verpflichtend implementiert werden müssen. Dadurch vertiefen sie ihr Verständnis der Vererbung und der gemeinsamen Modellierung ähnlicher Objekte.

27.4.3 Ergebnis Programmlauf

Anna Müller

Bonus: 500.0

Bernd Schmidt

Bonus: 5000.0

 

27.4.4 Lösung

Klasse Angestellter usw.

package firma;

abstract class Angestellter {

    String name;

    String vorname;

    void anzeigen() {

      System.out.println(vorname + " " + name);

    }

    abstract double berechneBonus();

}

class Sachbearbeiter extends Angestellter {

    String fachgebiet;

    @Override

    double berechneBonus() {

      return 500.0;

    }

}

class Abteilungsleiter extends Angestellter {

    double budget;

    @Override

    double berechneBonus() {

      return 5000.0;

    }

}

Klasse Aufgabe36

package firma;

public class Aufgabe36 {

  public static void main(String[] args) {

      Sachbearbeiter s1 = new Sachbearbeiter();

      s1.name = "Müller";

      s1.vorname = "Anna";

      s1.fachgebiet = "Personal";

      Abteilungsleiter a1 = new Abteilungsleiter();

      a1.name = "Schmidt";

      a1.vorname = "Bernd";

      a1.budget = 500000.0;

      s1.anzeigen();

      System.out.println("Bonus: " + s1.berechneBonus());

      System.out.println();

      a1.anzeigen();

      System.out.println("Bonus: " + a1.berechneBonus());

    }

}

27.4.5 Betrachtete Themen

  • Abstrakte Klassen
  • Abstrakte Methoden
  • Vererbung
  • Super- und Subklassen
  • Spezialisierung von Klassen
  • Überschreiben von Methoden
  • Annotation @Override
  • Methoden mit Rückgabewert
  • Rückgabewert vom Typ double
  • Gemeinsame und spezialisierte Methoden
  • Objekterzeugung mit new
  • Methodenaufrufe
  • Verhinderung direkter Objekterzeugung
  • Verpflichtung zur Implementierung abstrakter Methoden

27.4.6 Erläuterung

Eine abstrakte Klasse dient als Vorlage für andere Klassen. Sie kann gemeinsame Attribute und Methoden bereitstellen, soll aber selbst nicht zur Erzeugung von Objekten verwendet werden. Folgende Anweisung wäre also nicht erlaubt:

Angestellter a = new Angestellter();

Der Grund: Die Klasse Angestellter enthält eine abstrakte Methode:

Eine abstrakte Methode wie abstract double berechneBonus() besitzt keine Implementierung und endet deshalb mit einem Semikolon. Sie legt lediglich fest, dass jede konkrete Subklasse eine passende Methode bereitstellen muss. Die eigentliche Implementierung erfolgt in den Subklassen. Dadurch kann jede Subklasse ihr eigenes Verhalten festlegen, obwohl alle dieselbe Methodensignatur verwenden.

Theorieaspekte

Diese Aufgabe thematisiert zwei wichtige Konzepte der Objektorientierung:

Abstraktion

Die Klasse Angestellter beschreibt gemeinsame Eigenschaften aller Angestellten. Sie ist jedoch zu allgemein, um direkt als Objekt verwendet zu werden.

Verpflichtende Spezialisierung

Die abstrakte Methode berechneBonus() definiert eine Aufgabe, aber noch keine Lösung. Erst die Subklassen legen fest, wie der Bonus konkret berechnet wird.

Dadurch entsteht eine klare Trennung zwischen allgemeinen Eigenschaften einer Superklasse und den spezifischen Eigenschaften ihrer Subklassen.

 

28 Übungsaufgaben Teil 9

28.1 Aufgabe 37 - Interface

28.1.1 Aufgabenstellung

Richten Sie folgende Klassen ein:

  • Angestellter mit den Attributen name und vorname
  • Sachbearbeiter mit dem Attribut fachgebiet
  • Projekt mit dem Attribut bez

Erstellen Sie außerdem die Klasse Sachbearbeiter als Subklasse von Angestellter.

In diesem Unternehmen sollen verschiedene Objekte ausgedruckt werden können. Erstellen Sie dazu ein Interface Druckbar mit der Methode void drucken();

Legen Sie im Hauptprogramm einen Sachbearbeiter und ein Projekt an und rufen Sie anschließend für beide Objekte die Methode drucken() auf.

Die Klassen Angestellter und Projekt sollen dieses Interface implementieren. Die Methode drucken() soll jeweils eine sinnvolle Ausgabe erzeugen, etwa so wie im beispielhaften Programmlauf.

28.1.2 Lernziele

Hier geht es um das Konzept der Interfaces in Java. Sie verstehen, wie Interfaces gemeinsame Funktionalitäten unabhängig von einer Klassenhierarchie festlegen und wie Klassen diese durch die Implementierung der vorgegebenen Methoden bereitstellen. Dadurch erkennen sie, wie Interfaces zur besseren Strukturierung, Erweiterbarkeit und Wiederverwendbarkeit objektorientierter Programme beitragen.

28.1.3 Ergebnis Programmlauf

Angestellter: Anna Müller

Projekt: BPMN-Einführung

 

28.1.4 Lösung

Klasse Projekt, usw.

package firma;

interface Druckbar {

    void drucken(); }

abstract class Angestellter implements Druckbar {

    String name;

    String vorname;

    @Override

    public void drucken() {

      System.out.println("Angestellter: " + vorname + " " + name);

    }

}

class Sachbearbeiter extends Angestellter {

    String fachgebiet;

}

class Projekt implements Druckbar {

    String bez;

    Projekt(String bez) {

      this.bez = bez;

    }

    @Override

    public void drucken() {

      System.out.println("Projekt: " + bez);

    }

}

Klasse Aufgabe37

package firma;

public class Aufgabe37 {

    public static void main(String[] args) {

      Sachbearbeiter s1 = new Sachbearbeiter();

      s1.name = "Müller";

      s1.vorname = "Anna";

      s1.fachgebiet = "Personal";

      Projekt p1 = new Projekt("BPMN-Einführung");

      s1.drucken();

      p1.drucken();

    }

}

28.1.5 Betrachtete Themen

  • Abstrakte Klassen
  • Annotation @Override
  • Definition eines Interfaces
  • Gemeinsame Fähigkeiten unterschiedlicher Klassen
  • Implementierung eines Interfaces
  • Interfaces
  • Methode im Interface
  • Methodenaufrufe
  • Methodensignatur
  • Objekterzeugung mit new
  • Schlüsselwort implements
  • Subklassen
  • Trennung von Verhalten und Klassenhierarchien
  • Überschreiben von Methoden
  • Vererbung

28.1.6 Erläuterung

Ein Interface beschreibt eine Fähigkeit. Das Interface Druckbar sagt:

  • Jede Klasse, die dieses Interface implementiert, muss eine Methode drucken() besitzen.

Die Klassen Angestellter und Projekt sind fachlich völlig unterschiedliche Klassen. Trotzdem können beide druckbare Objekte besitzen. Damit beschreiben sie gemeinsames Verhalten unabhängig von einer gemeinsamen Superklasse.

 

28.2 Aufgabe 38 - Mehrere Interfaces

28.2.1 Aufgabenstellung

Diese Aufgabe setzt Aufgabe 37 fort.

Erstellen Sie zusätzlich zum Interface Druckbar ein weiteres Interface Suchbar. Dieses Interface soll folgende Methode enthalten:

boolean suchen(String text);

Die Klasse Projekt soll nun beide Interfaces implementieren:

  • Druckbar
  • Suchbar

Die Methode suchen(String text) soll prüfen, ob die Projektbezeichnung den gesuchten Text enthält. Legen Sie im Hauptprogramm ein Projektobjekt an und

  • geben Sie die Projektdaten mit der Methode drucken() aus,
  • suchen Sie nach einem vorgegebenen Text,
  • geben Sie aus, ob der Suchtext gefunden wurde.

28.2.2 Lernziele

Diese Aufgabe geht nochmals auf Interfaces ein. Sie verstehen, dass mit diesen verschiedene Fähigkeiten unabhängig von der Klassenhierarchie beschrieben werden können. Außerdem erfahren sie, wie mehrere Interfaces gemeinsam genutzt werden, um Klassen flexibel zu erweitern und unterschiedliche Funktionalitäten - beispielsweise Suchen und Drucken - in einer Klasse bereitzustellen.

28.2.3 Ergebnis Programmlauf

Projekt: BPMN-Einführung

Suchtext gefunden.

 

28.2.4 Lösung

Klasse Projekt

package firma;

interface Druckbar {

  void drucken();

}

interface Suchbar {

  boolean suchen(String text);

}

class Projekt implements Druckbar, Suchbar {

  String bez;

  Projekt(String bez) {

    this.bez = bez;

  }

  @Override

  public void drucken() {

    System.out.println("Projekt: " + bez);

  }

  

  @Override

  public boolean suchen(String text) {

    return bez.contains(text);

  }

}

Klasse Aufgabe38

public class Aufgabe38 {

    public static void main(String[] args) {

      Projekt p1 = new Projekt("BPMN-Einführung");

      p1.drucken();

      if (p1.suchen("BPMN")) {

        System.out.println("Suchtext gefunden.");

      } else {

        System.out.println("Suchtext nicht gefunden.");

      }

    }

}

28.2.5 Betrachtete Themen

  • Annotation @Override
  • Datentyp boolean
  • Implementierung von Interfaces
  • Interfaces
  • Konstruktoren
  • Mehrere Interfaces
  • Mehrfachimplementierung
  • Methode contains()
  • Methoden mit Rückgabewert
  • Objekterzeugung mit new
  • Parameterübergabe
  • Suche in Zeichenketten
  • Verpflichtung zur Implementierung von Methoden
  • Verzweigungen (if)
  • Zeichenketten (Strings)

28.2.6 Erläuterung

Eine Klasse kann in Java nur von einer einzigen Klasse erben. Sie kann aber mehrere Interfaces implementieren. Deshalb ist Folgendes möglich:

class Projekt implements Druckbar, Suchbar

Die Klasse Projekt "verpflichtet sich" damit, alle Methoden aus beiden Interfaces bereitzustellen. Die Methode bez.contains(text) prüft, ob der Suchtext in der Projektbezeichnung enthalten ist.

 

28.3 Aufgabe 39 - Polymorphie

28.3.1 Aufgabenstellung

In einem Unternehmen sollen verschiedene Arten von Angestellten verwaltet werden. Erstellen Sie eine abstrakte Klasse Angestellter mit den Attributen

  • name
  • vorname

sowie der abstrakten Methode anzeigen().

Leiten Sie davon die Klassen Sachbearbeiter und Abteilungsleiter ab. Die Klasse Sachbearbeiter erhält zusätzlich das Attribut fachgebiet. Die Klasse Abteilungsleiter erhält zusätzlich das Attribut budget.

Implementieren Sie in beiden Klassen die Methode anzeigen(). Die Ausgabe soll die Art des Angestellten sowie die zugehörigen Daten enthalten.

Legen Sie im Hauptprogramm folgende Objekte an:

Sachbearbeiter

  • Anna Müller
  • Fachgebiet: Personal

Abteilungsleiter

  • Bernd Schmidt
  • Budget: 500000,0

Speichern Sie beide Objekte in einem Array vom Typ Angestellter[]. Durchlaufen Sie das Array mit einer Schleife und geben Sie die Daten aller Angestellten aus. Verwenden Sie dabei die Methode anzeigen() ohne zu prüfen, um welche konkrete Subklasse es sich handelt.

28.3.2 Lernziele

Sie lernen das Konzept der Polymorphie kennen, verstehen, dass eine Referenz einer Oberklasse auf Objekte verschiedener Unterklassen zeigen kann und dass beim Methodenaufruf automatisch die zur tatsächlichen Objektklasse gehörende Implementierung ausgeführt wird. Dadurch erkennen sie einen der zentralen Vorteile der Vererbung und lernen, flexible und erweiterbare objektorientierte Programme zu entwickeln.

28.3.3 Ergebnis Programmlauf

Sachbearbeiter:

Anna Müller

Fachgebiet: Personal

Abteilungsleiter:

Bernd Schmidt

Budget: 500000.0

 

28.3.4 Lösung

Hier mal wieder: Alle Programme bzw. Klassen in einer Datei.

package firma;

abstract class Angestellter {

    String name;

    String vorname;

    abstract void anzeigen();

}

class Sachbearbeiter extends Angestellter {

    String fachgebiet;

    @Override

    void anzeigen() {

      System.out.println("Sachbearbeiter:");

      System.out.println(vorname + " " + name);

      System.out.println("Fachgebiet: " + fachgebiet);

    }

}

class Abteilungsleiter extends Angestellter {

    double budget;

    @Override

    void anzeigen() {

      System.out.println("Abteilungsleiter:");

      System.out.println(vorname + " " + name);

      System.out.println("Budget: " + budget);

    }

}

public class Aufgabe39 {

    public static void main(String[] args) {

      Sachbearbeiter s1 = new Sachbearbeiter();

      s1.name = "Müller";

      s1.vorname = "Anna";

      s1.fachgebiet = "Personal";

      Abteilungsleiter a1 = new Abteilungsleiter();

      a1.name = "Schmidt";

      a1.vorname = "Bernd";

      a1.budget = 500000.0;

      Angestellter[] personal = {s1, a1};

      for (Angestellter a : personal) {

        a.anzeigen();

        System.out.println();

      }

    }

}

28.3.5 Betrachtete Themen

  • Abstrakte Klassen
  • Abstrakte Methoden
  • Vererbung
  • Spezialisierung von Klassen
  • Super- und Subklassen
  • Polymorphie
  • Dynamische Bindung
  • Überschreiben von Methoden (Override)
  • Annotation @Override
  • Referenzen auf Superklassen
  • Objekterzeugung mit new
  • Objektarrays
  • Array vom Typ einer Superklasse
  • Speicherung unterschiedlicher Objekttypen in einem gemeinsamen Array
  • ForEach-Schleife
  • Methodenaufrufe über Superklassenreferenzen
  • Gemeinsame Verarbeitung unterschiedlicher Objekttypen

28.3.6 Ziel der Aufgabe, vertieft

Die Aufgabe zeigt das Zusammenspiel von

  • abstrakten Klassen,
  • Vererbung,
  • überschriebenen Methoden und
  • Polymorphie.

Obwohl die Objekte über Referenzen des Typs Angestellter angesprochen werden, wird automatisch die jeweils passende Implementierung der Methode anzeigen() ausgeführt. Dieses Verhalten wird als Polymorphie bezeichnet.

Etwas genauer

Das Array besitzt den Typ:

Angestellter[]

Trotzdem können darin Objekte der Klassen Sachbearbeiter und Abteilungsleiter gespeichert werden. Das funktioniert, weil beide Subklassen von Angestellter sind. Beim Methodenaufruf

a.anzeigen();

entscheidet Java zur Laufzeit, welche konkrete Methode ausgeführt wird.

  • Bei einem Sachbearbeiter wird die Methode aus der Klasse Sachbearbeiter ausgeführt.
  • Bei einem Abteilungsleiter wird die Methode aus der Klasse Abteilungsleiter ausgeführt.

Damit sind wir bei der Polymorphie.

28.4 Aufgabe 40 - Laufzeittyp

Laufzeittyp von Objekten mit instanceof

28.4.1 Aufgabenstellung

In den vorhergehenden Aufgaben wurden die Klassen Angestellter, Sachbearbeiter und Abteilungsleiter verwendet. Die Objekte wurden dabei über Referenzen des Typs Angestellter verwaltet.

Erstellen Sie erneut die Klassen

  • Angestellter (abstrakte Superklasse)
  • Sachbearbeiter
  • Abteilungsleiter

mit den bereits bekannten Attributen.

Legen Sie im Hauptprogramm mindestens einen Sachbearbeiter und einen Abteilungsleiter an und speichern Sie die Objekte in einem Array vom Typ

Angestellter[]

Durchlaufen Sie das Array mit einer Schleife und geben Sie die Daten aller Angestellten aus. Prüfen Sie anschließend für jedes Objekt zur Laufzeit, ob es sich um einen

  • Sachbearbeiter oder
  • Abteilungsleiter

handelt.

Verwenden Sie dazu den Operator

instanceof

Geben Sie zusätzlich aus, zu welcher konkreten Klasse das jeweilige Objekt gehört.

28.4.2 Lernziele

Hier wird gelernt, wie mit dem Operator instanceof der tatsächliche Typ eines Objekts zur Laufzeit überprüft werden kann. Sie verstehen, warum solche Typprüfungen insbesondere bei Vererbung und Polymorphie notwendig sein können, und lernen, objektspezifische Methoden oder Attribute sicher zu verwenden. Dadurch vertiefen sie ihr Verständnis des Zusammenspiels von Referenztyp und tatsächlichem Objekttyp in Java.

28.4.3 Ergebnis Programmlauf

Anna Müller

Fachgebiet: Personal

Objekttyp: Sachbearbeiter

Bernd Schmidt

Budget: 500000.0

Objekttyp: Abteilungsleiter

28.4.4 Lösung

Hier mal wieder: Alle Programme in einer Datei. Ist aber nicht empfohlen

package firma;

abstract class Angestellter {

    String name;

    String vorname;

    abstract void anzeigen();

}

class Sachbearbeiter extends Angestellter {

    String fachgebiet;

    @Override

    void anzeigen() {

      System.out.println(vorname + " " + name);

      System.out.println("Fachgebiet: " + fachgebiet);

    }

}

class Abteilungsleiter extends Angestellter {

    double budget;

    @Override

    void anzeigen() {

      System.out.println(vorname + " " + name);

      System.out.println("Budget: " + budget);

    }

}

public class Aufgabe40 {

    public static void main(String[] args) {

      Sachbearbeiter s1 = new Sachbearbeiter();

      s1.name = "Müller";

      s1.vorname = "Anna";

      s1.fachgebiet = "Personal";

      Abteilungsleiter a1 = new Abteilungsleiter();

      a1.name = "Schmidt";

      a1.vorname = "Bernd";

      a1.budget = 500000.0;

      Angestellter[] personal = {s1, a1};

      for (Angestellter a : personal) {

        a.anzeigen();

        if (a instanceof Sachbearbeiter) {

        System.out.println("Objekttyp: Sachbearbeiter");

        }

        if (a instanceof Abteilungsleiter) {

        System.out.println("Objekttyp: Abteilungsleiter");

        }

        System.out.println();

      }

    }

}

28.4.5 Betrachtete Themen

  • Abstrakte Klassen
  • Abstrakte Methoden
  • Vererbung
  • Spezialisierung von Klassen
  • Polymorphie
  • Referenzen auf Superklassen
  • Überschreiben von Methoden (Override)
  • Annotation @Override
  • Objekterzeugung mit new
  • Objektarrays
  • Array vom Typ einer Superklasse
  • Speicherung unterschiedlicher Objekttypen in einem gemeinsamen Array
  • ForEach-Schleife
  • Dynamische Bindung von Methodenaufrufen
  • Laufzeittyp eines Objekts
  • Operator instanceof
  • Typprüfung zur Laufzeit
  • Ausgabe typabhängiger Informationen
  • Zusammenspiel von Polymorphie und Vererbung

28.4.6 Erläuterung

Der Operator instanceof prüft, ob ein Objekt zu einer bestimmten Klasse gehört. Beispiel:

a instanceof Sachbearbeiter

Diese Prüfung ergibt true, wenn das Objekt tatsächlich ein Sachbearbeiter ist. Das ist nützlich, wenn man in einem Array oder einer Liste Objekte unterschiedlicher Subklassen gespeichert hat.

Theorieaspekte

Diese Aufgabe demonstriert wiederum das Konzept der Referenzen und zeigt:

Eine Referenz vom Typ der Superklasse kann auf Objekte verschiedener Subklassen zeigen.

Dadurch können unterschiedliche Objekte gemeinsam verwaltet werden, beispielsweise in einem Array vom Typ Angestellter[]. Während die Methode anzeigen() durch Polymorphie automatisch die passende Implementierung der jeweiligen Subklasse ausführt, ermöglicht der Operator instanceof die explizite Feststellung des tatsächlichen Objekttyps zur Laufzeit. Dies bildet die Grundlage für die moderne Variante mit Pattern Matching für instanceof.

Ziel der Aufgabe

Die Aufgabe zeigt u.a., dass unterschiedliche Objekte über Referenzen einer gemeinsamen Superklasse angesprochen werden können. Der tatsächliche Typ eines Objekts ist zwar bei der Erzeugung festgelegt, kann jedoch bei der Verarbeitung über eine Referenz der Superklasse erst zur Laufzeit bestimmt werden.

Mit Hilfe des Operators instanceof kann dieser Laufzeittyp ermittelt werden. Dadurch lassen sich typabhängige Verarbeitungen durchführen.

Vgl. zu den Konzepten Referenz und Typ auch Kapitel 2.

 

28.5 Aufgabe 40a - Angestellte

Verwaltung von Informationen zu Angestellten

28.5.1 Aufgabenstellung

Hier wird Aufgabe 40 etwas ausgebaut.

In einem Unternehmen sollen verschiedene Gruppen von Angestellten verwaltet werden. Erstellen Sie eine abstrakte Klasse Angestellter mit den Attributen

  • name
  • vorname

sowie der abstrakten Methode

anzeigen()

Leiten Sie davon die Klassen Sachbearbeiter und Abteilungsleiter ab. Die Klasse Sachbearbeiter erhält zusätzlich das Attribut

fachgebiet

Die Klasse Abteilungsleiter erhält zusätzlich das Attribut

budget

Implementieren Sie in beiden Klassen die Methode anzeigen(), sodass die jeweiligen Daten ausgegeben werden.

Legen Sie im Hauptprogramm zunächst folgende Objekte an:

 


Gruppe

Vorname

Name

Zusatzinformation

Sachbearbeiter

Anna

Müller

Personal

Abteilungsleiter

Bernd

Schmidt

Budget: 500000,0


 

Speichern Sie die Objekte in einer gemeinsamen Liste vom Typ

ArrayList<Angestellter>

Fragen Sie anschließend mit einem Dialogfenster, ob ein weiterer Angestellter eingegeben werden soll.

Wird die Frage mit Ja beantwortet, soll eine grafische Eingabemaske erscheinen. Die Eingabefelder sollen bereits mit Beispieldaten vorbelegt sein. Diese können unverändert übernommen oder geändert werden. Der Benutzer soll auswählen können, ob ein

  • Sachbearbeiter oder
  • Abteilungsleiter

angelegt werden soll.

Das neu erzeugte Objekt ist anschließend in die Liste aufzunehmen. Geben Sie danach alle vorhandenen Angestellten aus. Verwenden Sie zusätzlich die moderne Form von instanceof (Pattern Matching), um den konkreten Typ eines Objekts zu ermitteln und die gruppenabhängigen Attribute auszugeben.

28.5.2 Lernziele

Hier wird gelernt, wie ein Java-Programm um eine einfache grafische Benutzeroberfläche (GUI) erweitert wird. Sie erfahren, wie Eingabefelder, Auswahllisten und Schaltflächen mit Swing erstellt und zur Erfassung von Objektdaten genutzt werden. Außerdem erkennen sie, wie Benutzereingaben verarbeitet und anschließend zur Erzeugung und Ausgabe von Objekten verwendet werden. Dadurch wird der Übergang von reinen Konsolenprogrammen zu ereignisgesteuerten Anwendungen vorbereitet.

28.5.3 Ergebnis Programmlauf

Ohne einem zusätzlichen Angestellten ("Nein")

Anna Müller

Fachgebiet: Personal

Objekttyp: Sachbearbeiter

Fachgebiet direkt zugreifbar: Personal

Bernd Schmidt

Budget: 500000.0

Objekttyp: Abteilungsleiter

Budget direkt zugreifbar: 500000.0

Mit einem zusätzlichen Angestellten ("Ja")

Anna Müller

Fachgebiet: Personal

Objekttyp: Sachbearbeiter

Fachgebiet direkt zugreifbar: Personal

Bernd Schmidt

Budget: 500000.0

Objekttyp: Abteilungsleiter

Budget direkt zugreifbar: 500000.0

Clara Weber

Fachgebiet: Einkauf

Objekttyp: Sachbearbeiter

Fachgebiet direkt zugreifbar: Einkauf

Hinweise

  • Verwenden Sie eine ArrayList<Angestellter> zur Verwaltung der Objekte.
  • Nutzen Sie JOptionPane, JPanel, JTextField, JComboBox und GridLayout für die Eingabemaske.

Verwenden Sie außerdem die moderne Schreibweise

if (a instanceof Sachbearbeiter s)

anstelle eines expliziten Casts.

28.5.4 Exkurs - Explizit vs, implizit

Ein Cast ist in Java eine Typumwandlung. Man unterscheidet zwischen impliziten und expliziten Casts. Die ältere Schreibweise (mit explizitem Cast) ist wie folgt:

if (a instanceof Sachbearbeiter) {

  Sachbearbeiter s = (Sachbearbeiter) a;

  s.bearbeiteVorgang();

}

Hier bedeutet (Sachbearbeiter) a den expliziten Cast. Dem Compiler wird mitgeteilt: Behandle die Referenz a jetzt als Sachbearbeiter. Ohne diesen Cast würde der Compiler einen Fehler melden.

Seit Java 16 kann man den Cast direkt mit instanceof verbinden:

if (a instanceof Sachbearbeiter s) {

  s.bearbeiteVorgang();

}

Hier geschieht zweierlei gleichzeitig:

  • Es wird geprüft, ob a tatsächlich ein Sachbearbeiter ist.
  • Ist dies der Fall, wird automatisch die Variable Sachbearbeiter s angelegt.

Zusammenfassung

  • Expliziter Cast: Der Programmierer schreibt die Typumwandlung selbst, z. B. (Sachbearbeiter) a.
  • Moderne Schreibweise: instanceof prüft den Typ und führt die Typumwandlung automatisch durch, sodass kein expliziter Cast mehr erforderlich ist.

28.5.5 Lösung

Klasse Angestellter

package firma;

abstract class Angestellter {

  String name;

  String vorname;

  abstract void anzeigen();

}

Klasse Sachbearbeiter

package firma;

class Sachbearbeiter extends Angestellter {

  String fachgebiet;

  @Override

  void anzeigen() {

    System.out.println(vorname + " " + name);

    System.out.println("Fachgebiet: " + fachgebiet);

  }

}

Klasse Abteilungsleiter

package firma;

class Abteilungsleiter extends Angestellter {

  double budget;

  

  @Override

  void anzeigen() {

    System.out.println(vorname + " " + name);

    System.out.println("Budget: " + budget);

  }

}

Klasse Aufgabe40a

import java.util.ArrayList;

import javax.swing.*;

import java.awt.GridLayout;

public class Aufgabe40a {

  public static void main(String[] args) {

    Sachbearbeiter s1 = new Sachbearbeiter();

    s1.name = "Müller";

    s1.vorname = "Anna";

    s1.fachgebiet = "Personal";

    

    Abteilungsleiter a1 = new Abteilungsleiter();

    a1.name = "Schmidt";

    a1.vorname = "Bernd";

    a1.budget = 500000.0;

    

    ArrayList<Angestellter> personal = new ArrayList<>();

    personal.add(s1);

    personal.add(a1);

    

Fragefenster: Soll überhaupt ein neuer Angestellter eingegeben werden?

    int antwort = JOptionPane.showConfirmDialog(null,

      "Soll ein neuer Angestellter eingegeben werden?","Eingabe",

      JOptionPane.YES_NO_OPTION

    );

Nur wenn der Benutzer "Ja" auswählt, wird die Eingabemaske angezeigt.

    if (antwort == JOptionPane.YES_OPTION) {

      JComboBox<String> typAuswahl =

        new JComboBox<>(new String[]{"Sachbearbeiter", "Abteilungsleiter"});

        

Textfelder mit vorgegebenen Beispielwerten.
Diese Werte erscheinen bereits in der Eingabemaske.
Der Benutzer kann sie übernehmen oder ändern.

      JTextField nameFeld = new JTextField("Weber");

      JTextField vornameFeld = new JTextField("Clara");

      JTextField fachgebietFeld = new JTextField("Einkauf");

      JTextField budgetFeld = new JTextField("250000.0");

      

JPanel ist ein Behälter für die Elemente der Eingabemaske.
GridLayout(0, 2) bedeutet:
- beliebig viele Zeilen, aber immer zwei Spalten.
Links steht jeweils die Beschriftung, rechts das Eingabefeld.

      JPanel panel = new JPanel(new GridLayout(0, 2));

      

Die einzelnen Bestandteile werden der Maske hinzugefügt.

      panel.add(new JLabel("Typ:"));

      panel.add(typAuswahl);

      

      panel.add(new JLabel("Name:"));

      panel.add(nameFeld);

      

      panel.add(new JLabel("Vorname:"));

      panel.add(vornameFeld);

      

      panel.add(new JLabel("Fachgebiet:"));

      panel.add(fachgebietFeld);

      

      panel.add(new JLabel("Budget:"));

      panel.add(budgetFeld);

      

Die fertige Eingabemaske wird angezeigt.
Der Benutzer kann OK oder Abbrechen wählen.

      int eingabe = JOptionPane.showConfirmDialog(

        null, panel,"Neuer Angestellter",

        JOptionPane.OK_CANCEL_OPTION

      );

      

Nur wenn OK gedrückt wurde, werden die eingegebenen Werte verarbeitet.

      if (eingabe == JOptionPane.OK_OPTION) {

Auslesen der ausgewählten Klasse aus der Auswahlliste.

      String typ = (String) typAuswahl.getSelectedItem();

      

Fall 1: Es soll ein Sachbearbeiter erzeugt werden.

      if (typ.equals("Sachbearbeiter")) {

        Sachbearbeiter s2 = new Sachbearbeiter();

        

Die Inhalte der Textfelder werden in das neue Objekt übernommen.

        s2.name = nameFeld.getText();

        s2.vorname = vornameFeld.getText();

        s2.fachgebiet = fachgebietFeld.getText();

        

Das neue Objekt wird zur Liste hinzugefügt.

        personal.add(s2);

      }

      

Fall 2: Es soll ein Abteilungsleiter erzeugt werden.

      if (typ.equals("Abteilungsleiter")) {

        Abteilungsleiter a2 = new Abteilungsleiter();

        a2.name = nameFeld.getText();

        a2.vorname = vornameFeld.getText();

        

Der Inhalt eines Textfeldes ist zunächst immer ein String.
Für das Budget wird aber ein double-Wert benötigt.
Deshalb muss der Text in eine Zahl umgewandelt werden.

        a2.budget = Double.parseDouble(budgetFeld.getText());

        

Auch dieses neue Objekt wird zur Liste hinzugefügt.

        personal.add(a2);

      }

    }

  }

  for (Angestellter a : personal) {

      a.anzeigen();

      if (a instanceof Sachbearbeiter s) {

        System.out.println("Objekttyp: Sachbearbeiter");

        System.out.println("Fachgebiet direkt zugreifbar: "

        + s.fachgebiet);

      }

      if (a instanceof Abteilungsleiter al) {

        System.out.println("Objekttyp: Abteilungsleiter");

        System.out.println("Budget direkt zugreifbar: " + al.budget);

      }

      System.out.println();

    }

  }

}

28.5.6 Erläuterungen

  • JOptionPane.showConfirmDialog erzeugt ein Dialogfenster.
  • JPanel sammelt mehrere Eingabeelemente zu einer Maske.
  • JTextField enthält Texteingaben.
  • JComboBox bietet eine Auswahl an.
  • GridLayout(0, 2) ordnet die Maske zweispaltig an.
  • getText() liest den Inhalt eines Textfeldes aus.
  • Double.parseDouble() wandelt einen Text in eine Zahl um.

28.5.7 Betrachtete Themen

  • Abstrakte Klassen
  • Abstrakte Methoden
  • Vererbung
  • Spezialisierung von Klassen
  • Polymorphie
  • Überschreiben von Methoden (Override)
  • Annotation @Override
  • Referenzen auf Superklassen
  • Objekterzeugung mit new
  • ArrayList
  • Generics (ArrayList<Angestellter>)
  • Hinzufügen von Objekten zu Listen
  • ForEach-Schleife
  • Pattern Matching mit instanceof
  • Grafische Benutzerschnittstellen (GUI)
  • Swing-Komponenten
  • Dialogfenster mit JOptionPane
  • Eingabemasken mit JPanel
  • Layout-Manager (GridLayout)
  • Textfelder (JTextField)
  • Auswahllisten (JComboBox)
  • Beschriftungen (JLabel)
  • Benutzereingaben verarbeiten
  • Verzweigungen (if)
  • Zeichenkettenvergleich mit equals
  • Umwandlung von Zeichenketten in Zahlen (Double.parseDouble)
  • Dynamisches Erzeugen von Objekten
  • Dynamische Erweiterung einer Objektliste
  • Modul java.desktop

 

29 Übungsaufgaben Teil 10

29.1 Aufgabe 41 - Records

Daten effizient verwalten

29.1.1 Aufgabenstellung

Erstellen Sie für die Verwaltung von PCs einen Record PC. Ein PC soll folgende Daten besitzen:

  • invNr
  • bez
  • typ

Legen Sie mehrere PCs an und geben Sie deren Daten aus. Verwenden Sie dabei die automatisch erzeugten Zugriffsmethoden des Records.

29.1.2 Lernziele

Gegenstand dieser Aufgabe ist das Konzept der Record-Klassen in Java. Sie verstehen, wie sich einfache Datenklassen mit Records besonders kompakt definieren lassen und welche Methoden - beispielsweise Konstruktor, Zugriffsmethoden (Accessor), toString(), equals() und hashCode() - automatisch erzeugt werden. Dadurch erkennen sie, wann Records gegenüber herkömmlichen Klassen eine übersichtliche und effiziente Alternative darstellen.

 

29.1.3 Lösung

package firma;

record PC(

  int invNr,

  String bez,

  String typ

  ) {

}

Obige geschweifte Klammern sind notwendig: Body-Teil der Deklaration. Bei Bedarf kommen hier zusätzliche Methoden rein

public class Aufgabe41 {

    public static void main(String[] args) {

      PC pc1 = new PC(1001, "Dell Optiplex", "Desktop");

      PC pc2 = new PC(1002, "Lenovo ThinkPad", "Notebook");

      PC pc3 = new PC(1003, "HP EliteDesk", "Desktop");

      System.out.println("Inventarnummer: " + pc1.invNr());

      System.out.println("Bezeichnung: " + pc1.bez());

      System.out.println("Typ: " + pc1.typ());

      System.out.println();

      System.out.println(pc2);

      System.out.println(pc3);

  }

}

Ergebnis Programmlauf

Inventarnummer: 1001

Bezeichnung: Dell Optiplex

Typ: Desktop

PC[invNr=1002, bez=Lenovo ThinkPad, typ=Notebook]

PC[invNr=1003, bez=HP EliteDesk, typ=Desktop]

29.1.4 Betrachtete Themen

  • Ausgabe von Record-Daten
  • automatisch erzeugte Methode toString()
  • automatisch erzeugte Methoden equals() und hashCode()
  • automatisch erzeugte Zugriffsmethoden (Accessor-Methoden)
  • automatisch erzeugter Konstruktor
  • Erzeugen von Record-Objekten
  • kompakte Schreibweise für Datenklassen
  • Komponenten eines Records
  • Record-Klassen (Records)
  • unveränderliche Datenobjekte (Immutable Objects)
  • Verwendung der automatisch erzeugten Methoden
  • Zugriff ohne Getter-Methoden

29.1.5 Erläuterung

Ein Record ist eine besonders kurze Schreibweise für Klassen, die vor allem Daten speichern.

Aus

record PC(int invNr, String bez, String typ)

erzeugt Java automatisch:

  • einen Konstruktor
  • Zugriffsmethoden
  • die Methode toString()
  • die Methode equals()
  • die Methode hashCode()

Die Zugriffsmethoden heißen nicht getBez(), sondern einfach:

  • bez()
  • typ()
  • invNr()

Records sind standardmäßig unveränderlich. Nach der Erzeugung kann man die Werte also nicht mehr direkt ändern.

 

29.2 Aufgabe 42 - Projektmitarbeit

Angestellte und ihre Projekte

29.2.1 Aufgabenstellung

Erstellen Sie ein zusammenhängendes Programm zur Verwaltung von Angestellten und Projekten. Das Programm soll mehrere fortgeschrittene OOP-Konzepte miteinander verbinden.

Abstrakte Klasse

Erstellen Sie eine abstrakte Klasse Angestellter. Diese Klasse soll enthalten:

String name;

String vorname;

abstract double berechneBonus();

abstract void anzeigen();

Subklassen

Leiten Sie aus Angestellter zwei Subklassen ab:

  • Sachbearbeiter
  • Abteilungsleiter

Ein Sachbearbeiter besitzt zusätzlich:

String fachgebiet;

Ein Abteilungsleiter besitzt zusätzlich:

double budget;

Interfaces

Erstellen Sie zwei Interfaces:

interface Druckbar

mit der Methode:

void drucken();

und

interface Suchbar

mit der Methode:

boolean suchen(String text);

Die Klasse Projekt soll beide Interfaces implementieren.

Polymorphie

Speichern Sie mehrere Angestellte in einer Liste:

List<Angestellter>

Durchlaufen Sie diese Liste und rufen Sie polymorph die Methoden anzeigen() und berechneBonus() auf.

Records

Erstellen Sie zusätzlich einen Record mit der Bezeichnung PC. Ein PC besitzt folgende Attribute:

int invNr;

String bez;

String typ;

Legen Sie mindestens einen PC an und geben Sie ihn aus.

Ausgaben

Das Programm soll ausgeben:

  • alle Mitarbeiter
  • deren Bonus
  • alle Projekte
  • Suchergebnisse für Projekte
  • mindestens einen PC

29.2.2 Lernziele

In dieser Aufgabe wenden Sie mehrere grundlegende Konzepte der objektorientierten Programmierung gemeinsam an. Sie lernen, wie sich eine Klassenhierarchie mit Vererbung und abstrakten Klassen aufbauen lässt, wie Interfaces einheitliche Funktionalitäten bereitstellen und wie Polymorphie den einheitlichen Umgang mit unterschiedlichen Objektarten ermöglicht. Darüber hinaus vertiefen Sie den Einsatz von Collections zur Verwaltung mehrerer Objekte.

 

29.2.3 Lösung

Klasse Angestellter mit Interfaces

package firma;

interface Druckbar {

    void drucken();

}

interface Suchbar {

    boolean suchen(String text);

}

abstract class Angestellter implements Druckbar {

    String name;

    String vorname;

    Angestellter(String name, String vorname) {

      this.name = name;

      this.vorname = vorname;

    }

    abstract void anzeigen();

    abstract double berechneBonus();

    @Override

    public void drucken() {

      System.out.println(vorname + " " + name);

    }

}

Klasse Sachbearbeiter

package firma;

class Sachbearbeiter extends Angestellter {

    String fachgebiet;

    Sachbearbeiter(String name, String vorname, String fachgebiet) {

      super(name, vorname);

      this.fachgebiet = fachgebiet;

    }

    @Override

    void anzeigen() {

      System.out.println("Sachbearbeiter:");

      System.out.println(vorname + " " + name);

      System.out.println("Fachgebiet: " + fachgebiet);

    }

    @Override

    double berechneBonus() {

      return 500.0;

    }

}

Klasse Abteilungsleiter

package firma;

class Abteilungsleiter extends Angestellter {

    double budget;

    Abteilungsleiter(String name, String vorname, double budget) {

      super(name, vorname);

      this.budget = budget;

    }

    @Override

    void anzeigen() {

      System.out.println("Abteilungsleiter:");

      System.out.println(vorname + " " + name);

      System.out.println("Budget: " + budget);

    }

    @Override

    double berechneBonus() {

      return 5000.0;

    }

}

Klasse Projekt

package firma;

class Projekt implements Druckbar, Suchbar {

    String projektNr;

    String bezeichnung;

    Projekt(String projektNr, String bezeichnung) {

      this.projektNr = projektNr;

      this.bezeichnung = bezeichnung;

    }

    @Override

    public void drucken() {

      System.out.println(projektNr + ": " + bezeichnung);

    }

    @Override

    public boolean suchen(String text) {

      return bezeichnung.contains(text);

    }

}

record PC(

      int invNr,

      String bez,

      String typ

) {

}

Klasse Aufgabe42

package firma;

import java.util.ArrayList;

import java.util.List;

public class Aufgabe42 {

    public static void main(String[] args) {

      List<Angestellter> personal = new ArrayList<>();

      personal.add(new Sachbearbeiter("Müller", "Anna", "Personal"));

      personal.add(new Abteilungsleiter("Schmidt", "Bernd", 500000.0));

      personal.add(new Sachbearbeiter("Weber", "Clara", "Einkauf"));

      System.out.println("Alle Mitarbeiter");

      System.out.println("================");

      for (Angestellter a : personal) {

        a.anzeigen();

        System.out.println("Bonus: " + a.berechneBonus());

        if (a instanceof Sachbearbeiter) {

        System.out.println("Typprüfung: Sachbearbeiter");

        }

        if (a instanceof Abteilungsleiter) {

        System.out.println("Typprüfung: Abteilungsleiter");

        }

        System.out.println();

      }

      List<Projekt> projekte = new ArrayList<>();

      projekte.add(new Projekt("P100", "BPMN-Einführung"));

      projekte.add(new Projekt("P200", "Prozessoptimierung"));

      projekte.add(new Projekt("P300", "Java-Schulung"));

      System.out.println("Alle Projekte");

      System.out.println("=============");

      for (Projekt p : projekte) {

        p.drucken();

      }

      System.out.println();

      System.out.println("Suchergebnis für 'Java'");

      System.out.println("=======================");

      for (Projekt p : projekte) {

        if (p.suchen("Java")) {

        p.drucken();

        }

      }

      System.out.println();

      PC pc1 = new PC(1001, "Dell Optiplex", "Desktop");

      System.out.println("PC-Daten");

      System.out.println("========");

      System.out.println("Inventarnummer: " + pc1.invNr());

      System.out.println("Bezeichnung: " + pc1.bez());

      System.out.println("Typ: " + pc1.typ());

    }

}

29.2.4 Ergebnis Programmlauf

Alle Mitarbeiter

================

Sachbearbeiter:

Anna Müller

Fachgebiet: Personal

Bonus: 500.0

Typprüfung: Sachbearbeiter

Abteilungsleiter:

Bernd Schmidt

Budget: 500000.0

Bonus: 5000.0

Typprüfung: Abteilungsleiter

Sachbearbeiter:

Clara Weber

Fachgebiet: Einkauf

Bonus: 500.0

Typprüfung: Sachbearbeiter

Alle Projekte

=============

P100: BPMN-Einführung

P200: Prozessoptimierung

P300: Java-Schulung

Suchergebnis für 'Java'

=======================

P300: Java-Schulung

PC-Daten

========

Inventarnummer: 1001

Bezeichnung: Dell Optiplex

Typ: Desktop

29.2.5 Betrachtete Themen

  • Abstrakte Klassen
  • Abstrakte Methoden
  • ArrayList
  • Aufruf von super(...)
  • Ausgabe von Objektdaten
  • Automatisch erzeugte Zugriffsmethoden (Accessor-Methoden)
  • Collections mit List
  • Dynamische Methodenbindung
  • foreach-Schleife
  • Implementierung mehrerer Interfaces
  • instanceof
  • Interfaces
  • Konstruktoren
  • Methoden mit Rückgabewert
  • Superklasse und Subklassen
  • Objekterzeugung mit new
  • Polymorphe Objektlisten
  • Polymorphie
  • Records
  • Suche mit contains()
  • this-Referenz
  • Typprüfung zur Laufzeit
  • Überschreiben von Methoden (Override)
  • Vererbung
  • Zusammenarbeit mehrerer Klassen
  • Zusammenarbeit verschiedener OOP-Konzepte

29.2.6 Erläuterung

Dieses Projekt verbindet mehrere wichtige OOP-Konzepte. Die Klasse Angestellter ist abstrakt. Sie beschreibt gemeinsame Merkmale aller Angestellten, kann aber selbst nicht direkt als Objekt erzeugt werden. Die Klassen Sachbearbeiter und Abteilungsleiter erben von Angestellter. Beide Klassen müssen die abstrakten Methoden

  • anzeigen()
  • berechneBonus()

konkret implementieren. Durch die Liste

List<Angestellter> personal

können verschiedene Subklassenobjekte gemeinsam verwaltet werden. Beim Aufruf

a.anzeigen();

entscheidet Java zur Laufzeit, ob die Methode aus Sachbearbeiter oder aus Abteilungsleiter ausgeführt wird. Das ist Polymorphie.

Die Klasse Projekt implementiert zwei Interfaces:

  • Druckbar
  • Suchbar

Damit besitzt ein Projekt sowohl eine Druckfunktion als auch eine Suchfunktion. Der Record PC zeigt abschließend, wie einfache Datenobjekte in Java kompakt formuliert werden können.

 

29.3 Aufgabe 43 - Exception Handling

Vorgedachte Fehlermeldungen

29.3.1 Aufgabenstellung

Ausgangspunkt sind Studierende einer Hochschule.

Erstellen Sie ein Java-Programm Aufgabe43, das eine Matrikelnummer und die aktuelle Semesterzahl von der Tastatur einliest.

Da Benutzereingaben fehlerhaft sein können, soll das Programm die Eingaben gegen ungültige Werte absichern. Verwenden Sie dazu einen try-catch-Block.

Das Programm soll folgende Schritte ausführen:

  • Matrikelnummer eingeben
  • Semesterzahl eingeben
  • Beide Werte als ganze Zahlen (int) speichern
  • Die eingelesenen Werte auf dem Bildschirm ausgeben

Falls anstelle einer Zahl ein anderer Text eingegeben wird, soll das Programm nicht abbrechen, sondern eine verständliche Fehlermeldung ausgeben, z. B.:

Fehler: Bitte nur ganze Zahlen eingeben.

Schließen Sie den verwendeten Scanner am Ende des Programms wieder.

29.3.2 Lernziele

Mit dieser Aufgabe lernen Sie, wie Programme auf fehlerhafte Benutzereingaben reagieren können. Sie lernen den Einsatz von Exception Handling mit try und catch kennen und erfahren, wie die Ausnahme NumberFormatException abgefangen wird, damit das Programm kontrolliert weiterarbeiten bzw. sauber beendet werden kann.

29.3.3 Ergebnis Programmlauf

Matrikelnummer: 1009

Semester: 12

Matrikelnummer: 1009

Semester: 12

bzw. bei fehlerhafter Eingabe:

Matrikelnummer: 2002

Semester: abc

Fehler: Bitte nur ganze Zahlen eingeben.

 

29.3.4 Lösung

package hochschule;

import java.util.Scanner;

public class Aufgabe43 {

  public static void main(String[] args) {

    Scanner scanner = new Scanner(System.in);

    try {

      System.out.print("Matrikelnummer: ");

      int matrNr = Integer.parseInt(scanner.nextLine());

      System.out.print("Semester: ");

      int semester = Integer.parseInt(scanner.nextLine());

      System.out.println("Matrikelnummer: " + matrNr);

      System.out.println("Semester: " + semester);

    } catch (NumberFormatException e) {

      System.out.println("Fehler: Bitte nur ganze Zahlen eingeben.");

    }

    scanner.close();

  }

}

29.3.5 Betrachtete Themen

  • Ausgabe einer Fehlermeldung
  • Ausnahmebehandlung (Exception Handling)
  • Einlesen von Benutzereingaben mit Scanner
  • Erkennen fehlerhafter Eingaben
  • Klasse NumberFormatException
  • Ressourcen mit scanner.close() schließen
  • try-catch-Block
  • Umwandlung von Zeichenketten in Ganzzahlen (Integer.parseInt())

29.3.6 Erläuterung

Nicht jede Benutzereingabe ist korrekt. Wird beispielsweise statt einer Zahl der Text abc eingegeben, kann Java diesen Text nicht in eine ganze Zahl umwandeln. Ohne besondere Maßnahmen würde das Programm an dieser Stelle mit einer Fehlermeldung abbrechen.

Deshalb wird der fehleranfällige Programmteil in einen try-Block geschrieben. Tritt dort ein Fehler auf, springt Java automatisch in den catch-Block. Dort kann eine verständliche Meldung ausgegeben werden, ohne dass das Programm unkontrolliert beendet wird.

try {

  ...

}

catch (NumberFormatException e) {

  ...

}

Die Methode

Integer.parseInt(...)

wandelt einen Text in eine ganze Zahl (int) um. Ist der Text keine gültige Zahl, erzeugt Java eine NumberFormatException.

Mit scanner.close(); wird der Scanner am Ende des Programms wieder geschlossen. Dadurch werden die verwendeten Systemressourcen ordnungsgemäß freigegeben.

29.4 Aufgabe 44 - Eigene Exception

Eigene Fehlermeldungen

29.4.1 Aufgabenstellung

Erstellen Sie eine eigene Exception-Klasse SemesterException. Entwickeln Sie anschließend eine Klasse Stud mit den Attributen

  • Matrikelnummer
  • Name
  • Semester

Das Semester wird beim Erzeugen eines Studierenden überprüft.

  • Ist das Semester kleiner oder gleich 15, soll das Objekt normal erzeugt werden.
  • Ist das Semester größer als 15, soll eine SemesterException ausgelöst werden.

Fangen Sie diese Exception im Hauptprogramm mit einem try-catch-Block ab und geben Sie die Fehlermeldung auf dem Bildschirm aus. Testen Sie das Programm zunächst mit einem gültigen Semester und anschließend mit einem ungültigen Wert (z. B. 18).

29.4.2 Lernziele

In dieser Aufgabe lernen Sie, wie in Java eigene Fehlermeldungen (Exceptions) definiert und verwendet werden. Sie erfahren, wie eine Klasse selbst Fehler erkennt und diese an das aufrufende Programm weitergibt. Außerdem üben Sie den Umgang mit throw, throws sowie try und catch.

29.4.3 Ergebnis Programmlauf

12345 Müller, Semester: 10

bzw.

Fehler: Semester darf nicht größer als 15 sein.

 

29.4.4 Lösung

package hochschule;

class SemesterException extends Exception {

  SemesterException(String meldung) {

  super(meldung);

  }

}

class Stud {

  int matrNr;

  String name;

  int semester;

  

  Stud(int matrNr, String name, int semester) throws SemesterException {

  if (semester > 15) {

    throw new SemesterException("Semester darf nicht größer als 15 sein.");

  }

  this.matrNr = matrNr;

  this.name = name;

  this.semester = semester;

  }

  void anzeigen() {

  System.out.println(matrNr + " " + name + ", Semester: " + semester);

  }

}

public class Aufgabe44 {

  public static void main(String[] args) {

  try {

    Stud s1 = new Stud(12345, "Müller", 10);

    s1.anzeigen();

  } catch (SemesterException e) {

    System.out.println("Fehler: " + e.getMessage());

  }

  }

}

29.4.5 Betrachtete Themen

  • Auslösen einer Exception mit throw
  • Eigene Exception-Klassen
  • Fehlerbehandlung mit try-catch
  • Fehlermeldungen mit getMessage()
  • Konstruktor einer Exception
  • Objektorientierte Fehlerbehandlung
  • Plausibilitätsprüfung von Eingabedaten
  • Validierung von Attributwerten
  • Vererbung von Exception
  • Weitergabe einer Exception mit throws

29.4.6 Erläuterung

Java besitzt zahlreiche eingebaute Exception-Klassen, beispielsweise für Dateifehler oder ungültige Zahleneingaben. In eigenen Programmen treten jedoch häufig Fehler auf, die nur für den jeweiligen Anwendungsbereich gelten. Dafür können eigene Exception-Klassen definiert werden.

Im vorliegenden Beispiel darf das Semester eines Studierenden höchstens 15 betragen. Diese Regel wird bereits im Konstruktor der Klasse Stud überprüft.

Die Klasse

class SemesterException extends Exception

legt eine neue Exception-Klasse fest. Da sie von Exception erbt, verhält sie sich wie jede andere Java-Exception. Wird ein unzulässiger Wert erkannt, wird die Exception mit throw new SemesterException(...); ausgelöst. Dadurch wird die normale Programmausführung sofort unterbrochen.

Da der Konstruktor eine Exception auslösen kann, muss dies in seiner Methodendeklaration angegeben werden:

throws SemesterException

Das Hauptprogramm erzeugt das Objekt innerhalb eines try-Blocks. Tritt die Exception auf, wird sie im zugehörigen catch-Block abgefangen. Die eigentliche Fehlermeldung wird mit e.getMessage() ausgegeben. Durch diese Vorgehensweise bleibt der Programmcode übersichtlich. Fehler werden dort behandelt, wo sie auftreten, und das Hauptprogramm kann angemessen auf die jeweilige Situation reagieren.

29.5 Aufgabe 45 - Generics

Generische Listen - Eingaben absichern

29.5.1 Aufgabenstellung

Erstellen Sie die Klassen Angestellter und Projekt sowie den Record PC. Legen Sie anschließend für jede dieser Klassen eine eigene generische Liste (ArrayList) an. Fügen Sie jeweils mehrere Objekte in die Listen ein. Geben Sie anschließend

  • alle Angestellten,
  • alle Projekte und
  • alle PCs

auf dem Bildschirm aus. Verwenden Sie dazu jeweils eine forEach-Schleife.

Erweitern Sie das Programm anschließend um weitere Angestellte, Projekte oder PCs und überprüfen Sie, ob diese automatisch mit ausgegeben werden.

29.5.2 Lernziele

In dieser Aufgabe lernen Sie, wie generische Listen (Generics) in Java verwendet werden. Sie erfahren, wie Objekte unterschiedlicher Klassen in typisierten ArrayList-Objekten gespeichert werden können. Dadurch wird die Typsicherheit erhöht und das Arbeiten mit Sammlungen vereinfacht.

29.5.3 Ergebnis Programmlauf

Angestellte:

Anna Müller

Bernd Schmidt

Projekte:

BPMN

Prozessoptimierung

PCs:

PC[invNr=1001, bez=Dell Optiplex, typ=Desktop]

PC[invNr=1002, bez=Lenovo ThinkPad, typ=Notebook]

 

29.5.4 Lösung

package firma;

import java.util.ArrayList;

import java.util.List;

class Angestellter {

  String name;

  String vorname;

  

  Angestellter(String name, String vorname) {

    this.name = name;

    this.vorname = vorname;

  }

  void anzeigen() {

    System.out.println(vorname + " " + name);

  }

}

class Projekt {

  String bez;

  

  Projekt(String bez) {

    this.bez = bez;

  }

  void anzeigen() {

    System.out.println(bez);

  }

}

record PC(int invNr, String bez, String typ) {

}

public class Aufgabe45 {

  public static void main(String[] args) {

    List<Angestellter> angestellte = new ArrayList<>();

    List<Projekt> projekte = new ArrayList<>();

    List<PC> pcs = new ArrayList<>();

    

    angestellte.add(new Angestellter("Müller", "Anna"));

    angestellte.add(new Angestellter("Schmidt", "Bernd"));

    

    projekte.add(new Projekt("BPMN"));

    projekte.add(new Projekt("Prozessoptimierung"));

    

    pcs.add(new PC(1001, "Dell Optiplex", "Desktop"));

    pcs.add(new PC(1002, "Lenovo ThinkPad", "Notebook"));

    

    System.out.println("Angestellte:");

    for (Angestellter a : angestellte) {

      a.anzeigen();

    }

    

    System.out.println("\nProjekte:");

    for (Projekt p : projekte) {

    p.anzeigen();

  }

  System.out.println("\nPCs:");

  for (PC pc : pcs) {

    System.out.println(pc);

    }

  }

}

29.5.5 Betrachtete Themen

  • Generics
  • Interface List
  • Iteration mit der forEach-Schleife
  • Klasse ArrayList
  • Methode add()
  • Objekte in Listen speichern
  • Objektlisten verwalten
  • Records in Collections
  • Typisierte Collections
  • Typparameter (<Angestellter>, <Projekt>, <PC>)
  • Typsicherheit zur Compilezeit
  • Vermeidung von Typumwandlungen (Casting)

29.5.6 Erläuterung

In vielen Programmen ist die Anzahl der Objekte nicht von Anfang an bekannt. Deshalb verwendet man häufig Collections, also Sammlungen von Objekten. Eine der wichtigsten Collection-Klassen in Java ist die ArrayList. Die Deklaration

List<Angestellter> angestellte = new ArrayList<>();

bedeutet:

  • List ist das allgemeine Java-Interface für Listen.
  • ArrayList ist eine konkrete Implementierung dieses Interfaces.
  • <Angestellter> legt fest, dass ausschließlich Objekte der Klasse Angestellter in dieser Liste gespeichert werden dürfen.

Diese Schreibweise bezeichnet man als Generics. Der Vorteil besteht darin, dass der Java-Compiler bereits während der Übersetzung überprüft, ob nur passende Objekte eingefügt werden. Fehler werden dadurch früh erkannt.

Neue Elemente werden mit

angestellte.add(...);

an die Liste angehängt. Zur Ausgabe eignet sich die forEach-Schleife:

for (Angestellter a : angestellte) {

  a.anzeigen();

}

Sie durchläuft automatisch alle Elemente der Liste, unabhängig davon, wie viele vorhanden sind. Im Beispiel werden drei unterschiedliche Listen verwendet:

  • eine Liste von Angestellten,
  • eine Liste von Projekten und
  • eine Liste von PCs.

Alle Listen verwenden dieselbe Klasse ArrayList, unterscheiden sich jedoch durch ihren Typparameter. Dadurch kann dieselbe Datenstruktur für beliebige Objektarten genutzt werden, ohne an Typsicherheit zu verlieren.

Generics gehören zu den wichtigen Sprachmitteln der Java-Programmierung. Sie werden in nahezu allen größeren Java-Anwendungen eingesetzt und bilden die Grundlage für den sicheren Umgang mit Collections.

 

30 Übungsaufgaben Teil 11

30.1 Aufgabe 46 - HashSet

Mengen mit HashSet

30.1.1 Aufgabenstellung

Erstellen Sie eine Klasse Projekt mit den Attributen

  • Projektbezeichnung
  • Standort

Legen Sie anschließend mehrere Projekte in einer ArrayList an. Verwenden Sie dabei einige Standorte mehrfach, beispielsweise mehrere Projekte in Hannover. Übertragen Sie anschließend alle Standorte in ein HashSet. Geben Sie danach den Inhalt des HashSet auf dem Bildschirm aus.

Vergleichen Sie die Anzahl der Projekte mit der Anzahl der ausgegebenen Standorte und erläutern Sie, warum einige Standorte nur einmal erscheinen.

30.1.2 Lernziele

In dieser Aufgabe lernen Sie die Collection-Klasse HashSet kennen. Sie erfahren, wie sich Werte ohne Duplikate speichern lassen und wie doppelt vorkommende Elemente automatisch entfernt werden.

30.1.3 Ergebnis Programmlauf

Standorte:

Bremen

Hannover

Berlin

 

30.1.4 Lösung

package firma;

import java.util.ArrayList;

import java.util.HashSet;

import java.util.List;

import java.util.Set;

class Projekt {

  String bez;

  String standort;

  

  Projekt(String bez, String standort) {

    this.bez = bez;

    this.standort = standort;

  }

}

public class Aufgabe46 {

  public static void main(String[] args) {

    List<Projekt> projekte = new ArrayList<>();

    Set<String> standorte = new HashSet<>();

    

    projekte.add(new Projekt("BPMN", "Hannover"));

    projekte.add(new Projekt("Prozessoptimierung", "Bremen"));

    projekte.add(new Projekt("Java-Schulung", "Hannover"));

    projekte.add(new Projekt("Datenmodellierung", "Berlin"));

    

    for (Projekt p : projekte) {

      standorte.add(p.standort);

    }

      System.out.println("Standorte:");

      for (String s : standorte) {

        System.out.println(s);

    }

  }

}

30.1.5 Betrachtete Themen

  • Automatische Vermeidung doppelter Einträge
  • Collection-Klasse HashSet
  • Durchlaufen einer Collection mit der forEach-Schleife
  • Eindeutige Werte aus einer Objektliste gewinnen
  • Einfügen mit add()
  • Generics
  • Interface Set
  • Mengen ohne Duplikate
  • Typisierte Collections
  • Unterschied zwischen List und Set

30.1.6 Erläuterung

Während eine Liste (List) Elemente in ihrer Einfügereihenfolge speichert und gleiche Werte mehrfach enthalten kann, arbeitet ein Set nach einem anderen Prinzip: Jeder Wert darf nur ein einziges Mal vorkommen. Die Deklaration

Set<String> standorte = new HashSet<>();

legt eine Menge von Zeichenketten an. Der Typparameter <String> bedeutet, dass nur Zeichenketten gespeichert werden dürfen. Anschließend werden die Standorte aller Projekte in das HashSet übernommen:

standorte.add(p.standort);

Befindet sich ein Standort bereits in der Menge, wird er nicht noch einmal eingefügt. Dadurch enthält das HashSet am Ende nur die unterschiedlichen Standorte.

Im Beispiel existieren mehrere Projekte. Zwei davon befinden sich in Hannover. Obwohl der Standort zweimal eingetragen wird, erscheint er bei der Ausgabe nur einmal.

Der Name HashSet weist darauf hin, dass intern eine Hashfunktion verwendet wird. Diese berechnet aus jedem gespeicherten Wert einen sogenannten Hashwert, der eine sehr schnelle Suche nach bereits vorhandenen Elementen ermöglicht. Dadurch kann das HashSet effizient feststellen, ob ein Wert schon gespeichert wurde.

Ein HashSet eignet sich immer dann, wenn nur eindeutige Werte benötigt werden, beispielsweise

  • Standorte von Projekten
  • Namen von Abteilungen
  • Benutzernamen
  • Postleitzahlen
  • Artikelnummern

 

30.2 Aufgabe 47 - Maps

Schlüssel-Wert-Paare mit HashMap

30.2.1 Aufgabenstellung

Erstellen Sie eine Klasse Stud mit den Attributen

  • Matrikelnummer
  • Name
  • Vorname

Legen Sie anschließend eine HashMap an, in der die Matrikelnummer als Schlüssel und das zugehörige Objekt Stud als Wert gespeichert wird. Fügen Sie mehrere Studierende in die Map ein. Suchen Sie anschließend einen Studierenden über seine Matrikelnummer und geben Sie dessen Daten aus.

Falls keine passende Matrikelnummer vorhanden ist, soll eine entsprechende Meldung erscheinen. Geben Sie zum Abschluss alle gespeicherten Matrikelnummern aus. Ändern Sie danach den Suchschlüssel und testen Sie sowohl einen erfolgreichen als auch einen erfolglosen Suchvorgang.

30.2.2 Lernziele

In dieser Aufgabe lernen Sie die Collection-Klasse HashMap kennen. Sie erfahren, wie Objekte über einen eindeutigen Schlüssel gespeichert und später schnell wiedergefunden werden können.

30.2.3 Ergebnis Programmlauf

Gefundener Studierender:

23456: Clara Meier

Alle Matrikelnummern:

23456

34567

12345

 

30.2.4 Lösung

package hochschule;

import java.util.HashMap;

import java.util.Map;

class Stud {

  int matrNr;

  String name;

  String vorname;

  

  Stud(int matrNr, String name, String vorname) {

    this.matrNr = matrNr;

    this.name = name;

    this.vorname = vorname;

  }

void anzeigen() {

    System.out.println(matrNr + ": " + vorname + " " + name);

  }

}

public class Aufgabe47 {

  public static void main(String[] args) {

    Map<Integer, Stud> studierende = new HashMap<>();

    studierende.put(12345, new Stud(12345, "Müller", "Anna"));

    studierende.put(23456, new Stud(23456, "Meier", "Clara"));

    studierende.put(34567, new Stud(34567, "Weber", "Paul"));

    int suchNr = 23456;

    Stud gefunden = studierende.get(suchNr);

    if (gefunden != null) {

      System.out.println("Gefundener Studierender:");

      gefunden.anzeigen();

    } else {

      System.out.println("Kein Studierender mit dieser Matrikelnummer gefunden.");

    }

      System.out.println("\nAlle Matrikelnummern:");

      for (Integer nr : studierende.keySet()) {

      System.out.println(nr);

    }

  }

}

30.2.5 Betrachtete Themen

  • Ausgabe der Schlüssel mit keySet()
  • Collection-Klasse HashMap
  • Effiziente Suche nach Objekten
  • Einfügen mit put()
  • Generics
  • Interface Map
  • Prüfung auf vorhandene Einträge
  • Rückgabe von null
  • Schlüssel (Key) und Wert (Value)
  • Schlüssel-Wert-Paare
  • Suchen mit get()
  • Typisierte Collections

30.2.6 Erläuterung

Während eine Liste (List) Objekte der Reihe nach speichert und ein Set eindeutige Werte verwaltet, speichert eine Map ihre Daten als Schlüssel-Wert-Paare. Die Deklaration Map<Integer, Stud> studierende = new HashMap<>(); bedeutet:

Vgl. Kapitel 32 (NoSQL-DBn)

  • Map ist das allgemeine Interface für Schlüssel-Wert-Sammlungen.
  • HashMap ist eine häufig verwendete Implementierung dieses Interfaces.
  • Integer ist der Typ des Schlüssels.
  • Stud ist der Typ des gespeicherten Objekts.

Im Beispiel dient die Matrikelnummer als Schlüssel. Da jede Matrikelnummer eindeutig ist, eignet sie sich besonders gut für eine Map. Neue Einträge werden mit

studierende.put(12345, new Stud(...));

angelegt. Der erste Parameter ist der Schlüssel, der zweite das zugehörige Objekt.

Ein großer Vorteil einer Map besteht darin, dass ein Objekt direkt über seinen Schlüssel gefunden werden kann:

Stud gefunden = studierende.get(suchNr);

Existiert der Schlüssel, liefert get() das gespeicherte Objekt zurück. Ist kein entsprechender Eintrag vorhanden, liefert die Methode den Wert null. Deshalb wird anschließend geprüft,

if (gefunden != null)

ob tatsächlich ein Objekt gefunden wurde. Mit

studierende.keySet()

werden alle gespeicherten Schlüssel geliefert. Diese können anschließend beispielsweise mit einer forEach-Schleife ausgegeben werden.

Maps werden überall dort eingesetzt, wo Daten über einen eindeutigen Schlüssel schnell gefunden werden sollen, beispielsweise bei

  • Matrikelnummern,
  • Personalnummern,
  • Kundennummern,
  • Artikelnummern oder
  • ISBN-Nummern von Büchern.

 

30.3 Aufgabe 48 - Streams

Filtern von Daten mit Streams

30.3.1 Aufgabenstellung

Erstellen Sie eine Klasse Angestellter mit den Attributen

  • Name
  • Vorname
  • Gehalt

Legen Sie mehrere Angestellte in einer ArrayList an. Geben Sie anschließend nur diejenigen Angestellten aus, deren Gehalt größer als 4.000 Euro ist. Verwenden Sie dazu einen Stream, der

  • die Liste durchläuft,
  • die gewünschten Angestellten auswählt und
  • diese anschließend ausgibt.

Ändern Sie anschließend die Gehälter oder ergänzen Sie weitere Angestellte und beobachten Sie, wie sich das Ergebnis verändert.

30.3.2 Lernziele

In dieser Aufgabe lernen Sie den Einsatz von Streams kennen. Sie erfahren, wie sich Daten aus einer Collection auswählen und anschließend weiterverarbeiten lassen. Dabei lernen Sie den Aufbau einer einfachen Stream-Verarbeitung mit stream(), filter() und forEach() kennen.

30.3.3 Ergebnis Programmlauf

Angestellte mit Gehalt über 4000 Euro:

Bernd Schmidt: 5200.0

Clara Weber: 4500.0

 

30.3.4 Lösung

package firma;

import java.util.ArrayList;

import java.util.List;

class Angestellter {

  String name;

  String vorname;

  double gehalt;

  Angestellter(String name, String vorname, double gehalt) {

    this.name = name;

    this.vorname = vorname;

    this.gehalt = gehalt;

  }

  

  void anzeigen() {

    System.out.println(vorname + " " + name + ": " + gehalt);

  }

}

public class Aufgabe48 {

  public static void main(String[] args) {

    List<Angestellter> angestellte = new ArrayList<>();

    

    angestellte.add(new Angestellter("Müller", "Anna", 3800.0));

    angestellte.add(new Angestellter("Schmidt", "Bernd", 5200.0));

    angestellte.add(new Angestellter("Weber", "Clara", 4500.0));

    

    System.out.println("Angestellte mit Gehalt über 4000 Euro:");

    angestellte.stream()

      .filter(a -> a.gehalt > 4000.0)

      .forEach(a -> a.anzeigen());

  }

}

30.3.5 Betrachtete Themen

  • Ausgabe mit forEach()
  • Filtern von Daten mit filter()
  • Lambda-Ausdrücke (erste Anwendung)
  • Methode stream()
  • Streams
  • Typisierte Collections (List, ArrayList)
  • Verarbeitung von Collections
  • Verarbeitungsketten (Method Chaining)
  • Vergleichsoperatoren

30.3.6 Erläuterung

Seit Java 8 können Sammlungen (Collections) mit Streams verarbeitet werden. Ein Stream stellt keine eigene Datensammlung dar. Er ermöglicht vielmehr, die Elemente einer vorhandenen Collection nacheinander zu bearbeiten. Die Verarbeitung beginnt mit

angestellte.stream()

Dadurch wird aus der Liste ein Stream erzeugt. Anschließend folgt

.filter(a -> a.gehalt > 4000.0)

Die Methode filter() überprüft jedes Objekt der Reihe nach. Nur diejenigen Angestellten, deren Gehalt größer als 4.000 Euro ist, werden an den nächsten Verarbeitungsschritt weitergegeben. Zum Schluss werden die ausgewählten Objekte ausgegeben:

.forEach(a -> a.anzeigen());

forEach() führt die angegebene Anweisung für jedes gefilterte Objekt aus.

Die drei Methoden bilden gemeinsam eine Verarbeitungskette: Liste -> stream() ->filter() -> forEach(). Dadurch bleibt der Programmcode kurz und gut lesbar.

Streams eignen sich nicht nur zum Filtern. Sie können beispielsweise auch zum

  • Sortieren,
  • Zählen,
  • Gruppieren,
  • Berechnen von Summen oder Durchschnittswerten sowie
  • Umwandeln von Daten

verwendet werden.

 

30.4 Aufgabe 49 - Lambda

Sortieren mit Lambda-Ausdrücken

30.4.1 Aufgabenstellung

Erstellen Sie eine Klasse Angestellter mit den Attributen

  • Name
  • Vorname
  • Gehalt

Legen Sie mehrere Angestellte in einer ArrayList an.

Sortieren Sie die Liste zunächst alphabetisch nach dem Nachnamen und geben Sie das Ergebnis aus. Sortieren Sie dieselbe Liste anschließend aufsteigend nach dem Gehalt und geben Sie die sortierte Liste erneut aus. Verwenden Sie für beide Sortierungen Lambda-Ausdrücke.

Erweitern Sie das Programm anschließend um weitere Angestellte und überprüfen Sie, ob die Sortierung weiterhin korrekt funktioniert.

30.4.2 Lernziele

In dieser Aufgabe lernen Sie Lambda-Ausdrücke kennen. Sie erfahren, wie sich Listen mit einer kurzen und übersichtlichen Schreibweise sortieren lassen. Dabei lernen Sie zwei verschiedene Sortierkriterien kennen: alphabetisch nach dem Namen und numerisch nach dem Gehalt.

30.4.3 Ergebnis Programmlauf

Sortiert nach Name:

Anna Müller: 3800.0

Bernd Schmidt: 5200.0

Jo Staud: 9000.0

Clara Weber: 4500.0

Sortiert nach Gehalt:

Anna Müller: 3800.0

Clara Weber: 4500.0

Bernd Schmidt: 5200.0

Guiseppe Staud: 9000.0

 

30.4.4 Lösung

package firma;

import java.util.ArrayList;

import java.util.List;

class Angestellter {

  String name;

  String vorname;

  double gehalt;

  

  Angestellter(String name, String vorname, double gehalt) {

    this.name = name;

    this.vorname = vorname;

    this.gehalt = gehalt;

  }

  

  void anzeigen() {

    System.out.println(vorname + " " + name + ": " + gehalt);

  }

}

public class Aufgabe49 {

  public static void main(String[] args) {

    List<Angestellter> angestellte = new ArrayList<>();

    

    angestellte.add(new Angestellter("Müller", "Anna", 3800.0));

    angestellte.add(new Angestellter("Schmidt", "Bernd", 5200.0));

    angestellte.add(new Angestellter("Weber", "Clara", 4500.0));

    angestellte.add(new Angestellter("Staud", "Jo", 9000.0));

    

    System.out.println("Sortiert nach Name:");

    

    angestellte.sort(

      (a1, a2) -> a1.name.compareTo(a2.name)

    );

    

    for (Angestellter a : angestellte) {

      a.anzeigen();

    }

    

    System.out.println("\nSortiert nach Gehalt:");

    

    angestellte.sort(

      (a1, a2) -> Double.compare(a1.gehalt, a2.gehalt)

    );

    for (Angestellter a : angestellte) {

      a.anzeigen();

    }

  }

}

30.4.5 Betrachtete Themen

  • forEach-Schleife
  • Lambda-Ausdrücke
  • Mehrfaches Sortieren derselben Liste
  • Methode sort()
  • Moderne Java-Syntax
  • Sortieren von Collections
  • Typisierte Collections (List, ArrayList)
  • Vergleich numerischer Werte mit Double.compare()
  • Vergleich von Zeichenketten mit compareTo()
  • Vergleichsfunktionen (Comparator)

30.4.6 Erläuterung

Listen können in Java mit der Methode sort() sortiert werden. Damit Java weiß, nach welchem Kriterium sortiert werden soll, muss eine Vergleichsfunktion angegeben werden. Im Beispiel geschieht dies mit einem Lambda-Ausdruck:

(a1, a2) -> a1.name.compareTo(a2.name)

Der Ausdruck bedeutet:

  • a1 und a2 sind zwei zu vergleichende Angestellte.
  • Es werden ihre Nachnamen miteinander verglichen.
  • compareTo() liefert einen Wert zurück, der angibt, welches Objekt im Alphabet zuerst stehen soll.

Dadurch wird die Liste alphabetisch nach den Nachnamen sortiert.

Für die Gehälter wird ein anderer Vergleich verwendet:

(a1, a2) -> Double.compare(a1.gehalt, a2.gehalt)

Double.compare() vergleicht zwei Gleitkommazahlen und liefert ebenfalls das für die Sortierung benötigte Ergebnis zurück. Dadurch werden die Angestellten nach ihrem Gehalt geordnet.

Ein Lambda-Ausdruck ist eine kompakte Schreibweise für eine kleine Funktion. Früher musste hierfür häufig eine eigene Klasse oder ein anonymes Objekt erstellt werden. Heute genügt meist eine einzige Zeile. Die allgemeine Form lautet:

(Parameter) -> Anweisung

oder bei mehreren Anweisungen

(Parameter) -> {

  ...

}

Lambda-Ausdrücke werden in Java unter anderem verwendet für

  • Sortierfunktionen,
  • Streams,
  • Filter,
  • Ereignisbehandlungen in grafischen Oberflächen und

 

30.5 Aufgabe 50 - Projektmitarbeit

Personal- und Projektverwaltung mit Collections

30.5.1 Aufgabenstellung

Erstellen Sie eine Klasse Angestellter mit den Attributen

  • Personalnummer
  • Name
  • Vorname
  • Gehalt

Überprüfen Sie im Konstruktor, ob das angegebene Gehalt gültig ist. Ein negatives Gehalt soll nicht akzeptiert werden. In diesem Fall soll eine IllegalArgumentException ausgelöst werden.

Erstellen Sie außerdem eine Klasse Projekt mit den Attributen

  • Projektnummer
  • Projektbezeichnung
  • Standort

Legen Sie anschließend mehrere Angestellte in einer ArrayList an. Übertragen Sie die Angestellten zusätzlich in eine HashMap. Verwenden Sie dabei die Personalnummer als Schlüssel und das zugehörige Angestelltenobjekt als Wert. Legen Sie außerdem mehrere Projekte in einer weiteren ArrayList an.

Das Programm soll anschließend folgende Auswertungen durchführen:

  • Ausgabe aller Angestellten.
  • Suchen eines Angestellten über seine Personalnummer in der HashMap.
  • Ausgabe aller Projekte.
  • Mit einem Stream Filtern aller Projekte, deren Bezeichnung das Wort "Java" enthält.
  • Ermitteln aller unterschiedlichen Projektstandorte mit einem HashSet.
  • Sortieren der Angestellten mit einem Lambda-Ausdruck, aufsteigend nach ihrem Gehalt.
  • Abfangen einer möglichen IllegalArgumentException mit einem try-catch-Block.

Testen Sie das Programm anschließend auch mit einem negativen Gehalt und beobachten Sie die Reaktion des Programms.

30.5.2 Ergebnis Programmlauf

Alle Angestellten:

1: Anna Müller, Gehalt: 3800.0

2: Bernd Schmidt, Gehalt: 5200.0

3: Clara Weber, Gehalt: 4500.0

Suche Angestellter mit Personalnummer 2:

2: Bernd Schmidt, Gehalt: 5200.0

Alle Projekte:

P100: BPMN (Hannover)

P200: Prozessoptimierung (Bremen)

P300: Java-Schulung (Hannover)

Projekte mit Java im Namen:

P300: Java-Schulung (Hannover)

Alle Standorte:

Bremen

Hannover

Angestellte sortiert nach Gehalt:

1: Anna Müller, Gehalt: 3800.0

3: Clara Weber, Gehalt: 4500.0

2: Bernd Schmidt, Gehalt: 5200.0

30.5.3 Lernziele

In dieser Aufgabe wenden Sie mehrere wichtige Konzepte der Java-Programmierung gemeinsam an. Sie wiederholen den Umgang mit Exceptions, generischen Listen, Maps, Sets, Streams und Lambda-Ausdrücken.

Ziel ist es, Daten zu Angestellten und Projekten in unterschiedlichen Collections zu speichern, zu durchsuchen, zu filtern, auszuwerten und zu sortieren.

 

30.5.4 Lösung

package firma;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.HashSet;

import java.util.List;

import java.util.Map;

import java.util.Set;

class Angestellter {

  int persNr;

  String name;

  String vorname;

  double gehalt;

  

  Angestellter(int persNr, String name, String vorname, double gehalt) {

    if (gehalt < 0) {

      throw new IllegalArgumentException("Gehalt darf nicht negativ sein.");

    }

    this.persNr = persNr;

    this.name = name;

    this.vorname = vorname;

    this.gehalt = gehalt;

  }

  void anzeigen() {

    System.out.println(persNr + ": " + vorname + " " + name

      + ", Gehalt: " + gehalt);

    }

  }

class Projekt {

  String projektNr;

  String bez;

  String standort;

  

  Projekt(String projektNr, String bez, String standort) {

    this.projektNr = projektNr;

    this.bez = bez;

    this.standort = standort;

  }

  void anzeigen() {

  System.out.println(projektNr + ": " + bez + " (" + standort + ")");

  }

}

public class Aufgabe50 {

  public static void main(String[] args) {

    try {

      List<Angestellter> personal = new ArrayList<>();

      personal.add(new Angestellter(1, "Müller", "Anna", 3800.0));

      personal.add(new Angestellter(2, "Schmidt", "Bernd", 5200.0));

      personal.add(new Angestellter(3, "Weber", "Clara", 4500.0));

      

      Map<Integer, Angestellter> personalMap = new HashMap<>();

      

      for (Angestellter a : personal) {

      personalMap.put(a.persNr, a);

      }

      

      List<Projekt> projekte = new ArrayList<>();

      

      projekte.add(new Projekt("P100", "BPMN", "Hannover"));

      projekte.add(new Projekt("P200", "Prozessoptimierung", "Bremen"));

      projekte.add(new Projekt("P300", "Java-Schulung", "Hannover"));

      

      System.out.println("Alle Angestellten:");

      for (Angestellter a : personal) {

      a.anzeigen();

      }

      

      System.out.println("\nSuche Angestellter mit Personalnummer 2:");

      Angestellter gefunden = personalMap.get(2);

      

      if (gefunden != null) {

      gefunden.anzeigen();

      }

      

      System.out.println("\nAlle Projekte:");

      for (Projekt p : projekte) {

      p.anzeigen();

      }

      

      System.out.println("\nProjekte mit Java im Namen:");

      projekte.stream()

        .filter(p -> p.bez.contains("Java"))

        .forEach(p -> p.anzeigen());

      Set<String> standorte = new HashSet<>();

      

      for (Projekt p : projekte) {

        standorte.add(p.standort);

      }

      System.out.println("\nAlle Standorte:");

      for (String s : standorte) {

        System.out.println(s);

      }

      System.out.println("\nAngestellte sortiert nach Gehalt:");

      personal.sort(

        (a1, a2) -> Double.compare(a1.gehalt, a2.gehalt)

      );

      for (Angestellter a : personal) {

        a.anzeigen();

      }

    } catch (IllegalArgumentException e) {

      System.out.println("Fehler: " + e.getMessage());

    }

  }

}

30.5.5 Betrachtete Themen

  • Auslösen einer IllegalArgumentException
  • Ausnahmebehandlung mit try-catch
  • Filtern mit filter()
  • forEach-Schleifen
  • Generics
  • Interface List
  • Interface Map
  • Interface Set
  • Klasse ArrayList
  • Klasse HashMap
  • Klasse HashSet
  • Klassen und Objekte
  • Konstruktoren
  • Lambda-Ausdrücke
  • Method Chaining
  • Numerischer Vergleich mit Double.compare()
  • Plausibilitätsprüfung von Attributwerten
  • Schlüssel-Wert-Paare
  • Sortieren mit sort()
  • Streams
  • Suchen mit get()
  • Typisierte Collections
  • Verarbeitung mit forEach()
  • Vermeidung doppelter Werte

30.5.6 Erläuterung

Diese Aufgabe verbindet mehrere Konzepte, die in den vorherigen Aufgaben einzeln behandelt wurden. Dadurch entsteht eine kleine Personal- und Projektverwaltung.

Prüfung des Gehalts

Im Konstruktor der Klasse Angestellter wird geprüft, ob das Gehalt negativ ist:

if (gehalt < 0) {

  throw new IllegalArgumentException(

    "Gehalt darf nicht negativ sein."

  );

}

Ein negatives Gehalt wird als ungültiger Wert betrachtet. Deshalb wird eine IllegalArgumentException ausgelöst. Bei dieser Exception handelt es sich um eine bereits in Java vorhandene Exception-Klasse. Es muss daher keine eigene Exception-Klasse erstellt werden. Die Exception wird im Hauptprogramm abgefangen:

try {

Programmanweisungen

  } catch (IllegalArgumentException e) {

    System.out.println("Fehler: " + e.getMessage());

  }

Dadurch kann das Programm auf den Fehler reagieren und eine verständliche Meldung ausgeben.

Speicherung in einer Liste

Die Angestellten werden zunächst in einer ArrayList gespeichert:

List<Angestellter> personal = new ArrayList<>();

Eine Liste eignet sich, wenn mehrere Objekte nacheinander gespeichert und vollständig durchlaufen werden sollen.

Neue Angestellte werden mit add() eingefügt:

personal.add(

new Angestellter(1, "Müller", "Anna", 3800.0)

);

Speicherung in einer Map

Zusätzlich werden die Angestellten in einer HashMap gespeichert:

Map<Integer, Angestellter> personalMap = new HashMap<>();

Die Personalnummer dient dabei als Schlüssel. Das Angestelltenobjekt bildet den zugehörigen Wert. Die Übertragung aus der Liste in die Map erfolgt mit einer forEach-Schleife:

for (Angestellter a : personal) {personalMap.put(a.persNr, a);}

Danach kann ein Angestellter direkt über seine Personalnummer gesucht werden:

Angestellter gefunden = personalMap.get(2);

Wird kein passender Schlüssel gefunden, liefert get() den Wert null. Deshalb sollte das Ergebnis geprüft werden:

if (gefunden != null) {

  gefunden.anzeigen();

}

Filtern mit einem Stream

Die Projektliste wird mit einem Stream verarbeitet:

projekte.stream()

  .filter(p -> p.bez.contains("Java"))

  .forEach(p -> p.anzeigen());

Die Verarbeitung besteht aus drei Schritten: Projektliste -> -> stream() -> filter() -> forEach().

  • stream() erzeugt einen Stream aus der Liste.
  • filter() lässt nur diejenigen Projekte passieren, deren Bezeichnung das Wort „Java“ enthält.
  • forEach() gibt die ausgewählten Projekte aus.

Der Ausdruck

p -> p.bez.contains("Java")

ist ein Lambda-Ausdruck. p steht jeweils für ein Projekt. Das Ergebnis des Ausdrucks ist entweder true oder false.

Eindeutige Standorte mit einem Set

Für die Projektstandorte wird ein HashSet verwendet:

Set<String> standorte = new HashSet<>();

Alle Projektstandorte werden in diese Menge eingefügt:

for (Projekt p : projekte) {

  standorte.add(p.standort);

}

Ein Set speichert jeden Wert höchstens einmal. Da zwei Projekte den Standort Hannover besitzen, wird Hannover bei der Ausgabe trotzdem nur einmal angezeigt.

Sortieren mit einem Lambda-Ausdruck

Die Liste der Angestellten wird nach dem Gehalt sortiert:

personal.sort(

  (a1, a2) -> Double.compare(a1.gehalt, a2.gehalt)

);

Jeweils zwei Angestellte werden miteinander verglichen. Double.compare() prüft die beiden Gehälter und bestimmt daraus ihre Reihenfolge. Die Sortierung erfolgt aufsteigend. Der Angestellte mit dem niedrigsten Gehalt steht anschließend am Anfang der Liste.

Zusammenspiel der Collections

Die Aufgabe zeigt, dass unterschiedliche Collections für unterschiedliche Zwecke eingesetzt werden:

  • Eine List speichert mehrere Objekte in einer bestimmten Reihenfolge.
  • Eine Map ermöglicht den direkten Zugriff über einen Schlüssel.
  • Ein Set speichert nur unterschiedliche Werte.
  • Ein Stream verarbeitet und filtert die Elemente einer Collection.
  • Ein Lambda-Ausdruck beschreibt kurz, wie gefiltert oder sortiert werden soll.

Damit fasst Aufgabe 50 wesentliche Inhalte der Aufgaben 43 bis 49 in einem gemeinsamen Programm zusammen.

31 Exkurs - Zusammenhänge

Wie ist der Gesamtzusammenhang zwischen Listen, Array-Listen, Link-Arrays, usw.?

31.1 Die Grundidee

Mehrere Objekte verwalten

Angenommen, Sie möchten mehrere Studierende verwalten:

Stud1

Stud2

Stud3

Stud4

...

Dafür gibt es verschiedene Techniken.

31.2 Arrays

Die einfachste Lösung ist ein Array:

Stud[] gruppe = new Stud[100];

Hier wird Platz für 100 Referenzen reserviert.

Vorteile:

  • einfach
  • schneller Zugriff über Index: gruppe[2]

Nachteile:

  • Größe ist fest
  • Einfügen und Löschen aufwendig

31.3 Verkettete Listen

Hier kennt jedes Objekt seinen Nachfolger:

class Stud {

    String name;

    Stud naechster;

}

Beispiel:

s1 -> s2 -> s3 -> s4 -> null

Vorteile:

  • beliebig erweiterbar
  • Einfügen und Löschen einfach

Nachteile:

  • kein direkter Zugriff auf das 3. oder 10. Element
  • man muss die Liste durchlaufen

31.4 ArrayList

Die Klasse ArrayList kombiniert die Vorteile von Arrays und Listen.

List<Stud> gruppe = new ArrayList<>();

Intern arbeitet sie mit einem Array, vergrößert dieses aber automatisch.

gruppe.add(s1);

gruppe.add(s2);

Vorteile:

  • Größe flexibel
  • einfache Programmierung
  • direkter Zugriff mit gruppe.get(2)

Deshalb wird sie heute in Java am häufigsten verwendet.

31.5 LinkedList

Java besitzt zusätzlich die Klasse LinkedList.

List<Stud> gruppe = new LinkedList<>();

Intern ist sie eine verkettete Liste.

s1 <-> s2 <-> s3 <-> s4

Für Einsteiger ist sie interessant, weil sie dem theoretischen Listenkonzept entspricht.

31.6 Link-Arrays

Hier kommt eine andere Ebene ins Spiel.

Link-Arrays bei Beziehungen

Nehmen wir eine n:m-Beziehung:

Angestellter ---- Projekt

Ein Angestellter arbeitet in mehreren Projekten. Ein Projekt hat mehrere Angestellte. Dann speichert man oft Referenzen:

class Angestellter {

    List<Projekt> projekte;

}

oder einfacher:

class Angestellter {

    Projekt[] projekte;

}

Dieses Array enthält Links zu Projekten. Deshalb sprechen manche Lehrbücher von einem Link-Array.

Angestellter Meier

Das ist also kein eigener Datentyp, sondern lediglich ein Array, dessen Elemente Referenzen auf andere Objekte enthalten.

31.7 Zusammenfassung

 

Link-Arrays sind ein Modellierungsmittel für Beziehungen zwischen Objekten, während Listen und ArrayLists allgemeine Datenstrukturen zur Verwaltung beliebig vieler Objekte sind.

32 Exkurs - Sammlungen (collection)

In Java bezeichnet man als Sammlung (engl. Collection) eine Datenstruktur, die mehrere Objekte gemeinsam verwaltet. Statt viele einzelne Variablen anzulegen,

Stud s1;

Stud s2;

Stud s3;

kann man die Objekte in einer Sammlung zusammenfassen:

List<Stud> studierende = new ArrayList<>();

Die Sammlung enthält dann beliebig viele Objekte des Typs Stud.

32.1 Warum braucht man Sammlungen?

Angenommen, eine Hochschule verwaltet Studierende. Mit einzelnen Variablen:

Stud s1 = new Stud();

Stud s2 = new Stud();

Stud s3 = new Stud();

ist die Anzahl fest vorgegeben. Mit einer Sammlung:

List<Stud> studierende = new ArrayList<>();

studierende.add(s1);

studierende.add(s2);

studierende.add(s3);

können jederzeit weitere Studierende hinzugefügt werden.

32.2 Wichtige Sammlungen in Java

Die bekanntesten Sammlungen stammen aus dem Paket

import java.util.*;

32.2.1 List

Speichert Elemente in einer Reihenfolge.

List<String> namen = new ArrayList<>();

namen.add("Anna");

namen.add("Ben");

namen.add("Clara");

Ausgabe:

Anna

Ben

Clara

32.2.2 Set

Speichert Elemente ohne Duplikate.

Set<String> namen = new HashSet<>();

namen.add("Anna");

namen.add("Anna");

namen.add("Ben");

Das zweite "Anna" wird nicht übernommen.

32.2.3 Map

Speichert Schlüssel-Wert-Paare.

Map<Integer, String> studenten = new HashMap<>();

studenten.put(1001, "Maier");

studenten.put(1002, "Schulz");

Hier dient die Matrikelnummer als Schlüssel.

32.3 Sammlungen und Arrays

Ein Array ist keine Collection, erfüllt aber einen ähnlichen Zweck.

Array:

Stud[] gruppe = new Stud[3];

Vorteil:

  • einfach
  • gut für Einsteiger

Nachteil:

  • Größe ist fest

Collection:

List<Stud> gruppe = new ArrayList<>();

Vorteil:

  • wächst automatisch
  • bietet viele Hilfsmethoden

32.4 Zusammenhang mit obigen

In den bisherigen Aufgaben kamen Sammlungen bereits vor:

List<Note> noten = new ArrayList<>();

Diese Sammlung speichert alle Noten eines Studierenden. Ebenso bei der n:m-Beziehung:

List<Projekt> projekte = new ArrayList<>();

Ein Angestellter kann dadurch beliebig viele Projekte besitzen. Oder bei der Assoziationsklasse:

List<Mitarbeit> mitarbeitListe = new ArrayList<>();

Hier enthält die Sammlung alle vorhandenen Beziehungen zwischen Angestellten und Projekten.

Für den Einstieg ist es sinnvoll, sich eine Sammlung einfach als einen Container für viele Objekte desselben Typs vorstellen. Sie entspricht dem UML-Konzept eines mehrwertigen Attributs (Multiplizität 0..* oder 1..*), wird in Java aber meist durch Klassen wie ArrayList realisiert.

33 Exkurs - NoSQL?

Die Schlüssel-Wert-Paare von Java erinnern an die NoSQL-Key-Value - Datenbanken, deshalb hier eine kurze Betrachtung.

33.1 Vergleich

Sie beruhen auf demselben Grundprinzip, sind aber nicht identisch. Die Java-Klasse

Map<Integer, String> studenten = new HashMap<>();

und eine Key-Value-Datenbank wie z. B. Redis oder Amazon DynamoDB speichern beide Daten als Schlüssel-Wert-Paare:

1001 -> Maier

1002 -> Schulz

1003 -> Weber

Der Unterschied liegt vor allem darin, wo und wie die Daten gespeichert werden.

 


Java Map

Key-Value-Datenbank

Liegt im Arbeitsspeicher eines Programms

Liegt dauerhaft in einer Datenbank

Existiert nur während der Programmausführung

Bleibt nach dem Programmende erhalten

Wird von einem einzelnen Programm genutzt

Kann von vielen Programmen gleichzeitig genutzt werden

Sehr schnell innerhalb des Programms

Netzwerkzugriff möglich

Keine Datensicherung

Dauerhafte Speicherung


Vergleich

Java:

Map<Integer, String> studenten = new HashMap<>();

studenten.put(1001, "Maier");

studenten.put(1002, "Schulz");

NoSQL-Key-Value-Datenbank:

Key: 1001

Value: Maier

Key: 1002

Value: Schulz

Das Konzept ist also praktisch identisch.

33.2 Objekte als Werte

In Java speichert man häufig ganze Objekte:

Map<Integer, Stud> studenten = new HashMap<>();

Dann enthält die Map:

1001 -> Stud-Objekt von Maier

1002 -> Stud-Objekt von Schulz

Viele moderne Key-Value-Datenbanken machen genau dasselbe. Der Value ist dann nicht nur ein String, sondern ein komplexes Objekt, oft im JSON-Format:

{

  "matrNr": 1001,

  "name": "Maier",

  "vorname": "Paul"

}

33.3 Historischer Zusammenhang

Didaktisch kann man sagen:

 

Die NoSQL-Key-Value-Datenbanken übertragen das aus Java bekannte Map-Prinzip auf eine dauerhaft gespeicherte, verteilte Datenbank. Eine Key-Value-Datenbank kann man sich zunächst wie eine riesige, dauerhaft gespeicherte Map vorstellen. Die tatsächlichen Systeme bieten zusätzlich Datensicherung, Parallelzugriffe, Verteilung auf mehrere Rechner und oft sehr große Datenmengen.

34 Themenindex

A

Abstrakte Klassen ................ Aufgabe 36

Abstrakte Methoden ............... Aufgabe 36

Accessor-Methoden ................ Aufgabe 41

add()-Methode .................... Aufgaben 20, 27, 28

addKurs() ........................ Aufgabe 20

addMitarbeiter() ................. Aufgaben 28, 30

addProjekt() ..................... Aufgaben 27, 28

addStudent() ..................... Aufgabe 20

addUntergebener() ................ Aufgabe 29

Adresse .......................... Aufgaben 19, 19b

Aggregation (UML) ................ Kapitel UML

Aggregation von Daten ............ Aufgabe 30

Aggregationsfunktion ............. Aufgabe 30

Angestellter ..................... Aufgaben 23 ... 41

Anonyme Objekte .................. Aufgabe 3

Array ............................ Aufgaben 1 ... 3

ArrayList ........................ Aufgaben 20, 22, 27...30

Assoziation ...................... Aufgaben 4a...4e, 19...30

Assoziationsklasse ............... Aufgabe 4e

Attribute ........................ Aufgaben 1...41

Auswertungsmethoden .............. Aufgabe 30

B

Basisklasse ...................... Aufgaben 7, 33...40

Beziehungen zwischen Klassen ..... Aufgaben 4, 19...30

Bidirektionale Beziehung ......... Aufgaben 4c, 19b, 28

Bidirektionale n:m-Beziehung ..... Aufgabe 28

Body eines Records ............... Aufgabe 41

Boolean .......................... Aufgabe 9

BufferedReader ................... HS Daten 19.5

BufferedWriter ................... HS Daten 19.3

C

CSV-Datei ....................... HS Daten 19.8

contains() ...................... Aufgabe 28

Collection ...................... siehe List

Compiler ........................ HS-Projekte

D

Dateien ......................... HS Daten Kapitel 19

Datei erzeugen .................. HS Daten 19.1

Datei lesen ..................... HS Daten 19.4...19.8

Datei schreiben ................. HS Daten 19.2, 19.3

Dateipfad ....................... HS Daten

Datenaggregation ................ Aufgabe 30

Datenklasse ..................... Aufgabe 41

Datenkapselung .................. Aufgabe 8, HS8

Datensatz ....................... Aufgabe 41

double .......................... Aufgaben 30, 36

Druckbar (Interface) ............ Aufgabe 37

Durchlaufen einer Liste ......... Aufgaben 20, 22, 27...30

E

Eclipse ..........................HS-Projekte, hsdaten

Einlesen von Dateien ............ HS Daten 19.4...19.8

equals() ........................ Aufgabe 6, Aufgabe 41

equals()-Methode ................ Aufgaben 6, 41

Exception ....................... HS Daten 19

Exception-Behandlung ............ HS Daten 19

extends ......................... Aufgaben 7, 33

F

Fachattribute ................... Aufgaben 4, 23...41

Datei (File) .................... HS Daten 19.1

File-Klasse ..................... HS Daten 19.1

FileNotFoundException ........... HS Daten 19.4, 19.8

FileReader ...................... HS Daten 19.4, 19.8

FileWriter ...................... HS Daten 19.2, 19.3

foreach-Schleife ................ Aufgaben 20, 22, 27...30

G

Gehaltsauswertung ............... Aufgabe 30

Gehaltssumme .................... Aufgabe 30

Generalisierung ................. Aufgabe 33

Getter .......................... Aufgabe 8

Getter-Methoden ... ............. Aufgabe 8

GUI ............................. Aufgaben 22, 40a

H

hashCode() ...................... Aufgabe 6, Aufgabe 41

Hierarchie ...................... Aufgabe 29

Hierarchische Beziehungen ....... Aufgabe 29

Hochschulbeispiel ............... Aufgaben 1...22

hsdaten ......................... Kapitel 19

IOException ..................... HS Daten 19

Immutable Objects ............... Aufgabe 41

Inheritance (engl.) ............. siehe Vererbung

Instanz ......................... Aufgaben 1...41

Interface ....................... Aufgaben 37, 38

Interfaces ...................... Aufgaben 37, 38

Inventarnummer .................. Aufgabe 41

is-a-Beziehung .................. Aufgabe 33

I

Immutable Objects ............... Aufgabe 41

Instanz ......................... Aufgaben 1...41

Interface ....................... Aufgaben 37, 38

Interfaces ...................... Aufgaben 37, 38

Inventarnummer .................. Aufgabe 41

is-a-Beziehung .................. Aufgabe 33

instanceof ...................... Aufgabe 40

J

JOptionPane ..................... Aufgaben 22, 40a

K

Klassendiagramm ................. Aufgaben 21, 23

Klassenattribute ................ Grundlagen

Klassenbeziehungen .............. Aufgaben 4, 19...30

Komposition ..................... UML-Kapitel

Konstruktor ..................... Aufgaben 2, 7, 11, 19...41

Konstruktorüberladung ........... Aufgaben 9, 35

Konsistenz von Beziehungen ...... Aufgabe 28

Konsolenausgabe ................. zahlreiche Aufgaben

Konstruktor eines Records ....... Aufgabe 41

Kurs ............................ Aufgabe 20

L

Lesen von Dateien ............... HS Daten 19.4...19.8

LinkedList ...................... Aufgabe 22

List ............................ Aufgaben 20, 22, 27...30

List ............................ Aufgaben 4, 19

List ............................ Aufgaben 4a...4c

Lokale Variablen ................ Aufgaben 5, 30

M

Matrikelnummer .................. Aufgaben 1...22

Mehrfachvererbung (über Interf.). Aufgabe 38

Mehrere Interfaces .............. Aufgabe 38

Mehrwertige Attribute ........... Aufgaben 4, 20, 27, 28

Methoden überladen .............. Aufgaben 9, 35

Methoden überschreiben .......... Aufgaben 7, 34

Methodensignatur ................ Aufgaben 9, 35

Mitarbeiter ..................... Aufgaben 23...32

Modellierung .................... Aufgaben 21...30

Modellierung von Beziehungen .... Aufgaben 4, 19...30

module-info.java ................ HS-Projekte

n:m-Beziehung ................... Aufgaben 4d, 4e, 20, 21, 28

null ............................ Aufgabe 29

null-Prüfung .................... Aufgabe 29

Numerische Attribute ............ Aufgabe 30

O

Objekterzeugung ................. Aufgaben 1...41

Objekthierarchie ................ Aufgabe 29

Objektlisten .................... Aufgaben 20, 22, 27...30

Objektreferenz .................. Aufgaben 4...41

Objektverknüpfung ............... Aufgaben 4...30

Objektzustand ................... Aufgaben 8, 30

Superklasse ..................... Aufgaben 7, 33...40

Overloading (Überladen) ......... Aufgaben 9, 35

Overriding (Überschreiben) ...... Aufgaben 7, 34

P

PC .............................. Aufgaben 23...41

Polymorphie ..................... Aufgaben 39, 40

private ......................... Aufgabe 8, HS8

Projekt ......................... Aufgaben 23...32

Projektzuordnung ................ Aufgaben 27, 28

Prüfung auf null ................ Aufgabe 29

R

Reader .......................... HS Daten 19

Record .......................... Aufgabe 41

Records ......................... Aufgabe 41

Rekursive Beziehung............. Aufgabe 29

Rückgabewert .................... Aufgaben 30, 36

S

Scanner ......................... Aufgaben 5, HS Daten 19.4, 19.8

Schreiben von Dateien ........... HS Daten 19.2, 19.3

Selbstbeziehung ................. Aufgabe 29

Setter .......................... Aufgabe 8

Setter-Methoden ................. Aufgabe 8

Spezialisierung ................. Aufgabe 33

Standardkonstruktor ............. HS8

Standort ........................ Aufgaben 23...32

Student ......................... Aufgaben 1...22

Stud ............................ HS-Projekte

super ........................... Aufgaben 7, 33

Summenbildung ................... Aufgabe 30

Swing ........................... Aufgaben 22, 40a

T

this ............................ Aufgaben 28, 29

this-Referenz ................... Aufgaben 28, 29

toString() ...................... Aufgaben 6, 41

Typumwandlung ................... Aufgabe 40

U

Überladen ....................... Aufgaben 9, 35

Überschreiben ................... Aufgaben 7, 34

UML ............................. Aufgaben 21...41

UML-Klassendiagramm ............. Aufgaben 21, 23

Unidirektionale Beziehung ....... Aufgaben 4a, 4b, 19

Unveränderliche Datenobjekte .... Aufgabe 41

Untergebene ..................... Aufgabe 29

V

Vererbung ....................... Aufgaben 7, 33...40

Verkettete Liste ................ Aufgabe 22

Verknüpfung von Objekten ........ Aufgaben 4...30

Verwendung mehrerer Klassen ..... Aufgaben 4...41

Vorgesetzter .................... Aufgabe 29

W

while-Schleife .................. Aufgabe 22

Writer .......................... HS Daten 19

Z

Zugriff auf Objektattribute ..... zahlreiche Aufgaben

Zugriffsmethoden ................ Aufgaben 8, 41

Zusammenarbeit

  mehrerer Klassen .............. Aufgaben 4...41

35 Literatur

H

Habelitz 2022
Habelitz, Hans-Peter: Programmieren lernen mit Java. (7. Auflage). Print-Ausgabe. Bonn 2022 (Rheinwerk).

Habelitz 2025
Habelitz, Hans-Peter: Programmieren lernen mit Java. (8. Auflage). E-Book. Bonn 2025 (Rheinwerk)

Kofler 2022
Kofler, Michael: Java. Der Grundkurs. Bonn 2022 (Rheinwerk)

Pepper 2007
Pepper, Peter: Programmieren lernen. Eine grundlegende Einführung mit Java (3. Auflage), Berlin und Heidelberg 2007 (Springer-Verlag)

Ratz et al. 2018
Ratz, Dietmar; Schulmeister-Zimolong, Dennis; Seese, Detlef; Wiesenberger, Jan: Grundkurs Programmieren in Java (8. Auflage). München 2018

Staud

Staud 2019
Staud, Josef Ludwig: Unternehmensmodellierung - Objektorientierte Theorie und Praxis mit UML 2.5. (2. Auflage). Berlin u.a. 2019 (Springer Gabler)

Staud 2021
Staud, Josef Ludwig: Relationale Datenbanken. Grundlagen, Modellierung, Speicherung, Alternativen. 2. Auflage 2021. Verlag tredition GmbH,
Paperback: 978-3-347-35883-6
Hardcover: 978-3-347-35884-3
E-Book: 978-3-347-35885-0

Staud 2024
Staud, Josef Ludwig: Geschäftsprozesse und ihre Modellierung mit der Methode Business Procass Model and Notation (BPMN 2.0). (2. Auflage). Hamburg 2024 (tredition)
Als Hardcover, Softcover, E-Book

Staud 2025
Staud, Josef Ludwig: Ereignisgesteuerte Prozessketten. Das Werkzeug für die Modellierung von Geschäftsprozessen. (2. Auflage), Hamburg 2025 (tredition)

Staud 2026a
Staud, Josef Ludwig: Objektorientierte Modellierung mit der UML 2.5. Mit Java-Beispielen (Version 2026/7). Web-Text: https://www.staud.info/leitOO.php

Staud 2026b
Staud, Josef Ludwig: Java-Programme. Lern- und Lesehilfe für die Java-Programme in [Staud 2026a] (Version 2026/7). Web-Text: https://www.staud.info/leitOO.php

Steppan 2020
Steppan, Berhard: Einstieg in Java mit Eclipse. München 2020 (Hanser)

U

Ullenboom 2022
Ullenboom, Christian: Java ist auch eine Insel. Einführung, Ausbildung, Praxis. (16. Auflage). Bonn 2022 (Rheinwerk-Verlag)