Skip to Content
2026SOFT | Grundlagen und OOP (1-6)

SOFT | Grundlagen und OOP (1-6)

Datum: September 2026
Lesedauer: 25 Minuten

Überarbeitet mit Claude Sonnet 5


Referenzmodul: Softwareprogrammierung (SOFT) - Dipl. Informatiker HF

Quellen:

  • Grundkurs Datenbankentwurf - Eine beispielorientierte Einführung für Studierende und Praktiker (4. Auflage)
    Helmut Jarosch, Springer Vieweg
  • Grundkurs Datenbankentwicklung - Von der Anforderungsanalyse zur komplexen Datenbankanfrage (5. Auflage)
    Stephan Kleuker, Springer Vieweg
  • Grundkurs JAVA - Von den Grundlagen bis zu Datenbank- und Netzanwendungen (12. Auflage)
    Dietmar Abts, Springer Vieweg
  • WISS Schulen für Wirtschaft Informatik Immobilien Unterricht Juni - September 2026

Block 01: Grundlagen Software Engineering

Softwareentwicklung als Prozess

Software entsteht in wiederkehrenden Arbeitsschritten. Die Grenzen sind fliessend und werden in agilen Projekten regelmässig überprüft:

  1. Problem, Zielgruppen und Anforderungen klären
  2. Lösungsarchitektur und Entwurf festlegen
  3. Implementieren und lokal prüfen
  4. Komponenten integrieren und systematisch testen
  5. Veröffentlichen und betreiben
  6. Fehler beheben, beobachten und weiterentwickeln

Eine Spezifikation beschreibt dabei das erwartete Verhalten, Randbedingungen und Qualitätsziele. Sie ist überprüfbar formuliert und dient als gemeinsame Referenz.

Scrum im Überblick

Scrum organisiert komplexe Produktentwicklung in kurzen Sprints. Der Product Owner verantwortet den Produktwert und priorisiert das Backlog. Der Scrum Master unterstützt die Zusammenarbeit und beseitigt Prozesshindernisse. Die Entwicklerinnen und Entwickler erstellen gemeinsam ein fertiges Inkrement. Fachpersonen wie Product Manager, Requirements Engineers, Business Analysts und Architektinnen liefern je nach Kontext zusätzliche Perspektiven.

Software und Programmiersprachen

Typische Softwareklassen sind:

  • Anwendungssoftware: etwa ERP-, CRM- oder Fachanwendungen
  • Middleware: Laufzeitumgebungen, Messaging und Datenbankzugriffe
  • Betriebssysteme: Desktop-, Server- und mobile Plattformen
  • Firmware: hardwarenahe Programme wie UEFI oder Gerätecode

Generationen der Programmiersprachen

GenerationSprachebeneBeispiele
1MaschinenspracheBinärcode
2AssemblerspracheAssembler
3höhere, allgemeine ProgrammiersprachenC, Java, Python
4problemorientierte beziehungsweise deklarative SprachenSQL, MATLAB
5KI-orientierte und deklarative SprachenProlog, Lisp

Die Einteilung ist historisch und nicht vollständig trennscharf. Eine zweite, für die Ausführung wichtige Sicht unterscheidet Compiler-, Intermediate-Code- und Skriptsprachen:

SprachtypÜbersetzung und AusführungBeispiele
CompilerspracheQuellcode wird in plattformspezifischen Maschinencode übersetztC, C++, Swift
Sprache mit Intermediate CodeCompiler erzeugt Zwischencode; eine Laufzeitumgebung führt ihn ausJava, C#, F#
Skript- oder InterpreterspracheLaufzeitumgebung verarbeitet den Quelltext beziehungsweise eine ZwischenformPython, JavaScript, PHP

Java verbindet dabei Quelltext, Bytecode, JAR-Archiv und JVM: javac erzeugt .class-Dateien, ein Archiv bündelt sie, und die JVM führt den Bytecode auf dem jeweiligen Betriebssystem aus. Deshalb muss für jedes Zielsystem eine passende JVM vorhanden sein.

Paradigmen als Denkmodelle

Ein Paradigma beschreibt, wie ein Problem in Code ausgedrückt wird. Die Kategorien überschneiden sich:

ParadigmaTypische FrageBeispiel
imperativWelche Schritte ändern den Zustand?Schleife in Java
deklarativWelches Ergebnis wird benötigt?SQL-Abfrage
prozeduralWelche Funktionen zerlegen den Ablauf?C-Funktion
objektorientiertWelche Objekte besitzen Zustand und Verhalten?Java-Klasse
funktionalWelche Werte werden aus anderen Werten berechnet?Stream-Pipeline
logischWelche Fakten und Regeln führen zur Lösung?Prolog-Regel
generischWie bleibt ein Algorithmus typsicher wiederverwendbar?List<T>

Strukturierte Programmierung verwendet Sequenz, Auswahl und Wiederholung. Modulare Programmierung trennt ein System in überschaubare Einheiten. AOP kapselt Querschnittsthemen wie Logging, generative Verfahren erzeugen Code aus Modellen oder Vorlagen.

Programmiersprachen können nach ihrem Paradigma beschrieben werden. Imperative und prozedurale Sprachen formulieren Abläufe. Deklarative Sprachen beschreiben ein gewünschtes Ergebnis. Objektorientierte Programme bündeln Zustand und Verhalten in Objekten, funktionale Programme arbeiten bevorzugt mit Funktionen und unveränderlichen Werten. Generische, modulare, aspektorientierte und generative Ansätze ergänzen diese Sichtweisen. Java verbindet insbesondere imperative, objektorientierte, generische und funktionale Elemente.

Algorithmisches Denken

Ein Algorithmus ist ein endlicher, ausführbarer und eindeutig beschriebener Lösungsweg. Er verarbeitet definierte Eingaben und erzeugt definierte Ausgaben. Für die Beurteilung sind zusätzlich Terminierung, Effektivität, Schrittfolge und die Übertragbarkeit auf eine ganze Problemklasse wichtig.

Informationsebenen und Zeichen

EbeneFrageBeispiel
BedeutungWas soll die Information aussagen?eine E-Mail-Adresse
SyntaxWie ist sie strukturiert?JSON oder CSV
CodierungWie wird sie als Bitfolge dargestellt?UTF-8

ASCII ordnet 128 Zeichen siebenstelligen Bitmustern zu. Unicode definiert Zeichen unabhängig von einer bestimmten Sprache; UTF-8 codiert sie mit unterschiedlich vielen Bytes. Zeichensatz und Zeichencodierung sind daher nicht dasselbe.

Softwarearchitektur

Architektur beschreibt die wesentlichen Bausteine eines Systems, ihre Verantwortlichkeiten und ihre Beziehungen. Eine gute Architektur macht Entscheidungen sichtbar, begrenzt Abhängigkeiten und unterstützt Qualitätsziele wie Änderbarkeit, Sicherheit und Verfügbarkeit.

Ein Softwarearchitekt klärt Anforderungen, entwirft Strukturen, bewertet Risiken, kommuniziert Leitentscheide und begleitet die Umsetzung. Komponenten sind austauschbare Einheiten mit klarer Schnittstelle. Das Prinzip Separation of Concerns trennt Verantwortlichkeiten, sodass eine fachliche Änderung möglichst wenige Bereiche berührt.

Betriebsmodelle

ModellLangformBeispiele
IaaSInfrastructure as a ServicevSphere, OpenStack
PaaSPlatform as a ServiceWindows Azure
SaaSSoftware as a ServiceGmail, Salesforce, SAP
CaaSContainer as a ServiceDocker, Kubernetes
FaaSFunction as a ServiceAzure Functions

Layer und Tier

Ein Layer ist eine logische Verantwortlichkeit innerhalb der Software. Ein Tier ist eine physisch getrennte Ausführungseinheit. Eine Anwendung kann daher drei Layer besitzen, aber auf zwei Tiers laufen.

Bei einer 2-Tier-Lösung kommuniziert der Client direkt mit einem Server. Eine 3-Tier-Lösung trennt Benutzeroberfläche, Anwendungslogik und Datenhaltung. N-Tier erweitert diese Aufteilung beispielsweise um Webserver, Cache oder spezialisierte Dienste.

Typische Tier-Varianten

Beim Fat Client liegen Präsentation und ein grosser Teil der Logik auf dem Client. Der Server stellt hauptsächlich Daten oder zentrale Dienste bereit. Das kann die Bedienung beschleunigen, erschwert aber Verteilung, Updates und Schutz von Geschäftslogik.

Bei einer Webarchitektur enthält der Browser vor allem Darstellung und Interaktion. Fachlogik und Datenzugriff liegen überwiegend auf Servern. Das vereinfacht zentrale Aktualisierungen, macht aber Netzwerk, Serverbetrieb und Verfügbarkeit wichtig.

Weitere Architekturstile sind ereignisgesteuerte Systeme, Microkernel, Microservices, Space-based Architecture und Serverless. Die Auswahl folgt Anforderungen, nicht einem Modewort. Verteilung kann Ressourcen teilen und Last verteilen, erhöht aber Kommunikations-, Betriebs- und Ausfallkomplexität.

Verteilte Systeme

Verteilung betrifft drei Fragen: Wie kommunizieren Teile miteinander, wie laufen sie parallel, und wie werden gemeinsame Daten synchronisiert? RPC und Middleware verbergen Teile der Netzwerkkommunikation. Synchronität bedeutet, dass der Aufrufer wartet; asynchrone Kommunikation arbeitet mit Nachrichten, Callbacks oder Polling. Dauerhafte Speicherung muss zusätzlich gegen Ausfälle und konkurrierende Zugriffe geschützt werden.

Die wichtigsten Architekturentscheidungen sind deshalb ein Ausgleich zwischen Einfachheit, Performance, Skalierbarkeit, Verfügbarkeit und Wartbarkeit. Ein lokaler Prozess ist oft schneller und leichter zu testen. Gemeinsame Server, Daten und Funktionen rechtfertigen Verteilung nur, wenn ihr Nutzen die zusätzliche Komplexität überwiegt.

Block 02: Java-Grundlagen

Herkunft und Plattform

Java entstand bei Sun Microsystems aus dem Projekt Oak und wurde 1995 veröffentlicht. Oracle übernahm Sun 2010. James Gosling prägte die Sprache wesentlich. Java verfolgt Objektorientierung, Portabilität und eine umfangreiche Standardbibliothek.

Der Quelltext wird in Bytecode übersetzt. Eine Java Virtual Machine (JVM) führt diesen Bytecode auf der jeweiligen Plattform aus. Dadurch bleibt derselbe Build auf unterschiedlichen Betriebssystemen nutzbar, sofern eine passende JVM vorhanden ist.

Java-Plattformen

  • Java SE: Sprache, Standardbibliothek, Desktop- und Konsolenprogramme
  • Jakarta EE (früher Java EE): serverseitige Anwendungen und Unternehmensdienste
  • Java ME: reduzierte Laufzeit für eingebettete Geräte

Der Compiler übersetzt jede .java-Datei in eine .class-Datei. Das Java-Archivwerkzeug jar bündelt Klassen, Ressourcen und Metadaten zu einem JAR. Beim Deployment wird dieses Archiv auf ein Zielsystem kopiert; die dortige JVM lädt und verifiziert den Bytecode und führt ihn aus.

Andere JVM-Sprachen wie Scala oder Groovy können ebenfalls Bytecode erzeugen. Die JVM bildet damit eine gemeinsame Laufzeitumgebung für verschiedene Quellsprachen. Die Portabilität gilt für den Bytecode; betriebssystemabhängige Bibliotheken oder native Aufrufe können sie einschränken.

Namensregeln und Programmstart

Pakete werden kleingeschrieben. Klassen, Interfaces und Enums beginnen mit einem Grossbuchstaben. Methoden und Variablen beginnen mit einem Kleinbuchstaben und verwenden CamelCase. Konstanten stehen meist in UPPER_SNAKE_CASE.

public class Main { public static void main(String[] args) { System.out.println("Hello, World!"); } }

Typen, Variablen und Literale

Primitive Typen speichern Werte direkt: boolean, char, byte, short, int, long, float und double. String ist dagegen ein unveränderliches Referenzobjekt. Für Geldbeträge ist BigDecimal meist geeigneter als double.

TypSpeicherTypischer Wertebereich oder Inhalt
booleannicht festgelegttrue oder false
byte8 Bit-128 bis 127
short16 Bit-32’768 bis 32’767
int32 Bit-2^31 bis 2^31 - 1
long64 Bit-2^63 bis 2^63 - 1
float32 BitGleitkommazahl mit einfacher Genauigkeit
double64 BitGleitkommazahl mit doppelter Genauigkeit
char16 BitUTF-16-Codeeinheit

Ganzzahlliterale sind standardmässig int; ein Suffix L kennzeichnet long, F kennzeichnet float. Gleitkommazahlen sind standardmässig double. Ein char verwendet einfache, ein String doppelte Anführungszeichen.

int count = 12; long population = 9_000_000L; double ratio = 0.75; boolean active = true; char initial = 'L'; String label = "Example";

Zahlensysteme und Umrechnungen

ZahlensystemBasiserlaubte ZiffernJava-Schreibweise
Dezimal100 bis 942
Binär20, 10b101010
Oktal80 bis 7052
Hexadezimal160 bis 9, A bis F0x2A

Stellenwertprinzip

Der Wert einer Zahl ist die Summe aller Ziffern multipliziert mit ihrer Basis und der jeweiligen Potenz. Die Position ganz rechts hat den Exponenten 0.

1068 = 1 * 10^3 + 0 * 10^2 + 6 * 10^1 + 8 * 10^0 1011_2 = 1 * 2^3 + 0 * 2^2 + 1 * 2^1 + 1 * 2^0 = 11_10 142_8 = 1 * 8^2 + 4 * 8^1 + 2 * 8^0 = 98_10 3A_16 = 3 * 16^1 + 10 * 16^0 = 58_10

Umrechnungsregeln

  • Von einer Basis nach Dezimal: Stellenwerte berechnen und addieren.
  • Von Dezimal nach einer Basis: wiederholt durch die Zielbasis teilen; Reste von unten nach oben lesen.
  • Binär nach Hexadezimal: von rechts in Vierergruppen teilen und jede Gruppe als Hexadeziffer lesen.
  • Binär nach Oktal: von rechts in Dreiergruppen teilen und jede Gruppe als Oktalziffer lesen.
AB10_16 = 1010 1011 0001 0000_2 732_8 = 111 011 010_2 22_10 = 10110_2

Mit $n$ Bit lassen sich $2^n$ verschiedene Muster darstellen. Bei vier Bit reichen die Muster von 0000 bis 1111, also von 0 bis 15 ohne Vorzeichen.

Bitoperationen

int value = 8; // 0000 1000 int left = value << 1; // 0001 0000 = 16 int right = value >> 1; // 0000 0100 = 4

Eine Linksverschiebung entspricht bei passenden positiven Ganzzahlen einer Multiplikation mit 2, eine Rechtsverschiebung einer Division durch 2. Überläufe, Vorzeichen und der Unterschied zwischen >> und >>> müssen dabei berücksichtigt werden.

Seit Java 10 kann var bei lokalen Variablen verwendet werden. Der Compiler leitet den statischen Typ aus dem Initialwert ab; eine dynamische Typisierung entsteht dadurch nicht.

var name = "Lina"; // String var items = 3; // int var student = new Student("Lina");

Ein Cast fordert eine explizite Umwandlung. Bei primitiven Typen kann dabei Genauigkeit verloren gehen. Bei Referenzen prüft der Cast die tatsächliche Objektart zur Laufzeit.

double price = 27.95; int roundedDown = (int) price; long exactCount = 20; int countFromLong = (int) exactCount;

Zeichen und Escape-Sequenzen

SequenzBedeutungBeispielwirkung
\bRückschrittZeichen vor dem Cursor löschen
\tTabulatorSpalten ausrichten
\nneue ZeileLine Feed
\fSeitenvorschubForm Feed
\rWagenrücklaufCarriage Return
\"doppeltes Anführungszeichen" im String
\'einfaches Anführungszeichen' im Zeichenliteral
\\Backslash\ im String
\dddZeichen über oktalen Wertbis zu drei Oktalziffern
\uddddUnicode-Zeichenvier Hexadezimalziffern
String line = "Name:\tMira\nStatus:\tactive"; char copyright = '\u00A9';

Weitere Codierungen

Neben ASCII und Unicode werden in Spezialgebieten weitere Darstellungen verwendet. EBCDIC ist eine historische IBM-Codierung. Ein Quaternärcode nutzt vier Zustände, beispielsweise die DNA-Basen A, C, G und T; er ist kein allgemeiner Ersatz für UTF-8. Bei Datenübertragung müssen Sender und Empfänger dieselbe Codierung vereinbaren.

Strings

Strings werden inhaltlich mit equals verglichen, nicht mit ==. null, der leere String und ein String mit Leerzeichen beschreiben drei verschiedene Zustände.

String text = "some content"; String part = text.substring(5); boolean matching = text.equals("some content"); boolean present = text.contains("content"); int length = text.length(); String formatted = String.format("%s: %d", "Items", 3);

StringBuilder eignet sich für viele Änderungen an einer Zeichenkette. StringBuffer bietet zusätzlich Synchronisation und ist deshalb in vielen Situationen langsamer.

MethodeWirkung
length()aktuelle Zeichenanzahl
append(value)Wert anhängen
insert(index, value)Wert an Position einfügen
delete(start, end)Bereich [start, end) entfernen
replace(start, end, value)Bereich ersetzen
StringBuilder builder = new StringBuilder("Hello"); builder.append(" Java").insert(5, ','); String result = builder.toString();

StringTokenizer ist eine ältere Token-API. Für neue Programme sind split, Scanner oder ein Parser häufig klarer.

StringTokenizer tokenizer = new StringTokenizer("red,green,blue", ","); while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); System.out.println(token); }

Ein- und Ausgabe

Scanner liest beispielsweise Konsoleneingaben. Ausgaben erfolgen über System.out; printf unterstützt Formatmuster wie %d, %f, %s und %n.

Methode oder MusterZweck
next()nächstes Wort lesen
nextLine()ganze Zeile lesen
nextInt()Ganzzahl lesen
nextDouble()Gleitkommazahl lesen
%dGanzzahl formatieren
%fGleitkommazahl formatieren
%.2fGleitkommazahl mit zwei Nachkommastellen
%sString formatieren
%nplattformunabhängiger Zeilenwechsel
import java.util.Scanner; Scanner scanner = new Scanner(System.in); System.out.print("Name: "); String name = scanner.nextLine(); System.out.printf("Hello, %s%n", name);

Operatoren und Kontrollfluss

Arithmetische Operatoren sind +, -, *, / und %. Vergleiche verwenden <, <=, >, >=, == und !=. Für logische Ausdrücke sind !, &&, || und ^ üblich. && und || werten den rechten Ausdruck nur bei Bedarf aus.

if (score >= 50 && passed) { System.out.println("Passed"); } else { System.out.println("Retry"); }

switch wählt anhand eines Werts einen Zweig. Moderne Varianten können einen Wert direkt liefern:

String message = switch (level) { case 1 -> "Low"; case 2, 3 -> "Medium"; default -> "High"; };

for und while prüfen die Bedingung vor dem Durchlauf. do-while führt den Rumpf mindestens einmal aus. break beendet die umgebende Schleife, continue überspringt den restlichen aktuellen Durchlauf. Beschriftete Sprünge sollten wegen ihrer erschwerten Lesbarkeit selten verwendet werden.

Auswertungsregeln

Operatoren werden nach Priorität ausgewertet. Klammern machen die Absicht sichtbar und vermeiden Fehler. Bei int-Division wird der Nachkommateil abgeschnitten; eine Division durch null löst bei Ganzzahlen ArithmeticException aus. ++ und -- verändern eine Variable und sollten nicht in verschachtelten Ausdrücken versteckt werden.

int whole = 7 / 2; // 3 double exact = 7.0 / 2; // 3.5 boolean valid = value >= 0 && value <= 100; int absolute = value < 0 ? -value : value;

Die bitweisen Operatoren &, |, ^ und ~ arbeiten auf Bits. && und || sind dagegen logische Operatoren mit Kurzschlussauswertung. Der bedingte Operator condition ? whenTrue : whenFalse eignet sich für kurze Wertausdrücke, nicht für komplexe Abläufe.

Zuweisungen können mit einem Operator kombiniert werden: total += amount entspricht total = total + amount. Das gilt auch für -=, *=, /=, %= sowie bitweise und verschiebende Operatoren. Die Zuweisung hat eine niedrigere Priorität als die meisten Rechen- und Vergleichsoperatoren.

Aussagenlogik und boolesche Algebra

Eine Aussage ist ein Satz mit genau einem Wahrheitswert: wahr (W, in Java true) oder falsch (F, in Java false). Eine Aussageform enthält Variablen und wird erst nach deren Belegung zu einer konkreten Aussage.

SymbolNameJava
¬pNegation!p
p ∧ qKonjunktionp && q
p ∨ qDisjunktionp || q
p ⊕ qexklusives Oderp ^ q
p → qImplikation!p || q
p ↔ qÄquivalenzp == q

Wahrheitstabelle

pq¬pp ∧ qp ∨ qp → qp ↔ q
WWFWWWW
WFFFWFF
FWWFWWF
FFWFFWW

Die Implikation p → q ist nur dann falsch, wenn p wahr und q falsch ist. Die Äquivalenz ist wahr, wenn beide Aussagen denselben Wahrheitswert besitzen. In Java ist && nicht dasselbe wie &: Der logische Operator arbeitet mit Kurzschluss, der bitweise Operator wertet beide Seiten aus.

Grundgesetze

GesetzFormeln
Kommutativitätp ∧ q = q ∧ p; p ∨ q = q ∨ p
Distributivitätp ∧ (q ∨ r) = (p ∧ q) ∨ (p ∧ r)
Neutralitätp ∧ W = p; p ∨ F = p
Komplementp ∧ ¬p = F; p ∨ ¬p = W
Idempotenzp ∧ p = p; p ∨ p = p
Doppelnegation¬(¬p) = p
De Morgan¬(p ∧ q) = ¬p ∨ ¬q; ¬(p ∨ q) = ¬p ∧ ¬q

Grundbegriffe der Mathematik

Ein Term ist ein Ausdruck ohne Wahrheitsbehauptung, etwa 2x + 1. Eine Aussage ist beispielsweise 2 + 3 = 5. Eine Aussageform wie 2x + 1 = 12 enthält noch eine Variable.

Das Summenzeichen Σ fasst Werte zusammen, das Produktzeichen Π multipliziert sie:

Σ(i = 1..5) i = 15 Π(i = 1..4) i = 24

Für eine positive ganze Zahl ist n! = n * (n - 1) * ... * 1; zusätzlich gilt 0! = 1. Fakultäten und Summen treten häufig bei Laufzeit- und Kombinatorikaufgaben auf.

Arrays

Arrays enthalten Elemente eines festen Typs und haben nach der Erstellung eine unveränderliche Länge. Der erste Index ist 0.

int[] scores = {3, 5, 8}; scores[1] = 6; System.out.println(scores[scores.length - 1]); int[][] matrix = {{1, 2}, {3, 4}}; System.out.println(matrix[1][0]);

Für eine veränderliche Anzahl von Elementen wird etwa ArrayList<Integer> eingesetzt. Die Hilfsklasse Arrays bietet unter anderem sort, copyOf, equals, fill, toString und binarySearch; für die binäre Suche muss das Array sortiert sein.

Ein Array kann nicht mit array.length = ... resized werden. Ein Index ausserhalb 0 bis length - 1 führt zu ArrayIndexOutOfBoundsException. Mehrdimensionale Arrays sind Arrays von Arrays und dürfen deshalb auch unterschiedlich lange Zeilen enthalten.

Block 03: Objektorientierung mit Java I

Modellierung mit UML

UML beschreibt Struktur und Verhalten grafisch. Klassendiagramme zeigen Typen und Beziehungen, Objektdiagramme konkrete Instanzen, Komponentendiagramme Bausteine und Schnittstellen. Aktivitäts-, Zustands-, Anwendungsfall- und Sequenzdiagramme beschreiben Abläufe und Interaktionen.

Eine Klasse ist ein Bauplan. Ein Objekt ist eine konkrete Instanz dieses Typs. Attribute beschreiben Zustand, Methoden beschreiben Verhalten. Konstruktoren initialisieren neue Objekte; Java besitzt keinen manuell aufrufbaren Destruktor, da die Speicherfreigabe durch den Garbage Collector erfolgt.

public class Student { private final String name; public Student(String name) { this.name = name; } public String getName() { return name; } } Student first = new Student("Mira"); Student second = new Student("Mira"); boolean sameObject = first == second; // false

Methoden und Kapselung

Eine Methodensignatur umfasst Namen und Parametertypen. Der Rückgabetyp allein unterscheidet keine überladenen Methoden. Parameter sind lokale Variablen. private hält Implementierungsdetails verborgen, public bildet die externe Schnittstelle.

public class PriceCalculator { public double gross(double net, double taxRate) { return net * (1 + taxRate); } public int add(int left, int right) { return left + right; } public double add(double left, double right) { return left + right; } }

Statische Member gehören zur Klasse und werden über den Klassennamen angesprochen. Instanzmember gehören jeweils zu einem Objekt. Eine statische Methode kann nicht direkt auf Instanzzustand zugreifen. Ein static-Block wird beim Initialisieren der Klasse einmal ausgeführt.

public class MathTools { public static final double PI_APPROXIMATION = 3.14159; public static int square(int value) { return value * value; } } int result = MathTools.square(5);

Varargs verpackt beliebig viele Argumente des angegebenen Typs in ein Array. Der Varargs-Parameter muss am Ende stehen.

public static int sum(int... values) { int total = 0; for (int value : values) { total += value; } return total; }

Rekursion benötigt einen Basisfall und einen Schritt, der das Problem verkleinert. Jeder Aufruf beansprucht Stack-Speicher.

public static long factorial(int value) { if (value <= 1) { return 1; } return value * factorial(value - 1); }

Ein record ist eine kompakte Deklaration für überwiegend unveränderliche Datenträger. Komponenten, Konstruktor, Accessoren sowie equals, hashCode und toString werden bereitgestellt.

public record Point(int x, int y) {} Point origin = new Point(0, 0); int x = origin.x();

Sichtbarkeit und Zustand

Java unterscheidet vier Sichtbarkeitsstufen: public ist überall zugänglich, protected im Paket und in Unterklassen, ein Member ohne Modifizierer nur im Paket und private nur in der deklarierenden Klasse. Felder werden in robusten Klassen meist private gehalten. Validierung erfolgt beim Erzeugen oder über Methoden, damit ein Objekt keine ungültigen Zustände annehmen kann.

final verhindert eine zweite Zuweisung an eine Variable. Bei einer Referenz ist das Objekt selbst dadurch nicht automatisch unveränderlich. static final beschreibt eine klassenweite Konstante; eine gewöhnliche Instanzvariable existiert pro Objekt.

public class Counter { private static int created; private int value; public Counter() { created++; } public void increment() { value++; } public static int createdCount() { return created; } }

Eine Methode kann überladen werden, wenn ihre Parameterliste unterschiedlich ist. Überschreiben bedeutet dagegen, dass eine Unterklasse dieselbe Signatur mit einer spezialisierten Implementierung versieht. Diese beiden Begriffe dürfen nicht verwechselt werden.

Pakete und Module

Pakete ordnen Klassen und vermeiden Namenskonflikte. Die umgekehrte Domain-Schreibweise ist eine verbreitete Konvention, etwa ch.example.application. Klassen aus anderen Paketen werden importiert; java.lang wird automatisch importiert.

EinheitZweck
PaketNamensraum für zusammengehörigen Code
Bibliothekausgelieferte Sammlung, oft als JAR
ModulPakete mit expliziten Abhängigkeiten und Exporten

Ein statischer Import macht ausgewählte statische Member ohne Klassennamen verfügbar. Er sollte sparsam eingesetzt werden, damit die Herkunft eines Aufrufs erkennbar bleibt.

Die Verzeichnisstruktur folgt normalerweise dem Paketnamen. Eine Datei darf nur eine öffentliche Top-Level-Klasse mit passendem Dateinamen enthalten. Importiert werden konkrete Typen oder Pakete; import package.* importiert keine Unterpakete und keine statischen Member.

Block 04: Objektorientierung mit Java II

Entwurf guter Klassen

Gute Klassen haben eine klar erkennbare Verantwortung, wenig Wissen über andere Klassen und eine kleine, stabile Schnittstelle. Hohe Kohäsion bündelt zusammengehörige Aufgaben. Lose Kopplung begrenzt die Auswirkungen von Änderungen. Kapselung schützt Invarianten und erlaubt, die Implementierung hinter einer Schnittstelle auszutauschen.

Refactoring verändert die interne Struktur, ohne das beobachtbare Verhalten zu erweitern. Tests sichern dabei die bestehende Funktionalität. Neue Anforderungen sind ein eigener Änderungsgrund.

Vererbung

Vererbung modelliert eine echte Ist-ein-Beziehung. Gemeinsame Eigenschaften liegen in einer Oberklasse, Spezialisierungen in Unterklassen. Jede Java-Klasse erbt letztlich von Object.

public class Submission { private final String author; public Submission(String author) { this.author = author; } public String getAuthor() { return author; } } public class PhotoSubmission extends Submission { private final String fileName; public PhotoSubmission(String author, String fileName) { super(author); this.fileName = fileName; } }

Ein Unterklassenkonstruktor ruft zuerst super(...) auf; dieser Aufruf steht immer am Anfang des Konstruktors. Ohne expliziten Aufruf versucht Java super() zu verwenden, was nur bei einer passenden parameterlosen Oberklassenkonstruktion gelingt. Zugängliche Methoden der Oberklasse (public oder protected) lassen sich in einer Unterklasse an beliebiger Stelle mit super.methodenName(...) aufrufen. private-Member bleiben auch Unterklassen verborgen. protected ist für Unterklassen und Paketmitglieder sichtbar, sollte aber nicht unbedacht zur öffentlichen Kopplung werden.

Eine Zwischenklasse ist sinnvoll, wenn nur ein Teil der Untertypen eine Fähigkeit teilt. Noch besser passt oft ein Interface oder Komposition, wenn die Beziehung keine echte Spezialisierung ausdrückt.

UML-Beziehungen

Eine Generalisierung wird mit einer durchgezogenen Linie und einem hohlen Dreieck dargestellt. Die Realisierung eines Interfaces verwendet eine gestrichelte Linie. Eine ausgefüllte Raute steht für Komposition, eine leere Raute für Aggregation. Multiplizitäten wie 1, 0..1 und * beschreiben, wie viele Objekte beteiligt sind.

Polymorphie und Überschreiben

Eine Variable eines Supertyps kann ein Objekt eines Subtyps referenzieren. Der statische Typ bestimmt, welche Member der Compiler anbietet; der dynamische Typ bestimmt, welche überschriebene Instanzmethode läuft.

Submission submission = new PhotoSubmission("Mira", "photo.jpg"); System.out.println(submission.getAuthor());

Mit @Override prüft der Compiler, ob wirklich eine geerbte Methode ersetzt wird. instanceof testet die Laufzeitart. Pattern Matching verbindet Prüfung und lokale Variable.

if (submission instanceof PhotoSubmission photo) { System.out.println(photo); }

Ein Aufwärts-Cast ist implizit. Ein Abwärts-Cast benötigt eine Prüfung und kann sonst eine ClassCastException auslösen. Unverträgliche Typen weist der Compiler zurück.

Object-Vertrag und Kopien

Object liefert unter anderem toString, equals und hashCode. == prüft bei Referenzen Identität. equals soll fachliche Gleichheit ausdrücken. Wird equals überschrieben, muss auch hashCode konsistent überschrieben werden, damit HashMap und HashSet korrekt arbeiten.

@Override public boolean equals(Object other) { if (this == other) { return true; } if (!(other instanceof Student student)) { return false; } return name.equals(student.name); } @Override public int hashCode() { return name.hashCode(); }

clone erzeugt bei korrekter Implementierung zunächst eine flache Kopie. Referenzfelder zeigen danach noch auf dieselben Objekte. Eine tiefe Kopie muss verschachtelte, veränderliche Objekte ebenfalls kopieren. In neuem Code sind Kopierkonstruktoren oder Fabrikmethoden oft verständlicher.

Der Vertrag von equals verlangt Reflexivität, Symmetrie, Transitivität, Konsistenz und ein Ergebnis ungleich true für null. Hash-basierte Collections vergleichen bei gleichem Hashwert zusätzlich mit equals. Deshalb dürfen relevante Schlüsselattribute nach dem Einfügen nicht verändert werden. toString ist für Diagnose gedacht und kein stabiler Austauschvertrag.

Ausgewählte Standardklassen

Wrapper wie Integer, Double und Boolean repräsentieren primitive Werte als Objekte. Autoboxing und Unboxing wandeln automatisch um; bei null kann das Unboxing eine NullPointerException auslösen.

Properties speichert einfache Schlüssel-Wert-Konfigurationen als Strings. Class<?> repräsentiert einen Typ, etwa über object.getClass() oder Student.class. Math bietet statische Funktionen wie abs, min, max, sqrt, pow, sin und random sowie PI und E.

Ältere Typen wie Vector, Hashtable und Enumeration existieren weiterhin, sind aber für neuen Code meist nicht die erste Wahl. Apache Commons ergänzt das JDK mit Bibliotheken für konkrete Aufgaben. Abhängigkeiten sollten immer hinsichtlich Version, Lizenz und Sicherheitsmeldungen geprüft werden.

Properties

username=admin language=de
Properties properties = new Properties(); try (InputStream input = Files.newInputStream(Path.of("config.properties"))) { properties.load(input); } String language = properties.getProperty("language", "en");

Class und Arrays

AufgabeBeispiel
Laufzeittyp ermittelnobject.getClass()
Klassentyp referenzierenStudent.class
einfachen Klassennamen lesentype.getSimpleName()
Array ausgebenArrays.toString(values)
Array sortierenArrays.sort(values)
Array füllenArrays.fill(values, 0)
Array kopierenArrays.copyOf(values, values.length)
Arrays vergleichenArrays.equals(first, second)
binär suchenArrays.binarySearch(values, target)

binarySearch setzt ein nach derselben Ordnung sortiertes Array voraus. Für Objekt-Arrays wird eine natürliche Ordnung oder ein Comparator benötigt.

Math und verwandte Klassen

BereichMethoden oder Klassen
Beträge und Grenzenabs, min, max, signum
Rundungceil, floor, round
Potenzen und Wurzelnsqrt, cbrt, pow, hypot
Exponentialfunktionenexp, log, log10
Trigonometriesin, cos, tan, asin, acos, atan
weitere TypenDecimalFormat, Random, BigInteger

Datum und Uhrzeit

Die API java.time ist unveränderlich und thread-sicher. LocalDate, LocalTime und LocalDateTime enthalten keine Zeitzone. ZonedDateTime berücksichtigt eine Zone, Instant einen UTC-Zeitpunkt. Period beschreibt Datumsdifferenzen, Duration Zeitdifferenzen.

import java.time.LocalDate; import java.time.format.DateTimeFormatter; LocalDate date = LocalDate.of(2026, 8, 27); String output = date.format(DateTimeFormatter.ofPattern("dd.MM.yyyy"));

Block 05: Objektorientierung mit Java III

Interfaces und abstrakte Klassen

Ein Interface beschreibt einen Vertrag. Eine Klasse kann mehrere Interfaces implementieren, aber nur eine Klasse erweitern. Abstrakte Klassen können gemeinsamen Zustand und Verhalten bereitstellen, erzeugen aber keine direkten Instanzen.

Interfaces modellieren Fähigkeiten unabhängig von einer konkreten Klassenhierarchie. Eine implementierende Methode muss den Vertrag mindestens mit public erfüllen. Interfaces dürfen andere Interfaces erweitern. Treffen gleichnamige Default-Methoden zusammen, muss die konkrete Klasse die Mehrdeutigkeit auflösen.

public interface CellRule { boolean isActive(Field field); default boolean isInactive(Field field) { return !isActive(field); } static CellRule alwaysActive() { return field -> true; } }

Eine abstrakte Methode besitzt keinen Rumpf und muss in konkreten Unterklassen implementiert werden. default-Methoden enthalten eine Standardimplementierung. static-Methoden gehören dem Interface und werden über dessen Namen aufgerufen. Interface-Konstanten sind implizit public static final.

MerkmalAbstrakte KlasseInterface
Konstruktormöglichnicht vorhanden
Instanzzustandmöglichkeine Instanzfelder
Mehrere Typennur eine Oberklassemehrere Interfaces
Gemeinsamer Codebeliebige konkrete Methodendefault und static
Hauptzweckgemeinsame BasisFähigkeit oder Vertrag

Vererbung oder Delegation

Vererbung passt zu einer stabilen Ist-ein-Beziehung. Delegation beschreibt, dass ein Objekt eine Aufgabe an ein anderes Objekt weitergibt. Delegation stärkt Austauschbarkeit und Kapselung und ist häufig die bessere Wahl für optionale Fähigkeiten.

Nach dem Liskov-Substitutionsprinzip muss ein Subtyp die Zusagen des Supertyps einhalten. Er darf etwa keine strengeren Vorbedingungen verlangen oder erwartete Ergebnisse verletzen. Tiefe Hierarchien erhöhen die Kopplung und brauchen deshalb eine fachliche Begründung.

public class Report { private final Formatter formatter; private final Printer printer; public Report(Formatter formatter, Printer printer) { this.formatter = formatter; this.printer = printer; } public void print(String text) { printer.print(formatter.format(text)); } }

Geschachtelte Klassen

Eine static geschachtelte Klasse benötigt keine äussere Instanz. Eine nicht-statische innere Klasse besitzt implizit eine Referenz auf das umgebende Objekt. Lokale und anonyme Klassen sind auf einen kleinen Gültigkeitsbereich begrenzt. Für einfache Funktionsverträge ist ein Lambda meist lesbarer.

public class Person { private final String name; public Person(String name) { if (!Validator.isValid(name)) { throw new IllegalArgumentException("Name is required"); } this.name = name; } public static class Validator { public static boolean isValid(String value) { return value != null && !value.isBlank(); } } }

Debugging mit IntelliJ IDEA

Haltepunkte stoppen die Ausführung an einer interessanten Stelle. Im Debugger lassen sich Variablen, Aufrufstapel und Threads untersuchen. Step Over führt die aktuelle Zeile aus, Step Into öffnet einen aufgerufenen Methodenrumpf und Step Out beendet die aktuelle Methode.

Block 06: Objektorientierung mit Java IV

Konstanten und Enums

final verhindert eine erneute Zuweisung. Eine Konstante ist häufig zusätzlich static, wenn alle Instanzen denselben Wert teilen. Konstante Namen werden grossgeschrieben.

public class Physics { public static final double EARTH_GRAVITY = 9.81; }

Ein enum beschreibt eine abgeschlossene Menge benannter Werte. Die Werte werden mit == verglichen. Ein Enum kann Felder, Konstruktoren und Methoden besitzen.

public enum Status { NEW("New"), DONE("Done"); private final String label; Status(String label) { this.label = label; } public String label() { return label; } }

Defensive Programmierung und Logging

Eingaben werden an Systemgrenzen validiert. Ungültige Zustände werden früh abgewiesen, Fehlermeldungen erklären Ursache und Korrektur. Sicherheitsrelevante und betriebliche Ereignisse gehören in ein Log, nicht in eine zufällige Konsolenausgabe.

import java.util.logging.Logger; public class AccountService { private static final Logger logger = Logger.getLogger(AccountService.class.getName()); public void open(String owner) { if (owner == null || owner.isBlank()) { throw new IllegalArgumentException("Owner is required"); } logger.info("Account opened"); } }

java.util.logging kennt unter anderem FINE, INFO, WARNING und SEVERE. Handler und Log-Level werden zentral konfiguriert. Geheimnisse und unnötige personenbezogene Daten dürfen nicht geloggt werden.

Ein Datei-Handler kann Meldungen dauerhaft speichern. Der zweite Parameter von FileHandler aktiviert den Append-Modus. Formatter bestimmen das Ausgabeformat.

private static void configureLogging() { try { FileHandler handler = new FileHandler("logs/application.log", true); handler.setFormatter(new SimpleFormatter()); logger.addHandler(handler); logger.setLevel(Level.ALL); handler.setLevel(Level.ALL); } catch (IOException exception) { logger.log(Level.SEVERE, "Could not configure file logging", exception); } }

Exceptions

Throwable teilt sich in Error und Exception. Checked Exceptions müssen behandelt oder mit throws weitergegeben werden. Unchecked Exceptions stammen von RuntimeException und weisen häufig auf fehlerhafte Vorbedingungen hin. Eigene Ausnahmen modellieren fachliche Fehler.

public class InvalidAgeException extends Exception { public InvalidAgeException(int age) { super("Age is outside the allowed range: " + age); } } public void setAge(int age) throws InvalidAgeException { if (age < 0 || age > 130) { throw new InvalidAgeException(age); } }

try überwacht Code, catch behandelt passende Fehler und finally eignet sich für Aufräumarbeiten. Für Ressourcen ist try-with-resources vorzuziehen.

try (var reader = Files.newBufferedReader(path)) { String line = reader.readLine(); } catch (IOException exception) { logger.log(Level.WARNING, "Could not read file", exception); }

Spezifische catch-Typen stehen vor allgemeineren. Multi-Catch bündelt gleichartig behandelte Typen. Eine Ausnahme sollte entweder sinnvoll behandelt oder mit Kontext weitergereicht werden, nicht bloss verschluckt werden.

Eine Ausnahmehierarchie sollte fachliche und technische Ursachen unterscheidbar machen. Error steht typischerweise für schwerwiegende JVM-Probleme und wird nicht als normale Eingabeabweichung behandelt. RuntimeException eignet sich für verletzte Programmvorbedingungen; checked Exceptions passen zu erwartbaren externen Fehlern wie fehlgeschlagenem Datei- oder Datenbankzugriff.

JOptionPane zeigt in Desktop-Anwendungen Meldungs-, Bestätigungs- und Eingabedialoge. Der Rückgabewert von showOptionDialog ist der Index der gewählten Option oder CLOSED_OPTION. In einer Schichtarchitektur gehört die Entscheidung über die Darstellung in die UI-Schicht, nicht in die Domänenlogik.

JOptionPane.showMessageDialog( null, "Could not save the file", "Error", JOptionPane.ERROR_MESSAGE); Object[] options = {"Yes", "No", "Wait"}; int choice = JOptionPane.showOptionDialog( null, "Exit the application?", "Confirmation", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[2]);

Entwurfsprinzipien

KISS reduziert unnötige Komplexität. DRY verhindert widersprüchliche Mehrfachdefinitionen. YAGNI hält nicht benötigte Erweiterungen aus dem aktuellen Design heraus.

PrinzipLeitfrage
Single ResponsibilityGibt es genau einen Grund, diese Klasse zu ändern?
Open/ClosedKann Verhalten ergänzt werden, ohne stabile Teile zu verändern?
Liskov SubstitutionBleibt der Subtyp überall verträglich?
Interface SegregationMüssen Implementierungen unpassende Methoden anbieten?
Dependency InversionHängt die Fachlogik von Abstraktionen ab?

Dependency Injection liefert konkrete Abhängigkeiten von aussen und erleichtert Tests:

public interface OrderRepository { void save(Order order); } public class OrderService { private final OrderRepository repository; public OrderService(OrderRepository repository) { this.repository = repository; } public void save(Order order) { repository.save(order); } }

Javadoc und Architektur dokumentieren

Die öffentliche Schnittstelle umfasst Namen, Sichtbarkeit, Parameter, Rückgabewerte, Zustandsregeln und mögliche Ausnahmen. Private Felder und Hilfsmethoden gehören zur Implementierung. Javadoc erklärt diesen Vertrag direkt am Code; die Implementierung bleibt veränderbar, solange der Vertrag gilt.

/** Calculates an amount including tax. */ public double gross(double net, double taxRate) { return net * (1 + taxRate); }

Architekturdokumentation ergänzt den Code um Ziele, Randbedingungen, Kontext, Baustein- und Laufzeitsichten, Verteilung, Qualitätsanforderungen, Entscheide, Risiken und Glossar. arc42 bietet dafür eine verbreitete, technologieunabhängige Struktur.