JavaTrainer
Informatik-Test · Abstrakte Klassen, Interfaces, Exceptions

Java Prüfungstrainer

Erst die Theorie Schritt für Schritt, dann Praxisaufgaben wie im Test, am Ende ein Quiz mit Erklärungen. Alles basiert auf euren Folien.

Vorschlag für heute Abend

  1. Theorie von oben nach unten lesen. Die Kästen Merke sind das, was im Test sicher drankommt.
  2. Bei Exceptions die Ablauf-Simulation ausprobieren: dort versteht man try/catch/finally am schnellsten.
  3. Praxis durcharbeiten. Beim Programmieren erst selbst auf Papier schreiben, dann die Lösung aufklappen.
  4. Quiz machen, danach nur die falschen wiederholen, bis alles sitzt.
  5. Zum Schluss den Spickzettel einmal durchlesen.
Zur Quelle

Die Seite benutzt die Begriffe aus euren Unterlagen (z. B. „Systemausnahmen“, „Benutzerausnahmen“, „partiell abstrakt“, „erweiterte Schnittstelle“). Alle Codebeispiele wurden mit Java 21 kompiliert und ausgeführt, die Lösungen stimmen also.

Dein Stand

Noch kein Quiz gespielt.

Teil 1 · Grundlage

Vererbung kurz wiederholt

Abstrakte Klassen und Interfaces bauen darauf auf. Wenn das hier sitzt, wird der Rest leicht.

extends und die Ist-eine-Art-von-Beziehung

Eine Unterklasse erweitert eine Oberklasse mit extends und erbt deren sichtbare Eigenschaften. Jede Klasse ohne eigene extends-Angabe erbt automatisch von java.lang.Object (deshalb hat jede Klasse toString()). Java kennt keine Mehrfachvererbung: eine Klasse hat genau eine Oberklasse (Grund: Diamantenproblem).

class Auto { String name; }
class Cabrio extends Auto { boolean dachOffen; }   // Cabrio ist eine Art von Auto

Konstruktoren und super()

class Car {
    public String name;
    Car(String name) { this.name = name; }
}
class Cabriolet extends Car {
    Cabriolet() { super("Volvo"); }   // ohne diese Zeile: Compilerfehler
}

Überladen vs. Überschreiben

Überladen (overload)Überschreiben (override)
Wo?in derselben Klasse (oder Unterklasse)in einer Unterklasse
Namegleichgleich
Parameterlisteverschiedengleich (gleiche Signatur)
Beispieladd(int) und add(int,int)toString() in der Unterklasse neu schreiben

Die Signatur einer Methode ist Name + Parameterliste. Beim Überschreiben ist @Override eine Annotation vor der Methode: der Compiler prüft dann, dass wirklich etwas überschrieben wird.

Referenztyp und Objekttyp

Der Compiler kennt nur den Referenztyp (links vom =). Die JVM kennt zur Laufzeit den Objekttyp (rechts vom new).

Ferrari f = new Testarossa();    // erlaubt: Testarossa ist ein Ferrari (automatisch)
f.name;                          // ok, name steht in Ferrari
f.hubraum;                       // Compilerfehler: Ferrari kennt hubraum nicht
Testarossa t = (Testarossa) f;   // explizite Umwandlung (Cast) nötig
t.hubraum;                       // jetzt ok

Polymorphie und dynamisches Binden

Welche überschriebene Methode läuft, entscheidet die JVM erst zur Laufzeit anhand des Objekttyps (spätes/dynamisches Binden).

Object o = new Testarossa();
System.out.println(o.toString());   // ruft toString() von Testarossa auf!
Achtung

Nicht dynamisch gebunden werden private, static und final Methoden. Sie können nicht überschrieben werden. Mit final bei einer Klasse kann man gar keine Unterklassen bilden (z. B. String, Math).

Merke
  • Eine Klasse: ein extends. Konstruktoren erbt man nicht.
  • super(...) immer als erste Zeile; ohne Angabe wird super() ergänzt.
  • Compiler schaut auf den Referenztyp, Laufzeit auf den Objekttyp.
Teil 2

Abstrakte Klassen und Methoden

Eine abstrakte Klasse ist ein unfertiger Bauplan. Sie legt fest, was Unterklassen können müssen, aber nicht immer, wie.

Die Grundidee

Bisher waren alle Klassen konkret: man konnte mit new Objekte erzeugen. Manchmal ist das nicht sinnvoll. Ein allgemeines „Fahrzeug“ gibt es in der Realität nicht, nur Autos, Fahrräder usw. Eine Oberklasse soll dann nur als Modellierungsklasse dienen.

public abstract class Fahrzeug {            // Modifier abstract bei der Klasse
    protected String name;

    public Fahrzeug(String name) {           // Konstruktor ist erlaubt
        this.name = name;
    }

    public abstract void fahre(double speed);   // abstrakt: nur Kopf, kein Rumpf, endet mit ;

    public void stehe() {                    // normale Methode mit Implementierung
        System.out.println(name + " steht.");
    }
}

public class Auto extends Fahrzeug {
    public Auto(String name) { super(name); }

    @Override
    public void fahre(double speed) {        // Pflicht: abstrakte Methode ausimplementieren
        System.out.println(name + " fährt " + speed + " km/h.");
    }
}

Regeln auf einen Blick

AussageStimmt?
new Fahrzeug("x") bei abstrakter KlasseCompilerfehler
Variable vom abstrakten Typ: Fahrzeug f = new Auto("Fiat");erlaubt (Polymorphie)
Abstrakte Methode hat einen Rumpf { }Compilerfehler
Klasse hat eine abstrakte Methode, ist aber nicht abstractCompilerfehler
Abstrakte Klasse hat einen Konstruktorerlaubt, wird per super(...) aufgerufen
Abstrakte Klasse hat Objektvariablenerlaubt (anders als Interface)
Abstrakte Klasse ohne eine einzige abstrakte Methodeerlaubt
Unterklasse implementiert nicht alle abstrakten MethodenFehler, außer sie ist selbst abstract

Rein abstrakt oder partiell abstrakt

Rein (pure) abstrakt

Enthält ausschließlich abstrakte Methoden. Gibt nur Signaturen vor, wie ein Vertrag.

Partiell abstrakt

Enthält abstrakte und normale Methoden mit Implementierung. Bietet eine Basisimplementierung an.

Merke
  • Ist mindestens eine Methode abstrakt, ist die ganze Klasse abstrakt.
  • Von abstrakten Klassen gibt es keine Objekte.
  • Unterklassen müssen alle abstrakten Methoden ausimplementieren oder selbst abstrakt bleiben.
  • UML: Klassenname kursiv oder {abstract} unter den Namen schreiben.
Hinweis aus den Folien

Soll nur verhindert werden, dass ein Objekt der Klasse gebildet wird, geht das auch mit einem private oder protected Konstruktor. Abstrakt ist dann sinnvoll, wenn Unterklassen etwas ausimplementieren müssen.

Polymorphie mit abstrakten Klassen

Fahrzeug[] flotte = { new Auto("Fiat"), new Fahrrad("BMX") };
for (Fahrzeug f : flotte) {
    f.fahre(30);          // jedes Objekt führt seine eigene fahre() aus
}
Teil 3

Interfaces (Schnittstellen)

Java erlaubt keine Mehrfachvererbung bei Klassen. Damit eine Klasse trotzdem mehrere „Typen“ haben kann, gibt es Interfaces.

Was vs. Wie

In der Objektorientierung versucht man, das Was vom Wie zu trennen. Abstrakte Klasse und Interface beschreiben das Was (leere Methodenrümpfe), die erbende bzw. implementierende Klasse liefert das Wie (ausimplementierte Methoden).

Deklaration und Implementierung

public interface Buyable {            // interface statt class
    String price();                   // automatisch public abstract
}

public interface Paintable {
    void paint(Color farbe);
}

// Eine Klasse: genau EIN extends, aber beliebig viele implements (mit Komma)
public class Testarossa extends Ferrari implements Buyable, Paintable {
    @Override
    public String price() {           // public ist Pflicht!
        return this.preis + "";
    }
    @Override
    public void paint(Color farbe) { /* ... */ }
}

Was darf in ein Interface?

AttributMethode
Objektbezogennicht erlaubtja, üblicherweise abstrakt (kann aber auch ausimplementiert sein, siehe default)
Statisch (static)ja, als Konstanteja, immer mit Implementierung

Subinterfaces

Ein Interface kann ein anderes mit extends erweitern. Eine implementierende Klasse muss dann alle Methoden aus allen Interfaces der Hierarchie ausimplementieren. Oberinterface-Methoden dürfen im Unterinterface überschrieben werden.

public interface CHBuyable extends Buyable {
    int MAX_PRICE = 500_000;                     // = public static final
    static boolean isValidPrice(double price) {  // statische Methode mit Rumpf
        return price >= 0 && price < MAX_PRICE;
    }
    String price();
}

Default-Methoden: erweiterte Schnittstellen

Ändert man ein Interface im Nachhinein (neue abstrakte Methode), funktionieren alle implementierenden Klassen nicht mehr. Die Lösung ist eine default-Methode mit fertiger Implementierung. Ein Interface mit default-Methode heißt erweiterte Schnittstelle. Klassen dürfen die default-Methode überschreiben, müssen aber nicht.

public interface Paintable {
    void paint(Color farbe);              // abstrakt: Klasse muss implementieren

    default void paintRed() {             // default: fertig, darf überschrieben werden
        paint(Color.RED);
    }
}

Abstrakte Klasse oder Interface?

Abstrakte Klasse, wenn …

  • die Unterklasse nur eine Klasse erweitern soll
  • Gemeinsamkeiten in eine Oberklasse ausgelagert werden
  • sie Objektzustände (Attribute) enthält
  • eine Basisimplementierung vorgegeben wird

Interface, wenn …

  • die Klasse mehrere Typen haben soll
  • Vorschriften für Verhalten weitergegeben werden
  • die Implementierung fast ganz in der Unterklasse liegt
Merke
  • Klasse: extends (1×), Interface: implements (beliebig viele, mit Komma). Interface erweitert Interface mit extends.
  • Methoden: public abstract (implizit). Variablen: public static final (implizit).
  • Kein Konstruktor, keine Objektvariablen.
  • Ein Interface-Typ darf als Variablentyp dienen: Buyable b = new Testarossa();
Hinweis aus den Folien

Interfaces, die nur statische Methoden enthalten, ersetzt man besser durch eine finale Klasse mit privatem Konstruktor.

Teil 4

Assertions und Exceptions

Programme müssen auf Fehler vorbereitet sein und kontrolliert reagieren. Java bietet dafür zwei Mittel: Assertions für die innere Logik und Exceptions für beliebige Randbedingungen.

Assertions

Eine Assertion (Zusicherung) ist ein boolescher Ausdruck, der immer zutreffen muss. Die JVM überwacht sie. Ist sie false, stoppt das Programm mit einem AssertionError, die nachfolgenden Anweisungen laufen nicht mehr.

assert a <= b && b <= c;
assert x >= 0 : "Negatives x";     // Text nach dem Doppelpunkt = Fehlermeldung

Aktivieren

Assertions kosten Rechenzeit und sind standardmäßig aus. Aktivieren ohne Neukompilieren:

java -ea Test                        // enable assertions
java -da Test                        // disable (Standard)
java -ea:Klassenname Test            // nur in einer Klasse
java -ea:paketname... Test           // nur in einem Paket (3 Punkte!)
// IntelliJ: Run → Edit Configurations → VM options: -ea

Wann verwenden?

geeignet
  • Bedingungen, die das Programm selbst garantiert
  • Rechenergebnisse am Methodenende (Postconditions)
  • Schleifeninvarianten
  • „Tote Zweige“ in if/switch: assert false;
nicht geeignet
  • Bedingungen, die von äußeren Einflüssen abhängen
  • z. B. Parameter am Anfang einer öffentlichen Methode prüfen
  • (Das macht man mit Exceptions.)

Exceptions: Grundprinzip

Fehlerinformationen werden in ein spezielles Objekt verpackt, eine Instanz von Exception (oder einer Unterklasse). Ausgelöst wird es automatisch oder mit throw, abgefangen mit catch an einer anderen Stelle.

Vorteile: (1) Fehler können nicht ignoriert werden, es muss Maßnahmen zur Behandlung geben. (2) Code für den normalen Ablauf und für die Fehlerbehandlung ist getrennt.

try – catch – finally

try {
    // regulärer Code, der eine Ausnahme auslösen könnte
} catch (ArithmeticException e) {      // spezifisch zuerst
    // Fehlerbehandlung
} catch (RuntimeException e) {         // dann allgemeiner
    // Fehlerbehandlung
} finally {
    // wird IMMER ausgeführt (Aufräumen, z. B. Datei schließen)
}

Ablauf selbst ausprobieren

Was wird geworfen, während risiko() läuft?

Ausgabe:

    Die Exception-Hierarchie

    Alle Ausnahmen sind Objekte in einer Klassenhierarchie. Ganz oben steht java.lang.Throwable mit Methoden zur Fehleranalyse.

    • Throwable
      • Error JVM-Fehler, nicht behandeln
        • OutOfMemoryError
      • Exception Benutzerausnahmen: müssen behandelt werden
        • IOException MyException (eigene) …
        • RuntimeException Systemausnahmen: Behandlung freiwillig
          • ArithmeticException (Division durch 0)
          • NullPointerException (Zugriff auf null)
          • ArrayIndexOutOfBoundsException
          • ClassCastException
    ArtAuslöserMuss behandelt werden?
    Systemausnahmen (runtime exceptions, unchecked)automatisch: illegale Instruktionen, Bugs im Programmnein, kann abgefangen werden
    Benutzerausnahmen (checked exceptions)explizit vom Programmierer mit throw (oder Bibliothek, z. B. IOException)ja: fangen oder mit throws weiterreichen. Sonst Compilerfehler.
    ErrorsProbleme der JVM, z. B. zu wenig Speichernein, darauf kann man nicht sinnvoll reagieren

    Wird eine Systemausnahme nicht behandelt, steigt sie im Stacktrace nach oben (Methode für Methode zurück bis zu main), bis die JVM stoppt und den Stacktrace ausgibt. Daran findet man die Fehlerstelle.

    Nützliche Methoden von Throwable

    String getMessage()Fehlertext
    String toString()Klassenname + getMessage()
    void printStackTrace()gibt den Stacktrace aus

    throw und throws

    throwthrows
    Anweisung im Methodenrumpf. Löst die Exception aus (throw new MyException();). Unterbricht den Code sofort.Teil der Methodensignatur. Warnt, dass die Methode eine Exception weitergeben kann (int test(int x) throws IOException). Behandelt wird sie dann beim Aufrufer.

    Zwei Wege, eine Benutzerausnahme zu behandeln: try-catch umrahmt den Fehler und behandelt ihn direkt. throws reicht ihn nach oben weiter. Irgendwo muss er dann behandelt werden.

    Eigene Exceptions

    Eigene Ausnahmen erben von Exception. Wer eine Fehlermeldung mitgeben will, schreibt einen eigenen Konstruktor, der super(...) aufruft.

    class MyException extends Exception {
        public MyException() { }
        public MyException(final String specialinfo) {
            super(specialinfo);                  // Text landet in getMessage()
        }
    }
    
    static void pruefe(int alter) throws MyException {   // Warnung in der Signatur
        if (alter < 0) {
            throw new MyException("Falsche Eingabe");     // auslösen
        }
    }
    
    try {
        pruefe(-1);
    } catch (MyException e) {
        System.out.println(e.getMessage());               // Falsche Eingabe
    } finally {
        System.out.println("fertig");
    }

    finally

    Der finally-Block läuft immer als Abschluss, egal ob eine Ausnahme auftrat oder nicht. Typisch: im try wird eine Datei geöffnet, im finally wird sie geschlossen. Sogar ein return im try-Block überspringt das finally nicht.

    Exception-Chaining

    Wenn Methoden Ausnahmen immer nur weiterreichen, werden die throws-Listen lang und unübersichtlich. Beim Chaining fängt man Ausnahmen von untergeordneten Aufrufen, verpackt sie in eine neue Exception und wirft diese weiter. Die ursprüngliche Ursache bleibt als cause erhalten.

    try {
        // Aufrufe, die E1 oder E2 werfen können
    } catch (E1 ex) {
        throw new MyException(ex);
    } catch (E2 ex) {
        throw new MyException(ex);
    }
    
    // In der eigenen Exception muss der Konstruktor passen:
    MyException(final Throwable cause) {
        super(cause);
    }
    
    // Alternative mit initCause():
    catch (E1 ex) {
        MyException m = new MyException();
        m.initCause(ex);
        throw m;
    }
    Merke
    • Assertion: assert bedingung : "Text";, Standard aus, Einschalten mit -ea, Fehler = AssertionError.
    • catch-Reihenfolge: spezifisch → allgemein. finally läuft immer.
    • Benutzerausnahme: fangen oder throws, sonst Compilerfehler. Systemausnahme: freiwillig.
    • throw löst aus, throws kündigt an.
    • Eigene Exception: extends Exception und super(text).
    • Java folgt dem Termination Model.
    Teil 5

    Praxis

    Im Test musst du Code lesen, Fehler erkennen und selbst Code schreiben. Hier alle drei Arten.

    A · Was gibt das Programm aus?

    Jedes Programm kompiliert. Überlege erst im Kopf, dann wähle.

    B · Kompiliert das?

    Entscheide, ob der Code ohne Compilerfehler übersetzt wird. Zu jedem Beispiel steht die Begründung dabei.

    C · Code schreiben

    Schreib die Lösung auf Papier oder in IntelliJ. Klappe sie erst danach auf und hake ab, was du richtig hattest.

    D · Lückentexte

    Teil 6

    Quiz

    Zum Schluss

    Spickzettel

    Alles Wichtige auf einer Seite. Lies es kurz vor dem Test noch einmal.

    Abstrakte Klasse vs. Interface

    Abstrakte KlasseInterface
    Schlüsselwortabstract class, extendsinterface, implements
    Mehrfach?nur 1 Oberklassebeliebig viele
    Objekt erzeugenneinnein
    Konstruktorjanein
    Objektvariablenjanein, nur Konstanten (public static final)
    Methodenabstrakte und normale gemischtimplizit public abstract; zusätzlich default und static mit Rumpf
    Zweckgemeinsamen Zustand und Basisimplementierung teilenVerhalten vorschreiben (Was, nicht Wie)

    Exceptions in einem Bild

    try {
        ...                       // kann Exception werfen
    } catch (Spezifisch e) {      // 1. passende catch-Klausel gewinnt
        ...
    } catch (Allgemein e) {       // Oberklasse ZULETZT
        ...
    } finally {
        ...                       // immer
    }
    
    void m() throws MyException { // kündigt an
        throw new MyException("Text");   // löst aus
    }
    
    assert x > 0 : "Text";       // java -ea  → AssertionError
    Muss ich fangen / deklarieren?
    Exception, IOException, eigene (extends Exception)Ja (Benutzerausnahme)
    RuntimeException und Unterklassen (ArithmeticException, NullPointerException, …)Nein (Systemausnahme)
    Error (OutOfMemoryError)Nein

    Typische Compilerfehler

    Typische Laufzeitfehler