W4DSKI-103 · Grundlagen Programmierung
Dozent: Pfob, Mario
Java Duke

Organisatorisches

Links zu Vorlesungsmaterialien (Skript, Folien, Übungen) im Moodle-Kurs

Über mich

LinkedIn Website

Einleitung

Warum Programmieren?

  • Breites Jobspektrum
  • Fördert logisches Denken

Warum Java?

  • Plattformunabhängigkeit
  • Community und umfangreiches Ökosystem
  • Verlässlichkeit und Skalierbarkeit

Java & Python

  • Werden nebeneinander eingesetzt
  • Java: Stabilität → Software Development
  • Python: Einfachheit → Data Science

Umfrage JetBrains

Developer Ecosystem Report 2024

Innovation Graph GitHub

Programming Languages Globally

State of Java 2025

Moodle-Link
Lizenz-Kosten

Einführung in die Programmierung

Aspekte des Programmierens

Programmiersprachen

Quellcode

Der Quellcode ist die in einer Programmiersprache geschriebene textuelle Repräsentation eines Programms. Entwickler nutzen den Quellcode, um Algorithmen und logische Anweisungen zu formulieren, die vom Computer interpretiert und ausgeführt werden. Der Quellcode enthält die gesamte Logik der Anwendung und dient als Basis für die Kompilation oder Interpretation durch das System. Gut strukturierter und verständlicher Quellcode ist essenziell, nicht nur für die aktuelle Entwicklung, sondern auch für zukünftige Wartung und Erweiterung des Programms. Dies erfordert klare Namenskonventionen, konsistente Formatierung und Kommentierung.

Programmierstil

Der Programmierstil bezieht sich auf die Art und Weise, wie der Quellcode organisiert und geschrieben wird. Ein guter Programmierstil zielt darauf ab, die Lesbarkeit & Wartbarkeit des Codes zu verbessern. Dazu gehören der sinnvolle Einsatz von Einrückungen, eine klare und konsistente Benennung von Variablen und Funktionen sowie der Verzicht auf unnötige Komplexität. Ein konsistenter Programmierstil hilft dabei, Fehler zu vermeiden, und macht es anderen Entwicklern einfacher, den Code zu verstehen und daran weiterzuarbeiten. Der Stil variiert oft je nach Programmiersprache und Entwicklerumgebung, doch existieren allgemeine Richtlinien, die für alle Sprachen gelten.

Coding Conventions

Coding Conventions sind standardisierte Regeln für das Schreiben von Quellcode, welche die Lesbarkeit unabhängig vom individuellen Programmierstil verbessern. Sie erleichtern damit die Zusammenarbeit im Team und vereinfachen langfristig die Wartung des Codes. Wichtige Aspekte:

  • Namenskonventionen: Regeln für die Benennung von Variablen, Klassen, Funktionen und Dateien.
  • Formatierung: Einheitliche Einrückungen, Leerzeilen, Kommentarstile und maximale Zeilenlänge.
  • Programmierlogik: Standardisierter Einsatz von Kontrollstrukturen wie Schleifen & Bedingungen.

Java Coding Conventions

Kompilation & Ausführung

Compile Time Error vs. Runtime Error

Kompilation & Ausführung

  • Ein Programm kann durch Kompilation oder Interpretation ausgeführt werden.
  • Compiler: Übersetzt Quellcode vollständig in Maschinencode oder Bytecode (Java), wodurch das Programm wiederholt ohne erneute Übersetzung ausgeführt werden kann.
  • Interpreter: Übersetzt Quellcode Zeile für Zeile in Maschinencode und führt ihn direkt aus. Wird von Sprachen wie Python genutzt und bietet schnellere Tests, jedoch langsamere Ausführung.
  • VM: Die JVM ist ein Beispiel für eine VM, die plattformunabhängige Ausführung von Bytecode ermöglicht und die WORA-Fähigkeit von Java unterstützt.

A Deep Dive into JVM Start-Up

Kompilation & Ausführung

Java Compiler

Kompilation & Ausführung

Programming Language Levels

Exkurs: Python JIT


                
                

"Normaler" Python Code

Exkurs: Python JIT


                
                

CPython-Version


                
                

Console output (MacBook M4 Pro)


                
                

PyPy-Version


                
                

Console output (MacBook M4 Pro)

Exkurs: Python JIT

GitHub-Repo

Testen

Testen ist ein Bestandteil der Softwareentwicklung, der sicherstellt, dass Programme erwartungsgemäß funktionieren und Fehler frühzeitig erkannt werden. Kontinuierliches Testen verbessert die Softwarequalität und erhöht die Stabilität. Testarten (Auswahl):

  • Unit-Tests: Überprüfen isolierte Codeeinheiten wie Methoden oder Funktionen.
  • Integrationstests: Testen das Zusammenspiel verschiedener Systemkomponenten.
  • Systemtests: Validieren das gesamte System hinsichtlich (nicht-)funktionaler Anforderungen.

Automatisierung: Frameworks wie JUnit erleichtern das Schreiben & Automatisieren von Tests.

Debugging

  • Debugging bezeichnet die systematische Fehlersuche & -behebung im Quellcode. Entwickler nutzen Debugging-Tools, um Programme schrittweise auszuführen, den Programmablauf zu analysieren und den Zustand von Variablen zu überprüfen.
  • Breakpoints: Ermöglichen das Stoppen der Programmausführung an bestimmten Stellen, um Fehlerquellen gezielt zu untersuchen und Codeprobleme zu verstehen.
  • Ergänzend: Log-Dateien & Fehlermeldungen liefern zusätzliche Hinweise zur Identifikation und Analyse von Fehlern.

Java Runtime Environment

Java Conceptual Diagram

Java Runtime Environment

Das JRE bietet die Laufzeitumgebung, die erforderlich ist, um Java-Anwendungen auszuführen. Es umfasst die JVM, Bibliotheken und weitere Ressourcen, die Java plattformunabhängig machen. Kernkomponenten:

  • JVM: Führt Bytecode aus und übersetzt ihn in plattformspezifischen Maschinencode.
  • Class Loader: Lädt Klassen dynamisch während der Laufzeit.
  • Garbage Collector: Automatisiert die Speicherverwaltung, verhindert Speicherlecks.
  • Standardbibliothek: Bietet vorgefertigte Klassen und Methoden für typische Programmieraufgaben.

Java Development Kit

Das JDK ist eine Sammlung von Werkzeugen & Bibliotheken für die Entwicklung, Kompilierung und Ausführung von Java-Anwendungen. Es ist die Grundlage für alle Java-IDEs. Zentrale Bestandteile:

  • Java Compiler (javac): Übersetzt Quellcode (.java) in Bytecode (.class) + prüft auf Syntaxfehler.
  • Java Debugger (JDB): Ermöglicht schrittweises Debugging & Überwachung zur Fehlerbehebung.
  • Weitere Tools: z.B. javadoc (Dokumentationserstellung) & javap (Disassemblieren von Bytecode)

Versionierung: Seit Java 11 alle 6 Monate. Alle 2 Jahre gibt es eine LTS-Version. Aktuell: JDK 25

Distributionen: Offiziell von Oracle; Open-Source-Alternative: OpenJDK

Programmierparadigmen

Programmierparadigmen sind grundlegende Ansätze zur Strukturierung & Organisation von Code. Sie beeinflussen die Art und Weise, wie Entwickler Probleme lösen, Software entwerfen und implementieren.

Imperativ

Das imperative Programmieren ist ein Paradigma, das sich darauf konzentriert, wie ein Programm seine Aufgaben ausführt. Es beschreibt die Schritte, die zur Lösung eines Problems erforderlich sind, und legt fest, wie der Programmablauf gesteuert wird. In Java wird imperatives Programmieren häufig in Kombination mit Kontrollstrukturen wie Schleifen und Bedingungen verwendet.


                    
                    

Deklarativ

Im deklarativen Programmieren wird beschrieben, was erreicht werden soll, ohne detailliert anzugeben, wie dies geschehen soll. Dieses Paradigma konzentriert sich auf die Verwendung von Ausdrücken und Bedingungen, um das gewünschte Ergebnis zu erzielen. In Java zeigt sich deklaratives Programmieren häufig in der Verwendung von API-Funktionen und Datenbankabfragen (z.B. mit SQL).


                    
                    

Funktional

Funktionales Programmieren fokussiert sich auf die Anwendung und Komposition von Funktionen, wobei Zustandsveränderungen vermieden werden – fördert einen deklarativen Programmierstil.

Java: Implementiert durch Lambda-Ausdrücke, höherwertige Funktionen, das Package java.util.function und die Stream-API (z. B. Filtern, Transformieren, Reduzieren von Daten).

Funktional


                
                

Objektorientiert

Das objektorientierte Programmieren ist ein Paradigma, das die Strukturierung von Code um Objekte herum fördert. Objekte sind Instanzen von Klassen, die Daten (Attribute) und Verhalten (Methoden) kapseln. OOP bietet Konzepte wie Vererbung, Polymorphismus & Kapselung, die eine bessere Wiederverwendbarkeit und Wartbarkeit des Codes ermöglichen.

Entwicklungsumgebung (IDE)

Eclipse


Eine IDE die v. a. früher zahlreich verwendet wurde, heute abnehmende Beliebtheit.

VS Code


Zunehmende Beliebtheit. Leichtgewichtiger Editor mit zahlreichen Extensions.

IntelliJ


  • Community Edition (kostenlos): Link
  • Studenten-Lizenz für Ultimate-Edition: Link

Über Jahre stetig hohe Beliebtheit. Großes (vorgegebenes) Ökosystem.

Wird im Programmentwurf verwendet!

Grundlagen

Syntax

  • Beschreibt die Regeln für korrektes Schreiben von Programmen
  • Java → streng typisiert, objektorientiert, fördert Lesbarkeit & Wartbarkeit
  • Statements: Kleinste ausführbare Einheiten in Java, enden mit „;“, z. B. Variablenzuweisungen, Methodenaufrufe oder Kontrollstrukturen.

                
                

Syntax

  • Blöcke: Gruppieren mehrere Anweisungen mit „{}“ und definieren den Gültigkeitsbereich (Scope) von Variablen.

                
                

Main-Methode

Die main-Methode ist der Einstiegspunkt jedes Java-Programms und eine der wichtigsten Komponenten der Sprache. Sie wird von der JVM aufgerufen, um die Ausführung eines Programms zu starten.


                
                

Main-Methode-Aufbau

  • Zugriffsmodifikator public: Die Methode ist öffentlich, damit die JVM darauf zugreifen kann.
  • Statische Eigenschaft static: Die Methode gehört zur Klasse selbst und nicht zu einer Instanz, da die JVM beim Programmstart keine Objekte erzeugt und die Methode direkt aufrufen muss.
  • Rückgabewert void: Zeigt an, dass die main-Methode keinen Wert zurückgibt, sondern lediglich den Programmstartpunkt definiert und anschließend die Kontrolle an die JVM übergibt.
  • Parameter String[] args: Das Argument ist ein Array von Zeichenketten, das Befehlszeilenargumente enthält. Können zur Laufzeit genutzt werden, um das Programm-Verhalten dynamisch zu beeinflussen.

                
                

Main-Methode-Befehlszeilenargumente

java CommandLineExample Se7en

                
                

Einschub Command-Line

Je nach Betriebssystem, JDK-Version und Verzeichnis-Struktur können sich Command-Line-Befehle für Java unterscheiden.

Generell kann jedoch ein Java-Programm, dessen Klasse „Main“ heißt, mit java Main aufgerufen werden. Liegt noch keine kompilierte Version vor, kann mit javac Main.java eine solche Datei erzeugt werden, anschließend kann das Programm ausgeführt werden. Sollte das Programm in einem Unterverzeichnis, z. B. „src“ liegen, sehen die Befehle wie folgt aus: javac src/Main.java und java -cp src Main

Zur Vereinfachung wird während der Vorlesung auf die Funktionen der IDEs gesetzt.

Primitive Datentypen

Primitive Datentypen bilden die grundlegenden Bausteine für die Verarbeitung von Daten in Java. Sie sind die einfachsten Datentypen und werden zur Darstellung grundlegender Informationen wie Zahlen, Zeichen und Wahrheitswerte verwendet. Im Gegensatz zu Objekten, sogenannten Referenztypen haben primitive Datentypen keine Methoden und sind nicht veränderbar – wird als immutable bezeichnet).

Primitive Datentypen

Datentyp Speichergröße Wertebereich
byte 8 Bits -128 bis 127
short 16 Bits -32.768 bis 32.767
int 32 Bits -2.147.483.648 bis 2.147.483.647
long 64 Bits -9.223.372.036.854.775.808 bis 9.223.372.036.854.775.807
float 32 Bits ca. 1.4E-45 bis 3.4028235E+38
double 64 Bits ca. 4.9E-324 bis 1.7976931348623157E+308
char 16 Bits Unicode‑Zeichen (0 bis 65.535)
boolean 1 Bit true oder false

Praxis: Possible lossy conversion

Folgender Java-Code


                
                

Führt zu folgender Meldung in der Konsole

incompatible types: possible lossy conversion from long to int

Praxis: Possible lossy conversion

Andere Beispiele


                
                

                
                

Praxis: Possible lossy conversion

Lossy conversion


                
                

Möglich


                
                

Praxis: Possible lossy conversion

Größere Datentypen sind möglich


                
                

Praxis: Possible lossy conversion

Gegenmaßnahme (u.a.): Downcasting


                
                

Praxis: Overflow

Was ist hier zu erwarten?


                
                

Praxis: Overflow

Ergebnis


                
                

Praxis: Overflow

Gegenmaßnahmen (u.a.)

  • Andere/ größere Datentypen verwenden (z.B. long, BigInteger)
  • Validierung vor der Operation mit Bedingungen (siehe Kapitel Kontrollstrukturen)

Übungen

  1. Erstelle ein Programm, das alle primitiven Datentypen in Java deklariert, initialisiert und deren Werte auf der Konsole ausgibt.
  2. Vergleiche die Genauigkeit von float und double durch eine Berechnung, die Rundungsfehler demonstriert.

Einschub: String

In Java ist der Datentyp String eine unveränderliche (immutable) Klasse, die eine Folge von Zeichen repräsentiert und zur Speicherung und Manipulation von Text verwendet wird. Der String-Typ bietet zahlreiche Methoden, um Operationen wie Verkettung, Suche, Teilstring-Extraktion oder Vergleich durchzuführen.

Java Doc String

Einschub: String


                
                

Operatoren – Arithmetisch

Operator Beschreibung
+ Addition
- Subtraktion
* Multiplikation
/ Division
% Modulo (Rest)

Operatoren – Arithmetisch


                
                

Praxis: Floating-Point Precision Error

Floating-Point Precision Error

Praxis: Floating-Point Precision Error


                
                

JSON-Array mit Daten

Praxis: Floating-Point Precision Error


                
                

Ausschnitt der Vue-Component

Praxis: Floating-Point Precision Error

Warum tritt der Fehler auf?

Die Dezimalzahl 0.1306573 kann nicht exakt binär dargestellt werden. Begründung: Es können nur Brüche dargestellt werden, die Summen von Potenzen sind (1/2, 1/4, 1/8, usw.). Der Computer speichert stattdessen einen minimal ungenaueren Wert: 0.13065729999999998. Dies ist eine direkte Folge der IEEE 754-Norm zur Darstellung von Fließkommazahlen.

Als Lösung für dieses Problem bieten Programmiersprachen wie Java z.B. eine BigDecimal-Klasse an.

Operatoren – Relational

Operator Beschreibung
== Gleichheit
!= Ungleichheit
> Größer als
< Kleiner als
>= Größer oder gleich
<= Kleiner oder gleich

Operatoren – Logisch

Operator Beschreibung
&& Logisches UND
|| Logisches ODER
! Logische Negation
^ Logisches XOR

Exkurs: Operatoren – Bitweise

Operator Beschreibung
& Bitweise UND
| Bitweise ODER
~ Bitweise Negation
^ Bitweise XOR
<< Signed Left Shift
>> Signed Right Shift
>>> Unsigned Right Shift

Einsatzzwecke u.a. Embedded System, Kryptographie, mathematische Programme

Exkurs: Bitwise Operatoren


                
                

Operatoren – Zuweisung

Operator Beschreibung
= Zuweisung
+= Addition und Zuweisung
-= Subtraktion und Zuweisung
*= Multiplikation und Zuweisung
/= Division und Zuweisung
%= Modulo und Zuweisung

Operatoren – Inkrement & Dekrement

Die Inkrement- (++) & Dekrementoperatoren (--) erhöhen oder verringern den Wert einer Variablen um eins. Sie können sowohl vor der Variablen - präfix - als auch danach - postfix- verwendet werden.

Operatoren – Inkrement & Dekrement


                
                

Übungen

  1. Erstelle ein Programm, das zwei Zahlen definiert und die grundlegenden arithmetischen Operationen an diesen Zahlen durchführt. Zeige anschließend, wie Zuweisungsoperatoren verwendet werden.
  2. Schreibe ein Programm, das zwei ganzzahlige Variablen definiert und mithilfe von Vergleichsoperatoren überprüft, ob eine der Zahlen größer, kleiner oder gleich der anderen ist.
  3. Erstelle ein Programm, das drei boolean-Variablen definiert und mithilfe logischer Operatoren überprüft, ob mindestens zwei der Variablen den Wert true haben.
  4. Erstelle ein Programm, das die Verwendung von Inkrement- & Dekrementoperatoren veranschaulicht.

Scanner

Die Scanner-Klasse in Java dient zum Einlesen von Benutzereingaben aus verschiedenen Quellen, u. a. der Konsole. Sie bietet Methoden wie nextLine(), nextInt() oder nextDouble(), um unterschiedliche Datentypen (z. B. Strings, Zahlen) einfach einzulesen und zu verarbeiten.
Java Doc Scanner

Scanner


                
                

Kontrollstrukturen

if-Anweisung

Die einfachste Form einer bedingten Verzweigung in Java ist die if-Anweisung. Sie führt einen Codeblock nur dann aus, wenn die angegebene Bedingung erfüllt (true) ist. Andernfalls wird der Codeblock übersprungen.

if-Anweisung


                
                

if-else-Verzweigung

Die if-else-Verzweigung erweitert die if-Anweisung, indem sie eine Alternative bietet, wenn die Bedingung nicht wahr ist. Das bedeutet, dass ein alternativer Codeblock immer ausgeführt wird, wenn die Bedingung falsch ist.

if-else-Verzweigung


                
                

if-else-if-Kette

Wenn mehrere Bedingungen nacheinander geprüft werden sollen, wird die if-else-if-Kette verwendet. Mit dieser Struktur können verschiedene Bedingungen nacheinander geprüft werden, und der erste Block, dessen Bedingung wahr ist, wird ausgeführt.

if-else-if-Kette


                
                

Verschachtelung

If-Else-Verzweigungen können auch verschachtelt werden, indem man if- oder if-else-Blöcke innerhalb anderer if- oder else-Blöcke platziert. Dies ermöglicht komplexere Entscheidungsstrukturen.

Verschachtelung


                
                

Ternärer Operator

Java (und andere Programmiersprachen) bietet eine kompakte Möglichkeit, einfache If-Else-Verzweigungen mit dem ternären Operator in einer Zeile auszudrücken.

Ternärer Operator


                
                

Übungen

  1. Implementiere ein Programm, das eine Zahl einliest und mittels eines if-Statements prüft, ob sie positiv, negativ oder null ist, und die entsprechende Meldung ausgibt.
  2. Erstelle ein Programm, das Noten in Buchstabenwerte (A, B, C...) umwandelt, basierend auf Punktzahlen mit einer if-else-if-Kette.
  3. Schreibe ein Programm mit verschachtelten If-Else-Verzweigungen, das die maximale von drei Zahlen bestimmt.
  4. Nutze den ternären Operator, um zu evaluieren, ob eine Zahl gerade oder ungerade ist.

Switch-Statement

Die switch-Anweisung ist eine Kontrollstruktur in Java, die es ermöglicht, eine Variable mit mehreren möglichen Werten zu vergleichen und basierend auf dem Wert einen bestimmten Codeblock auszuführen. Sie wird häufig verwendet, wenn mehrere Bedingungen nacheinander geprüft werden müssen und es zu umständlich oder unübersichtlich wäre, viele if-else-if-Anweisungen zu verwenden.
switch-Anweisungen können zudem durch sogenannte jump tables performanter sein, als if-else-Anweisungen (Paper-Link).

Switch-Statement

  1. Ausdruck: Der Ausdruck, dessen Wert geprüft wird. In Java können dies die folgenden Datentypen sein: int, short, char, byte (oder deren Wrapper-Typen), String (seit Java 7) oder eine Enumaration.
  2. case: Jeder case-Block enthält einen möglichen Wert für den Ausdruck. Wenn der Ausdruck den angegebenen Wert hat, werden die Anweisungen im entsprechenden case-Block ausgeführt.
  3. break: Beendet die switch-Anweisung, damit keine Anweisungen in den folgenden case-Blöcken ausgeführt werden. Findet die break-Anweisung nicht statt, werden alle nachfolgenden case-Blöcke ausgeführt - dieses Verhalten nennt man Fallthrough
  4. default: Wird ausgeführt, wenn keiner der vorherigen case-Blöcke zutrifft. Der default-Block ist optional.

Switch-Statement


                
                

Switch-Statement - Fallthrough


                
                

Übungen

  1. Schreibe ein Programm, das dem Benutzer ein Menü mit verschiedenen Getränken (z. B. 1 = Wasser, 2 = Kaffee, 3 = Tee, 4 = Limonade) anzeigt. Der Benutzer gibt eine Zahl ein, und das Programm gibt den Namen des gewählten Getränks aus. Falls die Eingabe ungültig ist (z. B. 0 oder eine Zahl außerhalb des Menüs), soll eine Fehlermeldung erscheinen. Nutzer u.a. switch-Statements.
  2. Schreibe ein Programm, das eine Schulnote (1 bis 6) einliest und mithilfe eines switch-Statements eine passende Beschreibung ausgibt (z. B. 1 = „Sehr gut“, 2 = „Gut“, 3 = „Befriedigend“ usw.). Für ungültige Eingaben soll eine entsprechende Fehlermeldung ausgegeben werden.

Schleifen

Schleifen

Schleifen ermöglichen es, einen Block von Anweisungen wiederholt auszuführen, solange eine bestimmte Bedingung erfüllt ist. Java bietet mehrere Arten von Schleifen, die je nach Bedarf und Anwendungsfall verwendet werden können.

for-Schleife

Die for-Schleife wird verwendet, wenn die Anzahl der Wiederholungen bereits im Voraus bekannt ist:

  • Initialisierung: Hier wird eine Zählvariable erstellt und initialisiert.
  • Bedingung: Die Schleife wird so lange wiederholt, wie diese Bedingung wahr ist.
  • Iteration: In jedem Durchlauf wird die Zählvariable aktualisiert

for-Schleife


                
                

while-Schleife

Die while-Schleife wird verwendet, wenn die Anzahl der Wiederholungen nicht im Voraus bekannt ist und die Schleife so lange laufen soll, wie eine bestimmte Bedingung wahr ist.

while-Schleife


                
                

do-while-Schleife

Die do-while-Schleife ist ähnlich wie die while-Schleife, führt jedoch den Anweisungsblock mindestens einmal aus, bevor die Bedingung überprüft wird.

do-while-Schleife


                
                

do-while-Schleife

While vs. Do-While

for-each-Schleife

Die erweiterte for-Schleife, for-each-Schleife genannt, ist eine Schleife, die speziell für die Iteration über Arrays & Sammlungen (engl. Collections) in Java entwickelt wurde. Sie ist besonders nützlich, wenn alle Elemente in einer Sammlung durchlaufen werden, ohne einen Index verwenden zu müssen.

for-each-Schleife


                
                

break und continue

break: Beendet die Schleife sofort, unabhängig von der Bedingung.

break und continue


                
                

break und continue

continue: Überspringt den aktuellen Durchlauf der Schleife und fährt mit dem nächsten Durchlauf fort.

break und continue


                
                

Übungen

  1. Schreibe ein Programm, das die einfache for-Schleife verwendet, um die ersten 10 Fibonacci-Zahlen zu berechnen.
  2. Erstelle ein Programm, das eine while-Schleife verwendet, um eine Zahl so lange zu halbieren, bis sie kleiner als 1 ist.
  3. Implementiere eine do-while-Schleife, um ein Passwort von einem Benutzer abzufragen, bis es korrekt eingegeben wird.
  4. Implementiere ein Programm, das die Fakultät einer Zahl mit einer for- & while-Schleife berechnet.
  5. Schreibe ein Programm, das die Verwendung von break und continue in Schleifen zeigt, z. B. um Zahlen in einem Bereich zu überspringen.
  6. Implementiere ein Programm, das überprüft, ob eine gegebene Zahl eine Primzahl ist.

Datenstrukturen

Datenstrukturen

Datenstrukturen sind essenzielle Werkzeuge in der Programmierung, die es ermöglichen, Daten effizient zu speichern, zu organisieren und darauf zuzugreifen. In Java gehören Arrays & ArrayLists zu den grundlegendsten und am häufigsten verwendeten Datenstrukturen.

Arrays

Ein Array ist eine Datenstruktur, die eine feste Anzahl von Elementen desselben Typs enthält. Es wird zur Speicherung einer festgelegten Anzahl von Werten verwendet, die über einen Index angesprochen werden können.

Arrays


                
                

Einschub: Wrapper

Java bietet Mechanismen, die primitive Datentypen automatisch in ihre entsprechenden Wrapper- Klassen zu konvertieren und umgekehrt.

  • Autoboxing: Automatische Umwandlung eines primitiven Typs in sein entsprechendes Wrapper-Objekt.
  • Unboxing: Automatische Umwandlung eines Wrapper-Objekts in seinen primitiven Typ.

Wrapper


                
                

ArrayList

Die ArrayList ist eine flexible, dynamische Datenstruktur, die Teil der Java Collections Framework ist. Im Gegensatz zu Arrays kann ihre Größe dynamisch angepasst werden, was sie für Anwendungen geeignet macht, bei denen die Anzahl der zu speichernden Elemente nicht im Voraus bekannt ist.

ArrayList


                
                

Übungen

  1. Erstelle ein Programm, das ein Array mit mindestens 10 ganzzahligen Werten enthält. Verwende eine forEach-Schleife, um die Summe aller Elemente im Array zu berechnen. Gib anschließend den Durchschnitt der Werte auf der Konsole aus.
  2. Schreibe ein Programm, das eine ArrayList mit mindestens 15 Zufallszahlen zwischen 1 und 100 generiert - verwende hierzu new Random().nextInt(100)+1. Verwende eine forEach-Schleife, um alle Zahlen in der Liste zu durchlaufen und nur die geraden Zahlen auszugeben.
  3. Schreibe eine Applikation, die ein 2D-Array transponiert (Zeilen und Spalten vertauscht).
    Beispiel: (1, 2, 3), (4, 5, 6) wird zu (1, 4), (2, 5), (3, 6)

Parameter

Parameter

Ein Parameter ist eine Variable, die in der Methodensignatur definiert ist und der Methode einen Wert übergibt, wenn sie aufgerufen wird. Parameter ermöglichen es Methoden, Eingabewerte zu erhalten und basierend auf diesen Werten Operationen durchzuführen. Die Definition von Parametern erfolgt in der Methodensignatur, zwischen dem Methodennamen und den geschweiften Klammern.

Parameter


                
                

Call by Value

Bei der Übergabe von Wertparametern wird eine Kopie des Wertes an die Methode übergeben. Änderungen am Parameter innerhalb der Methode haben keinen Einfluss auf den ursprünglichen Wert außerhalb der Methode.

Call by Value


                
                

Call by Reference

Bei der Übergabe von Referenzparametern wird die Referenz (der Speicherort) eines Objekts an die Methode übergeben. Änderungen an den Parametern innerhalb der Methode wirken sich auf das ursprüngliche Objekt aus.

Call by Reference


                
                

Übungen

Demonstriere anhand eines einfachen Beispiels, wie Parameter in Java sowohl als Wert als auch als Referenz übergeben werden:

  1. Erstelle eine Methode, die einen Wert (z.B. eine Zahl) als Parameter entgegennimmt und diesen Wert verändert. Zeige, dass der ursprüngliche Wert in der aufrufenden Methode nicht verändert wird.
  2. Erstelle eine zweite Methode, die ein Objekt (z.B. eine ArrayList) als Parameter entgegennimmt und einen Wert des Objekts ändert. Zeige, dass sich das Objekt in der aufrufenden Methode verändert.

Varargs

Java unterstützt auch die Verwendung von variablen Argumenten, sogenannten Varargs, die es ermöglichen, eine beliebige Anzahl von Argumenten eines bestimmten Typs an eine Methode zu übergeben. Dies ist besonders nützlich, wenn die Anzahl der Eingaben zur Compile-Zeit nicht bekannt ist.

Varargs


                
                

Methoden

Prozedur & Funktion

In Java sind Methoden grundlegende Bausteine, die es ermöglichen, Code zu strukturieren und wiederverwendbar zu gestalten. Methoden können in zwei Hauptkategorien unterteilt werden: Prozeduren & Funktionen.

Prozedur

  1. Eine Prozedur ist eine Methode, die Aufgaben ausführt, aber keinen Rückgabewert hat. Sie dient dazu, wiederkehrende Operationen wie z. B. Datenmodifikationen oder Ausgaben auf dem Bildschirm zu kapseln.
  2. Rückgabetyp: Der Rückgabetyp void zeigt an, dass die Prozedur keine Werte zurückgibt, sondern lediglich Aktionen ausführt.
  3. Aufbau: Eine Prozedur besteht aus einer Methodensignatur, die den Namen und die Parameter definiert, sowie einem Methodenrumpf, der die auszuführenden Anweisungen enthält.

Prozedur

Prozedur aufbau

Prozedur


                
                

Funktion

  • Eine Funktion ist eine Methode, die Aufgaben ausführt und einen Wert zurückgibt. Sie wird eingesetzt, wenn eine Berechnung ein Ergebnis erzeugt, das weiterverwendet werden soll.
  • Rückgabewert: Der Rückgabewert wird in der Methodensignatur durch den Datentyp festgelegt. Funktionen geben mit dem Schlüsselwort return den berechneten Wert zurück.
  • Aufbau: Eine Funktion umfasst eine Methodensignatur und einen Methodenrumpf mit Anweisungen, die den gewünschten Wert berechnen.

Funktion


                
                

Übungen

  1. Schreibe eine Prozedur, die eine Nachricht auf der Konsole ausgibt, und eine Funktion, die den Durchschnitt von drei Zahlen berechnet.
  2. Schreibe eine Methode, die den Umfang eines Kreises berechnet, und definiere den Radius als Parameter.
  3. Schreibe eine Prozedur, die alle Zahlen von 1 bis n (Parameter) auf der Konsole ausgibt. Schreibe eine Funktion, die prüft, ob eine gegebene Zahl (Parameter) eine Primzahl ist. Die Funktion soll true zurückgeben, wenn die Zahl eine Primzahl ist, und false, wenn nicht.

Objekte und Klassen

Referenzdatentypen

Ein Referenzdatentyp ist ein Datentyp, der eine Referenz auf ein Objekt im Speicher hält, anstatt den Wert des Objekts selbst zu speichern – im Gegensatz zu primitiven Datentypen. Diese Referenz ermöglicht den Zugriff auf die Attribute und Methoden des Objekts.

Referenzdatentypen – Arten

Klassen: Grundlage für die Erstellung von Objekten in Java. Sie definieren die Struktur und das Verhalten von Objekten, einschließlich ihrer Attribute und Methoden.

Referenzdatentypen – Arten


                
                

Referenzdatentypen – Arten

Interface: Eine Sammlung von abstrakten Methoden, die von Klassen implementiert werden können. Interfaces fördern die Modularität und ermöglichen polymorphe Programmierung.

Referenzdatentypen – Arten


                
                

Referenzdatentypen – Arten

Arrays: Spezielle Objekte, die eine Sammlung von Werten des gleichen Datentyps speichern. In Java sind Arrays selbst Referenzdatentypen, die auf eine Gruppe von Werten verweisen.

Referenzdatentypen – Arten


                
                

Referenzdatentypen – Arten

Strings: Eine spezielle Art von Referenzdatentypen, die eine Zeichenfolgenstruktur repräsentieren.

Referenzdatentypen – Arten


                
                

Nullreferenzen

Eine Nullreferenz eine Referenzvariable, die mit null belegt ist und somit auf kein Objekt im Speicher verweist; der Zugriff auf Methoden oder Attribute einer solchen Referenz löst eine NullPointerException aus, weshalb vor der Nutzung stets eine Nullprüfung empfohlen wird.

Nullreferenzen


                
                

Operatoren – Instanceof

Der instanceof-Operator wird verwendet, um zu überprüfen, ob ein Objekt eine Instanz einer bestimmten Klasse oder eines Interfaces ist. Dies wird in der objektorientierten Programmierung verwendet, um die Typensicherheit zu gewährleisten.

Operatoren – Instanceof


                
                

Enums

Ein Enum (kurz für Enumerationen) ist eine spezielle Klasse, die eine Gruppe von konstanten Werten definiert. In Java werden Enums mit dem Schlüsselwort enum deklariert, gefolgt von einer Liste der Werte, die durch Kommas getrennt sind.

Enums


                
                

Enums


                
                

Übungen

  1. Definiere ein Enum namens Wochentag und implementiere eine Methode, die je nach Wochentag unterschiedliche Nachrichten ausgibt (z.B. „Wochenende“ oder „Arbeitstag“).
  2. Ergänze das Enum mit Eigenschaften (z.B. Anzahl der Stunden pro Arbeitstag) und demonstriere deren Nutzung.

Klassen

Eine Klasse in Java ist ein Datentyp, der eine Sammlung von Attributen und Methoden innehält, um das Verhalten und die Eigenschaften von Objekten zu beschreiben. Klassen ermöglichen es Entwicklern, Daten und Funktionen logisch zu organisieren und wiederverwendbare Codeeinheiten zu erstellen.

Klassen


                
                

Zugriffsmodifikatoren

In Java können Zugriffsmodifikatoren verwendet werden, um die Sichtbarkeit von Klassen, Attributen und Methoden zu steuern:

  • public: Die Klasse, das Attribut oder die Methode ist von überall im Programm zugänglich.
  • private: Die Klasse, das Attribut oder die Methode ist nur innerhalb der eigenen Klasse sichtbar.
  • protected: Die Klasse, das Attribut oder die Methode ist innerhalb der eigenen Klasse, in abgeleiteten Klassen und in Klassen im gleichen Paket sichtbar.
  • package-private (default): Die Klasse, das Attribut oder die Methode ist nur innerhalb des gleichen Pakets sichtbar.

Zugriffsmodifikatoren

Zugriffsmodifikatoren

Zugriffsmodifikatoren


                
                

Konstruktoren

Ein Konstruktor ist eine spezielle Methode, die beim Erstellen eines Objekts einer Klasse aufgerufen wird. Konstruktoren haben denselben Namen wie die Klasse und haben keinen Rückgabewert. Sie werden verwendet, um die Attribute eines Objekts zu initialisieren.

Konstruktoren


                
                

Konstruktoren

Hinweis: Der Vorteil von this liegt in der Lösung von Namenskonflikte zwischen Instanzvariablen & lokalen Variablen/ Parametern - shadowing. this repräsentiert die Referenz auf das aktuelle Objekt.

Objekte und Instanziierung

Ein Objekt ist eine Instanz einer Klasse, die im Speicher existiert und Zugriff auf die Attribute und Methoden der Klasse hat. Um ein Objekt zu erstellen, d. h. zu instanziieren, verwendet man das Schlüsselwort new, gefolgt von einem Aufruf des Konstruktors der Klasse.

Objekte und Instanziierung


                
                

Praxis: N Classes; 1 File


                
                

Mehrere Logik-Inhalte in einer Datei

Praxis: N Classes; 1 File


                
                

Verteilung der Logiken in Subbausteine

Praxis: N Classes; 1 File

Gründe gegen mehrere "lose" Klassen in einer Datei:

  • Compile-Schwierigkeiten, v.a. bei Abhängigkeiten
  • Unübersichtliche/ verwirrende Strukturierung
  • Verhindert logische Kapselung, es wird zu viel Code "offenbart"
Oracle Tutorial

Übungen

  1. Erstelle eine Person-Klasse, die zwei Felder enthält: name und age. Schreibe ein Programm, das eine Person-Instanz erstellt und deren Name und Alter ausgibt.
    • Vermeide eine NullPointerException, indem du sicherstellst, dass die Instanz nicht null ist, bevor du auf deren Felder zugreifst. Falls die Instanz null ist, gib eine entsprechende Fehlermeldung aus.
  2. Schreibe eine Methode, die einen String als Parameter entgegennimmt und dessen Länge zurückgibt.
    • Verhindere eine NullPointerException, indem du sicherstellst, dass der übergebene String nicht null ist, bevor du dessen Länge berechnest. Falls der String null ist, gebe eine Fehlermeldung aus, die darauf hinweist.

Übungen

Erstelle eine Klasse: Car. Verwende in beiden Klassen Konstruktoren und Zugriffsmodifikatoren:

  1. Erstelle eine Auto-Klasse mit den Attributen brand, model und yearOfManufacture.
  2. Implementiere einen Konstruktor, der diese Attribute initialisiert.
  3. Implementiere eine öffentliche Methode, die eine Nachricht auf der Konsole ausgibt, um die Werte der Attribute anzuzeigen (z. B. showCarInfo()).
  4. Verwende private Zugriffsmodifikatoren für die Felder und stelle sicher, dass sie nicht direkt von außen zugänglich sind, sondern nur über Methoden wie showCarInfo() verwendet werden.
  5. Füge eine geschützte Methode, die den Zustand des Autos beschreibt, z. B. „Das Auto fährt“. Diese Methode sollte nur innerhalb der Klasse und von abgeleiteten Klassen zugänglich sein.

Vererbung

Vererbung ermöglicht es einer Klasse, die Eigenschaften und Methoden einer anderen Klasse zu erben, was zu einer Hierarchie von Klassen führt.

Vererbung


                
                

Method Overriding

Method Overriding ist ein Konzept, bei dem eine abgeleitete Klasse eine Methode einer Basisklasse mit der gleichen Signatur neu implementiert. Diese Funktionalität ermöglicht es einer abgeleiteten Klasse, das Verhalten einer Methode der Basisklasse zu ändern. Das Überschreiben einer Methode wird durch die Annotation @Override gekennzeichnet.

Method Overriding


                
                

Übungen

  1. Schreibe eine Klasse mit mehreren überladenen Methoden zur Berechnung der Fläche von circle, rectangle und triangle.
  2. Implementiere eine Klasse Animal mit der Methode makeSound(). Leite Klassen wie Dog und Cat ab, und überschreibe die Methode.

Übergeordnete Variablen & Methoden

Das super-Keyword wird verwendet, um auf die Methoden und Attribute der übergeordneten Klasse zuzugreifen.

Übergeordnete Variablen & Methoden


                
                

Übergeordnete Variablen & Methoden


                
                

Übungen

  1. Implementiere ein Beispiel für Vererbung, indem eine Klasse Vehicle und davon abgeleitete Klassen wie Car und Motorcycle erstellt werden.
  2. Erstelle eine Basisklasse und eine abgeleitete Klasse. Zeige, wie mit super auf übergeordnete Variablen und Methoden zugegriffen werden kann.
  3. Definiere eine Klasse Vehicle mit Unterklassen Car und Bicycle. Implementiere unterschiedliche Methoden wie driving() für jede Klasse.

Klassenvariablen & -Methoden

Das static-Keyword wird verwendet, um Variablen und Methoden als Klassenmitglieder zu deklarieren, die nicht an eine spezifische Instanz einer Klasse gebunden sind. Stattdessen sind sie auf die Klasse selbst bezogen.

Klassenvariablen & -Methoden


                
                

Klassenvariablen & -Methoden


                
                

Übungen

  1. Erstelle eine Klasse ShoppingBasket mit einer statischen Konstanten MAX_ITEMS = 10. Füge eine statische Methode isFull(int currentAmount) hinzu, die prüft, ob die Artikelanzahl den Maximalwert erreicht. Teste die Methode mit verschiedenen Werten.
  2. Erstelle eine Klasse AppConfiguration mit einer statischen Liste defaultLanguages, die vordefinierte Sprachen (z.B. "Deutsch", "Englisch") enthält. Schreibe eine statische Methode addLanguages(String language), die eine neue Sprache zur Liste hinzufügt. Gib die Liste nach dem Hinzufügen aus.

Unveränderbarkeit

final wird verwendet, um eine Variable, Methode/ Klasse als unveränderlich zu kennzeichnen – einmal zugewiesene Werte können nicht geändert werden:

Unveränderbarkeit

Finale Variablen: Eine finale Variable kann nur einmal zugewiesen und danach nicht mehr geändert werden.


                
                

Unveränderbarkeit

Finale Methoden: Eine finale Methode kann nicht in einer abgeleiteten Klasse überschrieben werden.


                
                

Unveränderbarkeit

Finale Klassen: Eine finale Klasse kann nicht erweitert (vererbt) werden.


                
                

Übungen

  1. Erstelle eine Klasse Kreis, die eine finale Variable PI enthält, die den Wert von Pi (3.14159) speichert. In der Klasse soll eine Methode calculateArea() implementiert werden, die den Flächeninhalt eines Kreises berechnet. Die Methode nimmt den Radius als Parameter und berechnet die Fläche mit der finalen PI-Variablen.
  2. Erstelle eine Klasse Vehicle, die eine Methode driving() enthält, die mit dem final-Modifikator markiert ist.
  3. Erstelle eine unveränderliche Klasse Address mit den Attributen Street, town and postcode. Stelle sicher, dass die Klasse unveränderlich bleibt.

Getter und Setter

Getter & Setter sind spezielle Methoden in Java, die den Zugriff auf private Attribute einer Klasse ermöglichen. Sie fördern die Kapselung, welches die direkte Manipulation von Datenfeldern von außen verhindert. Der Zugriff auf die Werte dieser Attribute findet auf kontrollierte Weise statt.

  • Getter: Eine Methode, die den Wert eines privaten Attributs zurückgibt. Er folgt typischerweise der Konvention, den Namen des Attributs mit dem Präfix get zu versehen.
  • Setter: Eine Methode, die den Wert eines privaten Attributs festlegt. Er folgt typischerweise der Konvention, den Namen des Attributs mit dem Präfix set zu versehen und hat als Parameter den Wert, der zugewiesen werden soll.

Getter und Setter


                
                

Übungen

  1. Erkläre, warum Getter und Setter nützlich sind. Implementiere dazu eine Klasse Account, die das aktuelle Guthaben speichert. Schreibe Getter- und Setter-Methoden mit Validierungen (z.B. keine negativen Einzahlungen).
  2. Implementieren die Klasse Person mit privaten Attributen wie Name und Alter. Schreibe passende Getter- und Setter-Methoden. Modifiziere die Setter-Methode so, dass sie das Alter auf Werte zwischen 0 und 120 begrenzt.

Java Object

In Java ist Object die Wurzel der Klassenhierarchie. Jede Klasse erbt direkt oder indirekt von Object. Daher verfügen alle Objekte über grundlegende Methoden:

  • toString(): Gibt eine textuelle Darstellung des Objekts zurück. Für Logging und Debugging wird empfohlen, diese Methode aussagekräftig zu überschreiben.
  • equals(Object obj): Vergleicht zwei Objekte auf inhaltliche Gleichheit. Die Standardimplementierung prüft nur die Referenzgleichheit (wie der == Operator). Sie sollte überschrieben werden, um Felder zu vergleichen.
  • hashCode(): Gibt einen Integer-Wert (Hash-Code) für das Objekt zurück. Wenn equals() überschrieben wird, muss auch hashCode() überschrieben werden, um sicherzustellen, dass gleiche Objekte denselben Hash-Code haben.

Java Object


                
                

Vorschau nächstes Semester

Java 21 Documentation

Wie ist Java hierarchisch aufgebaut?

Übung

Erstelle eine Klasse Soundtrack mit den Attributen title, artist und duration. Überschreibe die Methoden toString(), equals() und hashCode() sinnvoll:

  1. toString() soll eine lesbare Darstellung des Musiktitels zurückgeben.
  2. equals() soll zwei Musiktitel-Objekte als gleich betrachten, wenn alle drei Attribute übereinstimmen.
  3. hashCode() soll einen Hash-Code basierend auf den Attributen berechnen.

Erstelle zwei Musiktitel-Objekte mit denselben Attributen und prüfe, ob sie gleich sind

Records

Records sind ein neuer Datentyp (ab Java 16). Sie bieten eine einfache Möglichkeit, Datenklassen zu erstellen, die nur aus einem Satz von unveränderlichen Attributen bestehen. Records reduzieren Boilerplate-Code, indem sie automatisch Getter, toString(), equals() und hashCode()-Methoden generieren. Ein Record wird mit dem Schlüsselwort record deklariert.

Records


                
                

Übungen

  1. Implementiere ein Record namens Point mit den Attributen x und y. Zeige, wie Records mit equals(), hashCode() und toString() arbeiten.
  2. Erstelle einen Record Book, der die Attribute title, author und pages enthält. Implementiere eine Methode describeBook(), die eine kurze Beschreibung des Buches ausgibt. Demonstriere in der main-Methode, wie du ein Buch-Objekt erstellst und die Methode aufrufst.

Abstrakte Klassen

Eine abstrakte Klasse wird mit dem Schlüsselwort abstract deklariert. Sie kann wie eine normale Klasse Attribute und Methoden definieren, jedoch müssen nicht alle Methoden implementiert sein. Abstrakte Methoden müssen in den abgeleiteten Klassen überschrieben werden. Zudem kann eine abstrakte Klasse nicht instanziiert werden.

Abstrakte Klassen


                
                

Übungen

  1. Erstellen eine abstrakte Klasse Shape mit Methoden wie calculateArea() und calculatePerimeter(). Leite konkrete Klassen wie circle and rectangle ab und implementiere die Methoden.
  2. Erstelle eine abstrakte Klasse Animal, die ein Attribut name enthält und eine abstrakte Methode makeNoise(). Implementiere in der abstrakten Klasse eine Methode showName(), die den Namen des Tieres ausgibt. Erstelle dann eine Klasse Dog, die von der abstrakten Klasse Animal erbt und die Methode makeNoise() implementiert. Demonstriere, wie du ein Hund-Objekt erstellst und beide Methoden aufrufst.

Interfaces

Ein Interface ist ein abstrakter Datentyp, der eine Sammlung von Definitionen zu Methoden & Konstanten beinhaltet. Die Deklaration findet mit dem Schlüsselwort interface statt. Es ist möglich, dass eine Klasse mehrere Interfaces implementiert. Eine Klasse, die ein Interface implementiert, muss alle in diesem Interface deklarierten Methoden implementieren.

Interfaces


                
                

default-Methoden

Seit Java 8 können Interfaces default-Methoden enthalten, die eine Standardimplementierung bereitstellen. Dies ermöglicht es, Interfaces zu erweitern, ohne bestehende Implementierungen zu beeinträchtigen. Klassen, die das Interface implementieren, erben automatisch die default-Methode, können sie aber bei Bedarf auch überschreiben.

default-Methoden


                
                

Übungen

Zeige den Unterschied zwischen abstrakten Klassen und Interfaces anhand eines Beispiels:

  1. Erstelle eine abstrakte Klasse Vehicle, die ein Attribut brand enthält und eine abstrakte Methode driving(). Implementiere in der abstrakten Klasse eine Methode showBrand(), die den Wert des Attributs ausgibt. Erstelle außerdem ein Interface EnergySource mit einer abstrakten Methode getEnergy(), die den Energietyp eines Fahrzeugs zurückgibt (z. B. „Benzin“, „Elektrisch“).
  2. Implementiere eine Klasse Car, die sowohl die abstrakte Klasse Vehicle erweitert als auch das Interface EnergySource implementiert. Zeige anhand eines Beispiels, wie die Methoden driving() und getEnergy() aufgerufen werden. Erläutere dabei den Unterschied in der Verwendung von abstrakten Klassen und Interfaces.

Method Overloading

Method Overloading bei dem mehrere Methoden in derselben Klasse den gleichen Namen, jedoch unterschiedliche Parameterlisten haben. Dies ermöglichtes Entwicklern, die gleiche Methode für verschiedene Eingabetypen oder -anzahlen zu verwenden. Bei der Methodenüberladung unterscheidet sich die Signatur der Methoden durch eine der folgenden Eigenschaften:

  • Anzahl der Parameter: Die Methoden können eine unterschiedliche Anzahl von Parametern haben.
  • Reihenfolge der Parameter: Die Reihenfolge der Parameter kann unterschiedlich sein, auch wenn die Anzahl und der Typ übereinstimmen.

Method Overloading


                
                

Übung

Erstelle eine Klasse AppDate mit überladenen Methoden showDate(), die:

  1. day, month und year als int-Variablen in der Reihenfolge Tag-Monat-Jahr ausgibt,
  2. month (String), day (int) und year (int) in der Reihenfolge Monat-Tag-Jahr ausgibt.

Erstelle eine Instanz von AppDate und rufe beide Methoden mit unterschiedlichen Parametern auf, um die Überladung zu demonstrieren.

Kohäsion

Kohäsion beschreibt die Beziehung innerhalb einer Klasse und misst, inwieweit die Methoden einer Klasse miteinander verbunden sind und zusammenarbeiten. Gründe für hohe Kohäsion:

  • Einfaches Verständnis: Hohe Kohäsion erleichtert das Verständnis der Funktionalität eines Moduls.
  • Einfache Wartung: Änderungen in einem Modul erfordern keine Änderungen in verwandten Modulen.
  • Einfache Wiederverwendbarkeit: Module können leichter wiederverwendet werden, da sie eine klare und spezifische Funktionalität bieten.

Kopplung

Kopplung beschreibt die Beziehung zwischen Klassen und gibt die Stärke der Abhängigkeiten zwischen Programmeinheiten an. Eine hohe Kopplung bedeutet, dass Einheiten stark voneinander abhängig sind, während eine niedrige Kopplung Unabhängigkeit anzeigt. Gründe für niedrige Kopplung:

  • Reduzierte Auswirkungen: Änderungen in einem Modul haben weniger Auswirkungen auf andere Module.
  • Einfachere Zusammenstellung: Zusammenstellung der Module erfordert weniger Aufwand und Zeit.
  • Erleichterte Wiederverwendbarkeit & Testbarkeit: Module können leichter wiederverwendet und getestet werden, da sie weniger Abhängigkeiten haben.

Kohäsion und Kopplung

Coupling vs Cohesion

Polymorphismus

Polymorphismus ermöglicht, dass Objekte oder Methoden unterschiedliche Formen/Verhaltensweisen annehmen können, abhängig vom Kontext. Dies macht den Code erweiterbar & wiederverwendbar. Arten von Polymorphismus:

  • Coercion: Automatische Umwandlung von Datentypen, um Kompatibilität zu gewährleisten.
  • Overloading: Verwendung desselben Methodennamens/ Operators für verschiedene Aufgaben basierend auf Argumenten/ Datentypen.
  • Parametrischer Polymorphismus: Nutzung von Variablen/ Methoden durch Generics.
  • Subtyp-Polymorphismus: Methodenüberschreibung in Unterklassen, wobei der Typ des Objekts bestimmt, welche Methode ausgeführt wird, auch bei Oberklassen-Referenzen.

Coercion


                
                

Overloading


                
                

Overloading


                
                

Parametrischer Polymorphismus


                
                

Subtyp-Polymorphismus


                
                

Upcasting

Upcasting ist die Umwandlung eines Objekts von einem Untertyp zu einem Obertyp. Dies geschieht in der Regel implizit und ist immer typsicher, da ein Objekt der Unterklasse immer auch eine Instanz der Oberklasse ist.

Upcasting


                
                

Downcasting

Downcasting ist die Umwandlung eines Objekts von einem Obertyp zu einem Untertyp. Dies erfordert einen expliziten Cast und kann zur Laufzeit eine ClassCastException auslösen, wenn das Objekt nicht tatsächlich eine Instanz des Zieltyps ist. Daher sollte vorher mit instanceof geprüft werden.

Downcasting


                
                

Übungen

  1. Implementiere ein Beispiel, das Polymorphismus zeigt, mit einer Methode playSound(), die je nach Typ eines Instruments (z. B. Gitarre, Klavier) unterschiedliche Sounds abspielt.
  2. Erstelle eine Oberklasse MedicalDevice mit der Methode deviceInfo().
    1. Leite eine Unterklasse BloodPressureMonitor ab, die die Methode deviceInfo() überschreibt und zusätzlich die Methode measure() enthält.
    2. Erstelle ein Objekt der Klasse BloodPressureMonitor und weise es einer Variablen vom Typ MedicalDevice zu (Upcasting).
    3. Prüfe mit dem instanceof-Operator, ob das Objekt vom Typ BloodPressureMonitor ist.
    4. Führe ein Downcasting durch und rufe die Methode measure() auf.

Ausblick

Anwendungsfelder

  • Datenverarbeitung/ DataScience
  • Webentwicklung
  • Mobile App-Entwicklung
  • Verteilte Systeme & Cloud-Anwendungen

Kommende Vorlesungen

  • Datenbanken & Persistenz
  • Verteilte Systeme und Netzwerke
  • Fortgeschrittene Programmiertechniken
  • KI & ML

Zum Thema Benamsung

Software fachlich modellieren: CRUD war gestern

Zum Thema Architektur

Weniger ist mehr: Was guten Code (und gute Architektur) kaputtmacht

Zum Thema Softwarequalität

Die Broken-Windows-Theorie: Null Toleranz für Softwarequalität
Java Duke