Informatik – Zusammenfassung Unterricht: Abstrakte Klassen, Interfaces, Exceptions

Quellen (Google Classroom → 4AT Informatik → Objektorientierung)

  • Folien: „Pakete und Vererbung 2_2” (abstrakte Klassen, Interfaces), „Ausnahmenbehandlung” (Assertions, Exceptions)
  • Lehrer-Beispiele: ExceptionTest.java, ExceptionTest4.java, ExceptionTest6.java, AssertionTest.java
  • Übungen OO: OOP_Ex5 (Aufgabe 3 Flugzeuge), OOP_Ex6 (Wahlen in Südtirol), OOP_Ex7 (Exceptions + Assertions)
  • Dein Code: Informatik/Skripte/07_Wahlen, 05_Strings/03_CharacterTwice

Alle Codes und Ausgaben in dieser Notiz wurden mit Java getestet.

So lernst du damit

  1. Theorie Teil 1–5 lesen, die Merksätze aus den Folien können
  2. Teil 6: Lehrer-Beispiele: Ausgabe selbst vorhersagen, dann vergleichen
  3. Teil 7 + 8: Übungsblätter selbst in VS Code programmieren, erst dann die Lösung aufklappen
  4. Teil 10: Compilerfehler-Fallen

Teil 1 – Abstrakte Klassen und Methoden

1.1 Grundidee (Folie 1)

  • Bisher: immer konkrete Klassen, von denen man Objekte erzeugen kann
  • Das ist nicht immer erwünscht oder sinnvoll
  • Von abstrakten Klassen gibt es keine Objekte → new ergibt einen Compilerfehler
  • Oberklassen werden manchmal nur als Modellierungsklassen verwendet:
    • Sie geben nur Signaturen für die Unterklassen vor
    • Die Unterklassen implementieren die Methoden dann aus
  • Kennzeichnung mit dem Modifier abstract
  • UML: Klassenname kursiv oder {abstract} unter den Namen

Folie 1, Randnotiz

Soll nur verhindert werden, dass ein Exemplar der Klasse gebildet wird, verwendet man besser einen private oder protected Konstruktor.

1.2 Arten von abstrakten Klassen (Folie 2)

  • Abstrakte Methoden geben die Signatur vor, aber keine Implementierung:
public abstract void fahre(double speed);   // kein Rumpf, Semikolon!
ArtInhalt
Reine (pure) abstrakte Klasseenthält ausschließlich abstrakte Methoden
Partiell abstrakte Klasseenthält abstrakte und nicht abstrakte Methoden; die nicht abstrakten haben eine konkrete Implementierung

Regeln (Folie 2)

  • Ist mindestens eine Methode abstract, so ist es auch die Klasse.
  • Unterklassen von abstrakten Klassen müssen selbst abstrakt sein oder alle abstrakten Methoden ausimplementieren.

1.3 Grundgerüst

public abstract class Form {                 // partiell abstrakt
    protected String name;
 
    public Form(String name) {               // Konstruktor ist erlaubt
        this.name = name;
    }
 
    public abstract double flaeche();        // abstrakt
    public abstract double umfang();
 
    public void ausgeben() {                 // konkrete Methode
        System.out.printf("%s: Fläche=%.2f, Umfang=%.2f%n", name, flaeche(), umfang());
    }
}
 
public class Kreis extends Form {
    private double r;
 
    public Kreis(double r) {
        super("Kreis");                      // Konstruktor der abstrakten Klasse
        this.r = r;
    }
 
    @Override public double flaeche() { return Math.PI * r * r; }
    @Override public double umfang()  { return 2 * Math.PI * r; }
}
Form f = new Kreis(1);   // ✅ Referenz vom abstrakten Typ
Form g = new Form("x");  // ❌ Form is abstract; cannot be instantiated
f.ausgeben();            // Kreis: Fläche=3.14, Umfang=6.28

Teil 2 – Schnittstellen (Interfaces)

2.1 Wozu? (Folie 3)

  • In Java gibt es keine Mehrfachvererbung
  • Damit eine Klasse trotzdem mehrere Typen besitzen kann, gibt es Schnittstellen
  • Eine Klasse kann beliebig viele Schnittstellen implementieren
  • In der OO trennt man immer das Was vom Wie:
Abstrakte Klasse und InterfaceWas? Leere Methodenrümpfe
Erbende / implementierende KlasseWie? Ausimplementierte Methoden
  • Alle Variablen sind public static final, also Klassenvariablen (Konstanten)
  • Im Gegensatz zu abstrakten Klassen dürfen Schnittstellen keine Objektvariablen haben, immer nur Konstanten

2.2 Deklaration (Folie 4)

  • Schnittstellen enthalten nur Köpfe von Methoden (Modifizierer + Rückgabetyp + Signatur)
  • Statt class schreibt man interface
  • Alle Objektmethoden sind automatisch abstract und public → beides kann man weglassen
  • Methoden haben keinen Rumpf {} und enden mit ;
  • Keine Konstruktoren
  • Namen enden oft auf „-ble” (Accessible, Runnable, Buyable, Electable)

2.3 Implementieren (Folie 5 + 6)

  • Hinter den Klassennamen kommt implements
  • Mehrere Schnittstellen werden durch Beistrich getrennt
  • Eine Klasse kann gleichzeitig:
    • maximal 1 Klasse erweitern (extends) und
    • beliebig viele Schnittstellen implementieren (implements)
  • UML: «interface» über dem Namen
public interface Buyable {
    String price();          // public abstract ist implizit dabei
}
 
public class Testarossa extends Ferrari implements Buyable, Paintable {
    @Override
    public String price() {
        return this.preis + "€";
    }
    // + alle Methoden von Paintable
}

2.4 Subinterfaces (Folie 7)

  • Schnittstellen können andere Schnittstellen erweitern → extends
  • Die implementierende Klasse muss dann alle Methoden aller Schnittstellen in der Typhierarchie implementieren
  • Methoden der Oberschnittstelle können in der Unterschnittstelle überschrieben werden
  • Auch statische Methoden sind möglich (immer mit Implementierung)
public interface CHBuyable extends Buyable {
    int MAX_PRICE = 500_000;                         // Konstante
 
    static boolean isValidPrice(double price) {      // statische Methode
        return price >= 0 && price < MAX_PRICE;
    }
 
    String price();                                  // aus Buyable überschrieben
}
// Aufruf: CHBuyable.isValidPrice(300_000)

2.5 Erlaubte Eigenschaften einer Schnittstelle (Folie 8)

AttributMethode
Objekt-❌ nein, nicht erlaubt✅ ja, üblicherweise abstrakt
Statische(s)✅ ja, als Konstante✅ ja, immer mit Implementierung
  • Objektmethoden in Schnittstellen müssen nicht immer abstrakt sein, sie können auch ausimplementiert sein (→ default)
  • Schnittstellen nur mit statischen Methoden ersetzt man besser durch eine finale Klasse mit privatem Konstruktor (wie Math)

2.6 Default-Methoden (Folie 9)

  • Schnittstellen sollten nicht im Nachhinein geändert werden, sonst funktionieren alle implementierenden Klassen nicht mehr
  • Lösung: Default-Methode
    • enthält eine Implementierung
    • Schnittstellen mit Default-Methode heißen erweiterte Schnittstellen
    • Default-Methoden können überschrieben werden
public interface Paintable {
    void paint(Color farbe);
 
    default void paintRed() {        // neue Methode, alte Klassen funktionieren weiter
        paint(Color.RED);
    }
}

2.7 Beispiel zum Üben

public interface Fahrbar {
    int MAX_SPEED = 130;                    // public static final
    void fahren(int kmh);                   // public abstract
    default void hupen() { System.out.println("Tuut!"); }
}
 
public interface Aufladbar {
    void laden(int prozent);
}
 
public class EAuto implements Fahrbar, Aufladbar {
    private int akku = 50;
 
    @Override public void fahren(int kmh) {
        if (kmh > MAX_SPEED) kmh = MAX_SPEED;
        System.out.println("E-Auto fährt " + kmh + " km/h");
    }
    @Override public void laden(int prozent) {
        akku = Math.min(100, akku + prozent);
        System.out.println("Akku: " + akku + "%");
    }
}
 
public class Fahrrad implements Fahrbar {
    @Override public void fahren(int kmh) { System.out.println("Fahrrad fährt " + kmh + " km/h"); }
    @Override public void hupen() { System.out.println("Kling kling!"); }   // default überschrieben
}
Fahrbar[] fz = { new EAuto(), new Fahrrad() };
for (Fahrbar f : fz) { f.fahren(200); f.hupen(); }
if (fz[0] instanceof Aufladbar) ((Aufladbar) fz[0]).laden(70);
// E-Auto fährt 130 km/h
// Tuut!
// Fahrrad fährt 200 km/h
// Kling kling!
// Akku: 100%

Teil 3 – (Abstrakte) Klassen vs. Schnittstellen (Folie 10)

Abstrakte Klasse verwenden, wenn …

  • die Unterklasse nur 1 Klasse erweitern soll
  • man Gemeinsamkeiten von Klassen in eine Oberklasse auslagern möchte
  • die Klasse Objektzustände (Attribute) enthält
  • man eine Basisimplementierung vorgeben möchte

Schnittstelle verwenden, wenn …

  • die Unterklasse mehrere Klassen (Schnittstellen) erweitern soll
  • Vorschriften für Verhalten weitergegeben werden sollen
  • die Implementierung fast ausschließlich in die Unterklasse ausgelagert werden soll
Abstrakte KlasseInterface
Schlüsselwortabstract classinterface
Verwenden mitextendsimplements
Wie viele pro Klasse?einebeliebig viele
Objektvariablen✅❌ nur Konstanten (public static final)
Konstruktor✅❌
Methoden mit Rumpf✅nur default und static
Methoden-Sichtbarkeitbeliebigautomatisch public
new möglich?❌❌
UMLkursiv / {abstract}«interface»

Teil 4 – Assertions

4.1 Motivation (3.1)

Programme müssen auf Fehlersituationen vorbereitet sein. Java hat dafür zwei Möglichkeiten:

  • Assertions: Absicherung der inneren Programmlogik
  • Exceptions: Einhalten beliebiger Randbedingungen

4.2 Assertions allgemein (3.2.1 + 3.2.2)

  • Assertion (Zusicherung) = boolescher Ausdruck, der immer zutreffen muss
  • Wird von der JVM zur Laufzeit überwacht
  • Ist sie false → das Programm stoppt mit einem AssertionError, die nachfolgenden Anweisungen werden nicht mehr ausgeführt; der Ort der gescheiterten Assertion wird ausgegeben
assert ausdruck;            // ausdruck muss true liefern
assert ausdruck : string;   // string = Fehlermeldung
 
assert a <= b && b <= c;
assert x >= 0 : "Negatives x";

4.3 Aktivierung (3.2.3)

  • Assertions kosten Rechenzeit → sie sind standardmäßig deaktiviert!
  • Aktivieren per Flag beim Start, ohne neu zu kompilieren:
BefehlWirkung
java -ea Testenable assertions: aktivieren
java -da Testdisable assertions: deaktivieren (Default)
-ea:Klassennamenur in einer Klasse
-ea:paketname...in einem Paket (3 Punkte!)
java -ea:project2... -da:project2.datastore... -ea:project2.datastore.Store Testmehrere Schalter kombiniert
  • IntelliJ: Run → Edit Configurations → -ea bei VM options
  • VS Code: in launch.json "vmArgs": "-ea"

4.4 Wann einsetzen? (3.2.4 + 3.2.5)

✅ Geeignet für Bedingungen, deren Einhaltung das Programm selbst garantieren kann:

  • Berechnungsergebnisse prüfen, die nicht möglich sein dürfen (z.B. sqrt(-2))
  • Postconditions: was am Ende einer Methode erfüllt sein muss
  • Schleifeninvarianten
  • „Tote Zweige” in if/switch markieren, die nie erreicht werden sollten → assert false;

❌ Nicht geeignet für Bedingungen, die von äußeren Einflüssen abhängen, z.B. Parameter am Anfang einer öffentlichen Methode prüfen → dafür Exceptions!


Teil 5 – Exceptions

5.1 Motivation und Vorteile (3.3.1 + 3.3.2)

  • Fehler im laufenden Programm: logische Fehler, falsche Bedienung durch den Benutzer, Probleme im Laufzeitsystem
  • Exceptions (Ausnahmen) = Sprachmittel zur kontrollierten Reaktion auf Laufzeitfehler (Assertions sind ein Sonderfall davon)
  • Zwei große Vorteile:
    1. Fehler können nicht ignoriert werden, es müssen Maßnahmen getroffen werden
    2. Code für Programmablauf und Fehlerbehandlung wird getrennt

5.2 Grundprinzip (3.3.3 + 3.3.4)

  • An bestimmten Stellen werden Ausnahmesituationen geprüft und gemeldet; an einer anderen Stelle steht der Code zur Reaktion
  • Die Information über die Ausnahme wird in ein Objekt verpackt: eine Instanz von Exception (oder einer Unterklasse)
  • Explizit auslösen mit throw
  • Abfangen in einer catch-Klausel (äußerer Block, aufrufende Methode, …)

5.3 try-catch (3.3.5)

try {
    // regulärer Code
} catch (ExceptionType1 e) {
    // Fehlerbehandlung 1
} catch (ExceptionType2 e) {
    // Fehlerbehandlung 2
} finally {
    // läuft IMMER
}

5.4 Ablauf (3.3.6)

  • Bei einer Ausnahme vom Typ E wird eine passende catch-Klausel vom Typ E gesucht, oder von einer Oberklasse von E (Substitution)
  • Die Ausnahme wird der Reihe nach mit den catch-Klauseln verglichen
  • Es wird die erste kompatible ausgeführt, alle weiteren werden ignoriert
  • Deshalb: Reihenfolge von spezifisch zu allgemein (z.B. erst MyNewException, dann MyException, dann Exception)
  • Kein passender catch → das Programm bricht ab
  • Mit passendem catch → Ausnahme gefangen, das Programm läuft weiter (nach dem try-catch)

5.5 Termination Model vs. Resumption Model (3.3.7)

Termination Model (Java!)Der Kontrollfluss kehrt nicht an die Stelle zurück, an der die Ausnahme aufgetreten ist. Der Rest des try-Blocks wird übersprungen.
Resumption Model (nicht in Java)Das Programm setzt an der Stelle der Ausnahme fort

5.6 Arten von Ausnahmen (3.3.8 + 3.3.9)

Systemausnahmen (runtime / unchecked)Benutzerausnahmen (checked)Errors
Auslösungautomatisch (illegale Instruktionen, Bugs)explizit vom Programmierer (throw)JVM-Probleme
BeispieleArithmeticException (Division durch 0), NullPointerException (Zugriff auf null), ArrayIndexOutOfBoundsExceptionjava.io.IOException, eigene ExceptionsOutOfMemoryError
Abfangen?kann, muss aber nichtmuss immer (try-catch oder throws), die IDE fordert dazu aufmuss nicht, man kann nicht reagieren
OberklasseRuntimeExceptionExceptionError
  • Nicht behandelte Ausnahme → sie steigt im Stacktrace nach oben (Methode → aufrufende Methode → … → main → Java VM Runtime stoppt). Am Stacktrace sieht man, wo der Fehler war.
  • Beispiel aus den Folien: sc.nextInt() bekommt ein Double:
Scanner.nextInt  →  ReadTest.readInt (return sc.nextInt();)
                 →  ReadTest.main   (println("Deine Zahl ist: " + readInt))
                 →  Java VM Runtime → Programmabbruch (InputMismatchException)
  • try-catch rahmt den Fehler ein und behandelt ihn direkt
  • throws gibt die Exception nach oben an die aufrufende Methode weiter, irgendwo muss sie dann behandelt werden

5.7 Hierarchie (3.3.10)

java.lang.Throwable            ← getMessage(), toString(), printStackTrace()
├── Error                      ← OutOfMemoryError, …
└── Exception                  ← checked (Benutzerausnahmen)
    ├── IOException
    ├── eigene: MyException
    └── RuntimeException       ← unchecked (Systemausnahmen)
        ├── ArithmeticException
        ├── NullPointerException
        ├── ArrayIndexOutOfBoundsException
        ├── ClassCastException
        └── IllegalArgumentException
            └── NumberFormatException
  • Ausnahmen sind Objekte mit Informationen über den Fehler
  • Ein catch für Klasse E fängt auch alle Unterklassen von E
  • Methoden von Throwable:
Methodeliefert
String getMessage()Text der Ausnahme
String toString()Klassenname + ": " + getMessage() (ohne Message nur der Klassenname)
void printStackTrace()Stacktrace auf die Konsole
Konstruktorenleer, mit Fehlermeldung, mit Ursache (cause), …

5.8 Eigene Exceptions (3.3.10.2)

  • Eigene Klassen erben immer von Exception
  • Sie haben zuerst nur einen Defaultkonstruktor. Für eine Fehlermeldung braucht man einen eigenen Konstruktor mit super(...):
class MyException extends Exception {
    public MyException() {
    }
    public MyException(final String specialinfo) {
        super(specialinfo);
    }
}
  • Auslösen (werfen) mit throw; throw unterbricht die Ausführung sofort:
throw new MyException();
throw new MyException("Falsche Eingabe");
  • Kann eine Methode eine (checked) Exception werfen, muss das in der Signatur stehen:
int test(int x) throws IOException { ... }

→ Die Methode reicht die Ausnahme damit weiter; die aufrufende Methode muss sie behandeln.

throwthrows
in der Methodeim Methodenkopf
wirft jetzt ein Exception-Objektkündigt Typen an, die geworfen werden können
throw new MyException("x");void m() throws MyException, IOException

5.9 finally (3.3.11)

  • Steht nach den catch-Klauseln
  • Wird immer am Ende ausgeführt, egal ob eine Ausnahme auftrat oder nicht (auch bei return)
  • Zweck: im try begonnene Arbeiten sauber abschließen
  • Beispiel aus den Folien: Im try wird eine Datei geöffnet, im finally wird sie geschlossen

5.10 Exception-Chaining (3.3.12)

  • Problem: Werden Ausnahmen immer weitergereicht, entstehen sehr lange throws-Signaturen; Detailfehler müssen an ganz anderer Stelle behandelt werden; Änderungen ziehen viele Änderungen nach sich
  • Lösung: Ausnahmen untergeordneter Aufrufe werden aufgefangen, zusammengefasst und in einer neuen Ausnahme weitergegeben (die alte ist die Ursache / cause)
try {
    ...
} catch (E1 ex) {
    throw new MyException(ex);       // E1 wird in MyException verpackt
} catch (E2 ex) {
    throw new MyException(ex);
}
  • Dafür braucht die eigene Exception einen Konstruktor mit Throwable cause:
MyException(final Throwable cause) {
    super(cause);
}
// oder beides:
MyException(String message, Throwable cause) {
    super(message, cause);
}
  • Alternative mit initCause():
catch (E1 ex) {
    MyException m = new MyException();
    m.initCause(ex);
    throw m;
}
  • Im Stacktrace erscheint die Ursache als Caused by: ...

Teil 6 – Lehrer-Beispiele (Classroom) mit Ausgaben

6.1 ExceptionTest.java: mehrere catch-Klauseln

public class ExceptionTest {
    public static void main(String[] args) {
        int x, y;
        try {
            x = Integer.parseInt(args[0]);
            y = Integer.parseInt(args[1]);
            System.out.println(x / y);
            System.out.println("nach Division");
        } catch (ArithmeticException ae) {
            System.out.println("Du darfst nicht durch 0 dividieren!");
            ae.printStackTrace();
        } catch (NumberFormatException nfe) {
            System.out.println("Die eingegebene Zahl ist kein Integer");
        } catch (ArrayIndexOutOfBoundsException aiob) {
            System.out.println("Gib bitte genau 2 Parameter an");
        }
        System.out.println("nach ExceptionHandling");
    }
}

6.2 ExceptionTest4.java: Exception-Hierarchie und überschriebene Methoden

class MyException extends Exception { }
class MyNewException extends MyException { }
class MyNewExtraException extends MyException { }
 
class A {
    public void a() throws MyException {
        throw new MyException();
    }
}
class B extends A { }                    // erbt a() von A
class C extends B {
    @Override
    public void a() throws MyNewException, MyNewExtraException {   // nur Unterklassen erlaubt!
        if (Math.random() > 0.5) {
            throw new MyNewException();
        } else {
            throw new MyNewExtraException();
        }
    }
}
 
public class ExceptionTest4 {
    public static void main(String[] args) {
        int zahl = 3;
        A t1 = new A(); B t2 = new B(); C t3 = new C();
        try {
            switch (zahl) {
                case 1: t1.a(); break;
                case 2: t2.a(); break;
                case 3: t3.a(); break;
                default: throw new MyException();
            }
        } catch (MyNewException e) {     // spezieller zuerst
            System.out.println(e);
        } catch (MyException e) {        // fängt auch MyNewExtraException
            System.out.println(e);
        }
    }
}

Regel beim Überschreiben

Eine überschreibende Methode darf nur dieselben oder speziellere (Unterklassen-)Exceptions in throws angeben, keine neuen, allgemeineren checked Exceptions.

6.3 ExceptionTest6.java: Exception-Chaining + finally

class MyException extends Exception {
    MyException() { }
    MyException(final Throwable cause) { super(cause); }
}
class MyOtherException extends Exception { }
 
public final class ExceptionTest6 {
    void test(final int i) throws MyException {
        try {
            switch (i) {
                case 1: throw new MyException();
                case 2: throw new MyOtherException();
            }
        } catch (final MyOtherException e1) {
            throw new MyException(e1);              // Chaining: verpacken
        }
    }
 
    public static void main(final String[] args) {
        if (args.length != 1) {
            System.out.println("Bitte genau ein Argument uebergeben!");
        } else {
            final ExceptionTest6 et = new ExceptionTest6();
            try {
                et.test(Integer.parseInt(args[0]));
            } catch (final MyException e) {
                System.out.println(e);
            } finally {
                System.out.println("finally wurde erreicht");
            }
        }
    }
}

6.4 AssertionTest.java: Fehler in der Logik finden

public class AssertionTest {
    static int max(int x, int y) { return x < y ? x : y; }   // ⚠️ Fehler: liefert das Minimum!
    static int min(int x, int y) { return x > y ? x : y; }   // ⚠️ Fehler: liefert das Maximum!
 
    static void test(int x, int y) {
        int maximum = AssertionTest.max(x, y);
        int minimum = AssertionTest.min(x, y);
        assert maximum >= minimum : "Maximum ist kleiner als Minimum";
        System.out.println(maximum + " " + minimum);
    }
 
    public static void main(String[] args) {
        AssertionTest.test(10, 15);
        AssertionTest.test(8, 3);
        AssertionTest.test(2, 2);
    }
}

6.5 Dein CharacterTwice: try-catch mit NullPointerException

try {
    System.out.println(isEveryCharacterTwice(null));   // null.length() → NPE
} catch (NullPointerException e) {
    System.out.println("NullPointerException");
}

Teil 7 – OOP_Ex6: Wahlen in Südtirol (dein Projekt 07_Wahlen)

Aufgabenstellung (Kurzfassung):

  • Abstrakte Klasse Partei: Name, Quote (Mann/Frau)
  • Konkrete Klasse Person: Name, Alter, Geschlecht, Wohnort, kann einen Kandidaten wählen
  • Konkrete Klasse SuedtirolerPartei extends Partei: Beschreibung, N Kandidaten, Anzahl, alle Kandidaten mit Slogan ausgeben
  • Konkrete Klasse Kandidat extends Person implements Electable: gehört einer Partei an, Stimmen
  • Interface Electable: Über-18-Prüfung, Werbeslogan
  • Konkrete Klasse Wahl: Datum (Stimmen nur an diesem Tag), Parteien, Ausgabe der Stimmverteilung + ungültige Stimmen (Person nicht 18)
  • Test: 3 Parteien mit 3–5 Kandidaten, 1000 zufällige Bürger, alle wählen zufällig (nur einmal), Wahl heute + Wahl vor einigen Tagen; Kandidat muss 5 Monate vor der Wahl 18 sein
            Person                    «interface» Electable
   (name, birthDate, gender,           isOver18(LocalDate)
    city, hasVoted)                    getSlogan()
              ▲                              ▲
              │ extends                      │ implements
              └────────── Kandidat ──────────┘
                     (partei, votes, slogan)

   «abstract» Partei (name, quote)  ◄── extends ──  SuedtirolerPartei
                                                    (description, Kandidat[5], anzahl)

   Wahl (datum, SuedtirolerPartei[], ungueltig)
public interface Electable {
    boolean isOver18(LocalDate date);
    String getSlogan();
}
 
public abstract class Partei {
    protected String name;
    protected String quote;
    public Partei(String name, String quote) {
        this.name = name;
        this.quote = quote;
    }
}
 
public class Kandidat extends Person implements Electable {
    protected Partei partei;
    protected int votes = 0;
    protected String slogan;
 
    public Kandidat(String name, LocalDate birthDate, String gender, String city, Partei partei, String slogan) {
        super(name, birthDate, gender, city);
        this.partei = partei;
        this.slogan = slogan;
    }
    public boolean isOver18(LocalDate date) {
        return !birthDate.plusYears(18).isAfter(date);
    }
    public String getSlogan() { return slogan; }
}
// Wahl.vote: nur am Wahltag, nur einmal, Kandidat 5 Monate vor der Wahl 18
public void vote(Person p, Kandidat k) {
    if (!LocalDate.now().equals(datum) || p.hasVoted || !k.isOver18(datum.minusMonths(5))) {
        return;
    }
    p.hasVoted = true;
    if (p.birthDate.plusYears(18).isAfter(datum)) {
        ungueltig++;
    } else {
        k.votes++;
    }
}

LocalDate

LocalDate.now(), LocalDate.of(2000, 1, 30), plusYears(n), minusMonths(n), minusDays(n), isAfter(d), isBefore(d), equals(d); Import java.time.LocalDate


Teil 8 – Übungsblätter aus Classroom mit Lösungen

OOP_Ex7 Aufgabe 1 – parseInts (Exceptions)

Integer.parseInt(String) wirft eine NumberFormatException, wenn keine Konvertierung möglich ist (z.B. "0x10" oder null).

  • Methode static int[] parseInts(String... numbers) schreiben (varargs: beliebig viele Strings oder ein Array)
  • Größe des Rückgabe-Arrays = Anzahl der Strings
  • Nicht konvertierbar → 0 an dieser Stelle (durch Abfangen der NumberFormatException)
  • null als einzelnes Argument → 0
  • parseInts() ohne Argumente ist ok; parseInts(null) muss eine Ausnahme auslösen
parseInts("1", "234", "333")                      // [1, 234, 333]
parseInts("1", "ll234", "3", null, "99")          // [1, 0, 3, 0, 99]
parseInts("Person", "Woman", "Man", "Camera, TV") // [0, 0, 0, 0]

OOP_Ex7 Aufgabe 2 – Division Exception-Chain

  • divideNumbers(a, b) dividiert und gibt das Ergebnis zurück
  • Bei Divisor 0 → ArithmeticException
  • Im catch eine eigene DivisionException werfen, mit der ursprünglichen als Ursache
  • Mit 10, 0 testen, DivisionException fangen und mit printStackTrace() ausgeben

OOP_Ex7 Aufgabe 3 – DataProcessor Exception-Chain

  • Klasse DataProcessor mit processData(List<Object> data)
  • processData ruft für jedes Element processObject(Object obj) auf
  • processObject prüft den Typ: nur Integer und String ausgeben ("String: hallo")
  • Unbekanntes Objekt → IllegalArgumentException("Ungültiges Datenobjekt: " + Name des Objekts)
  • In processData: NullPointerException → DataProcessingException("Ungültiges Datenobjekt: Null-Wert gefunden.", Ursache)
  • In processData: alle anderen → DataProcessingException("Fehler bei der Verarbeitung des Datenobjekts.", Ursache)

OOP_Ex7 Aufgabe 4 – Konto mit Assertions

Klasse Konto mit guthaben, Konstruktor mit Startguthaben, einzahlen, abheben. Mit Assertions sicherstellen: Guthaben immer positiv, nur positive Beträge ein-/auszahlen.

OOP_Ex7 Aufgabe 5 – ZahlenListe mit Assertions

Klasse ZahlenListe mit ArrayList<Integer> zahlen, Konstruktor (leere Liste), zahlHinzufuegen, zahlEntfernen, druckeListe. Nach jedem Hinzufügen/Entfernen mit Collections.sort sortieren und mit Assertions prüfen, dass die Liste sortiert ist. Nur positive Zahlen erlaubt.

OOP_Ex5 Aufgabe 3 – Flugzeuge (abstrakte Klasse)

Gegeben ist die abstrakte Klasse Flugzeug (hersteller, maxSpeed, immatNummer, anzahlFluegel; abstract public boolean getLooping();).

  • Verkehrsflugzeug: genau 1 Flügelpaar, Attribut Passagiere; nie Loopings, auch nicht in Unterklassen; Konstruktor (Hersteller, maxSpeed, ImmatNr, Passagiere); Getter/Setter Passagiere
  • Doppeldecker: genau 2 Flügelpaare; Konstante LOOPINGSPEED = 320; getLooping() = maxSpeed > LOOPINGSPEED; offenesCockpit nach dem Setzen unveränderbar; isOffenesCockpit(); 2 Konstruktoren (der 2. ohne Cockpit → Default true); nicht erweiterbar

Teil 9 – Zusätzliche Übungsaufgaben

Aufgabe A – Abstrakte Klasse Mitarbeiter

  • abstrakte Klasse Mitarbeiter (name, Konstruktor, abstract double gehalt(), void info() → „Name: Gehalt €”)
  • Angestellter (fixgehalt) und Stundenarbeiter (stunden × lohnProStunde)
  • main: Mitarbeiter[] mit 3 Objekten, alle info() + Summe

Aufgabe B – Interface mit default-Methode

  • Interface Bezahlbar mit double preis() und default String preisText() → „Preis: X €”
  • Buch (titel, preis) und Abo (monate × preisProMonat) implementieren es
  • Warenkorb Bezahlbar[], alle ausgeben + Gesamtpreis

Aufgabe C – Abstrakt + Interface kombiniert

  • abstrakte Klasse Tier (name, abstract String laut()), Interface Haustier (String besitzer())
  • Hund extends Tier implements Haustier, Wolf extends Tier
  • Tier[] durchgehen, nur bei Haustieren zusätzlich den Besitzer (instanceof + Cast)

Aufgabe D – Was ist die Ausgabe?

String[] eingaben = { "10", "abc", "0", null };
for (String e : eingaben) {
    try {
        int zahl = Integer.parseInt(e);
        System.out.println("100 / " + zahl + " = " + (100 / zahl));
    } catch (NumberFormatException ex) {
        System.out.println("Keine Zahl: " + e);
    } catch (ArithmeticException ex) {
        System.out.println("Division durch 0");
    } finally {
        System.out.println("fertig mit " + e);
    }
}

Aufgabe E – finally und return

static int test() {
    try { System.out.println("try"); return 1; }
    finally { System.out.println("finally"); }
}
System.out.println(test());

Aufgabe F – Welche Exception?

Code?
int x = 5 / 0;
int[] a = new int[3]; a[3] = 1;
String s = null; s.length();
Integer.parseInt("12a");
Object o = "Hi"; Integer i = (Integer) o;
"Java".charAt(10);
double d = 5.0 / 0;
assert 1 > 2; mit -ea

Teil 10 – Compilerfehler-Fallen (alle getestet)

CodeErgebnis
new A() bei abstract class A❌ A is abstract; cannot be instantiated
abstract void f() { }❌ abstract methods cannot have a body
class X { abstract void f(); }❌ Klasse muss selbst abstract sein
class X implements I { } (Methode fehlt)❌ X is not abstract and does not override abstract method
class X implements I { void f() {} }❌ fehlendes public → schwächere Sichtbarkeit
I.KONSTANTE = 2;❌ cannot assign a value to static final variable
throw new Exception("x"); ohne try/throws❌ unreported exception; must be caught or declared to be thrown
catch (Exception e) vor catch (ArithmeticException e)❌ has already been caught
Überschreibende Methode wirft allgemeinere checked Exception❌
class X extends A, B❌ nur 1 Oberklasse
class X extends A implements B, C✅
interface B extends A✅ Subinterface
throw new IllegalArgumentException() ohne throws✅ unchecked
assert x > 0; ohne -ea✅ wird ignoriert

Begriffe auf einen Blick

BegriffKurz erklärt
abstrakte Klassekeine Objekte, Modellierungsklasse, abstract
abstrakte Methodenur Signatur, kein Rumpf
rein / partiell abstraktnur abstrakte / gemischte Methoden
InterfaceVertrag, „Was” statt „Wie”, nur Konstanten
implementsInterface erfüllen (beliebig viele)
Subinterfaceinterface B extends A
default-MethodeMethode mit Rumpf im Interface → erweiterte Schnittstelle
AssertionZusicherung der inneren Logik, assert, nur mit -ea aktiv
AssertionErrorwird geworfen, wenn eine Assertion false ist
ExceptionObjekt mit Fehlerinformationen
Systemausnahmeunchecked, RuntimeException, muss nicht gefangen werden
Benutzerausnahmechecked, Exception, muss gefangen oder weitergegeben werden
ErrorJVM-Fehler, z.B. OutOfMemoryError
Termination ModelJava kehrt nicht zur Fehlerstelle zurück
throwException werfen
throwsException in der Signatur weitergeben
finallyläuft immer, zum sauberen Abschließen
Exception-ChainingException in neue Exception verpacken, super(cause), „Caused by”
initCause()Ursache nachträglich setzen
StacktraceAufrufkette bis zur Fehlerstelle