Was sind abstrakte Klassen?

Abstrakte Klassen sind Klassen, die als Oberklassen dienen und selbst nicht zur Bildung von Objekten herangezogen werden können.

Sie werden oft verwendet, um Eigenschaften und Fähigkeiten einer allgemeinen Typgruppe zu definieren, deren abgeleitete Unterklassen diese dann weiter spezifizieren.

Abstrakte Klassen und Methoden werden bei der Deklaration durch das Schlüsselwort abstract spezifiziert. Abstrakte Methoden bestehen lediglich aus ihre Signatur, die mit einem Semikolon abgeschlossen werden muss. Als Zugriffsmodifikatoren dürfen nur public oder protected angegeben werden. Ein Methodenkörper fehlt an dieser Stelle völlig.
Abstrakte Klassen können jedoch auch Variablen und vollständig implementierte Methoden enthalten.

abstract class Fahrzeug {
    Point pos = new Point(0,0);
    abstract void bewegDich(int h, int v);
    Point getPos() {
        return pos;
    }
}

Das Beispiel demonstriert dies anhand einer Klasse Fahrzeug. Sie definiert ein Point -Objekt als Standpunkt eines jeden Fahrzeugs, die zugehörige Getter-Methode, sowie die Signatur der abstrakten Methode bewegDich(), die die Bewegung eines Fahrzeugs durch Ändern der Position simulieren soll.
Die Klassen Motorrad und Luftfahrzeug sind von ihr abgeleitet. Sie zeigen, dass abstrakte Klassen problemlos als Elternklassen weiterer abstrakter Klassen dienen können.

abstract class Motorrad extends Fahrzeug {
    //...
}

abstract class Luftfahrzeug extends Fahrzeug {
    Point fliege(int h, int v) {
        return new Point(getPos().x + h, getPos().y + v);
    }
}

In Luftfahrzeug wurde mit fliege(int h, int v) eine nicht abstrakte Methode hinzugefügt, obwohl die Klasse selbst auch wieder abstract deklariert wurde. Eine abstrakte Klasse muss somit keine abstrakten Methoden enthalten. Eine abstrakte Methode kann jedoch nur in einer abstrakten Klasse stehen. Die Implementierung findet dann in der abgeleiteten, nicht abstrakten Unterklasse statt.

class Norton extends Motorrad {
    @Override
    void bewegDich(int h, int v) {
        pos = new Point(h, v);
    }
	
    void fahre(int h){
        bewegDich(h, 0);
    }
}

Dies geschieht im Beispiel mit der Methode bewegDich() in der Klasse Norton. Hier wird die Instanzvariable der Elternklasse mit einem neuen Point-Objekt belegt. Sie wird in der Methode fahre() aufgerufen, die einen Parameter für die Vorwärtsbewegung übergeben bekommt.
Anders verhält es sich bei der Klasse Cesna. In ihr wird innerhalb der Methode bewegDich() die Methode fliege() der Elternklasse Luftfahrzeug aufgerufen und so die Instanzvariable pos der abstrakten Elternklasse Fahrzeug belegt.

class Cesna extends Luftfahrzeug {
    @Override
    void bewegDich(int h, int v) {
        pos = fliege(h, v);
    }
}
			
			

Zusammenfassend hier der gesamte Quelltext in der Übersicht:

import java.awt.Point;

public class AbstractClassBsp {

    public static void main(String[] args) {
        Norton norton = new Norton();
        System.out.println("Ausgangspunkt: " + norton.getPos().x);
        norton.fahre(10);
        System.out.println("Fahrtziel: " + norton.getPos().x);
        
        // Luftfahrzeug lfz = new Luftfahrzeug();    // Fehler
        
        Cesna cesna = new Cesna();
        cesna.bewegDich(2000, 150);
        System.out.println("Flugziel: " + cesna.getPos().x + ", Hoehe: " + cesna.getPos().y);
    }
}

abstract class Fahrzeug {
    Point pos = new Point(0,0);
    
    abstract void bewegDich(int h, int v);
    
    Point getPos() {
        return pos;
    }
}

abstract class Motorrad extends Fahrzeug {
    //...
}

class Norton extends Motorrad {

    @Override
    void bewegDich(int h, int v) {
        pos = new Point(h, v);
    }
    
    void fahre(int h){
        bewegDich(h, 0);
    }
}

abstract class Luftfahrzeug extends Fahrzeug {
    Point fliege(int h, int v) {
        return new Point(getPos().x + h, getPos().y + v);
    }
}

class Cesna extends Luftfahrzeug {
    @Override
    void bewegDich(int h, int v) {
        pos = fliege(h, v);
    }
}