Abstract Classes

An abstract class is a class that is deliberately left incomplete — it can declare methods with no body at all, forcing every concrete subclass to fill in the missing pieces.

An abstract class is a class declared with the abstract keyword that cannot be instantiated directly. It exists to be extended, and it can mix fully implemented (concrete) methods with abstract methods — methods that have a signature but no body, which every non-abstract subclass is required to implement.

Declaring an Abstract Class

Adding abstract before class marks the class as incomplete by design; the compiler enforces that it cannot be instantiated with new, even though it can still have fields, constructors, and fully working methods.

Shape mixes an abstract method (area()) with a concrete method (printArea()) and a normal constructor — all of this is legal in an abstract class.

public abstract class Shape {
    protected String name;

    public Shape(String name) {
        this.name = name;
    }

    public abstract double area(); // no body -- must be supplied by subclasses

    public void printArea() {       // concrete method -- has a full body
        System.out.println(name + " area: " + area());
    }
}

Abstract Methods

An abstract method is declared with the abstract keyword, has a signature and return type, but ends with a semicolon instead of a body. It can only appear inside an abstract class (or an interface). Any concrete (non-abstract) subclass must override it and supply an implementation, or the subclass itself must also be declared abstract.

Circle must provide area() because it is not itself declared abstract; leaving it out would be a compile error.

abstract class Shape {
    abstract double area(); // no braces, no body
}

class Circle extends Shape {
    private final double radius;
    Circle(double radius) { this.radius = radius; }

    @Override
    double area() { // required -- Circle is concrete
        return Math.PI * radius * radius;
    }
}

Concrete Methods in an Abstract Class

An abstract class is free to include fully implemented methods alongside its abstract ones. These concrete methods are inherited by subclasses exactly like methods from any other superclass, and are commonly used to implement shared logic once, often built on top of the abstract methods subclasses will supply.

Every subclass of Employee automatically gets printPaySlip() for free, built on whatever monthlyPay() calculation that subclass provides.

abstract class Employee {
    protected String name;
    Employee(String name) { this.name = name; }

    abstract double monthlyPay(); // subclass-specific

    void printPaySlip() {          // shared logic, reused by every subclass
        System.out.println(name + ": $" + monthlyPay());
    }
}

Constructors in Abstract Classes

An abstract class can — and often should — declare constructors, even though it can never be instantiated directly. Its constructor runs whenever a concrete subclass is constructed, via an implicit or explicit super(...) call, just like any other superclass constructor.

Account's constructor still does real initialization work — setting owner — even though no bare Account object can ever exist.

abstract class Account {
    protected final String owner;

    protected Account(String owner) { // runs when any subclass is constructed
        this.owner = owner;
    }

    abstract double interestRate();
}

class SavingsAccount extends Account {
    SavingsAccount(String owner) {
        super(owner); // required: initializes the abstract class's fields
    }

    @Override
    double interestRate() { return 0.02; }
}

Fields in Abstract Classes

Abstract classes can declare instance fields exactly like any other class, and those fields are inherited by every subclass. This is a key practical advantage over interfaces (see the Interfaces topic), which traditionally cannot hold instance state.

mileage is real per-object state that every subclass of Vehicle shares the mechanism for, without redeclaring it.

abstract class Vehicle {
    protected int mileage = 0; // ordinary instance field, inherited by all subclasses

    void drive(int miles) {
        mileage += miles;
    }
}

Cannot Instantiate an Abstract Class

Attempting new AbstractClassName(...) is always a compile error, regardless of whether the abstract class has a public constructor — abstract explicitly forbids direct instantiation. Only a concrete (non-abstract) subclass, with all abstract methods implemented, can be instantiated.

Even though the reference is declared as the abstract type Shape, the actual object created must be of a concrete subclass.

abstract class Shape {
    abstract double area();
}

// Shape s = new Shape(); // compile error: Shape is abstract; cannot be instantiated

class Square extends Shape {
    private final double side;
    Square(double side) { this.side = side; }
    @Override double area() { return side * side; }
}

Shape s = new Square(4); // OK: Square is concrete

Use Cases

Abstract classes are the right tool when a group of related classes share both common state and common implemented behavior, but also need each subclass to fill in some specific piece of logic — this is the basis of the Template Method design pattern, where an abstract class defines an algorithm's overall structure using concrete methods, and leaves specific steps as abstract methods for subclasses to define.

generate() is marked final so the overall structure (header, body, footer) can never be overridden — only the printBody() step varies per subclass.

abstract class ReportGenerator {
    // template method: defines the overall algorithm, calls abstract steps
    final void generate() {
        printHeader();
        printBody();  // subclass-specific step
        printFooter();
    }

    private void printHeader() { System.out.println("=== Report ==="); }
    private void printFooter() { System.out.println("=== End ==="); }

    abstract void printBody(); // each subclass defines its own content
}

class SalesReport extends ReportGenerator {
    @Override void printBody() { System.out.println("Sales figures..."); }
}

Practical Usage

Choose an abstract class over an interface when subclasses genuinely need to share instance state or inherited implementation, and there is a real is-a relationship among them (a single abstract superclass, since Java only supports single class inheritance). Choose an interface (covered in its own topic) when unrelated classes need to share only a contract, possibly across multiple unrelated hierarchies.

Complete Example

Every Instrument shares tuneUp() automatically, while play() is defined independently by each concrete subclass — this is the abstract class pattern working end to end.

abstract class Instrument {
    protected final String name;

    protected Instrument(String name) {
        this.name = name;
    }

    abstract void play(); // subclass-specific sound

    void tuneUp() {        // shared, concrete behavior
        System.out.println(name + " is being tuned...");
    }
}

class Guitar extends Instrument {
    Guitar() { super("Guitar"); }
    @Override void play() { System.out.println("Strum!"); }
}

class Piano extends Instrument {
    Piano() { super("Piano"); }
    @Override void play() { System.out.println("Plink!"); }
}

public class Main {
    public static void main(String[] args) {
        Instrument[] band = { new Guitar(), new Piano() };
        for (Instrument i : band) {
            i.tuneUp(); // shared concrete method
            i.play();    // subclass-specific abstract method
        }
    }
}

Mental Model

Think of an abstract class as a partially filled-in form: some sections (concrete methods, fields, constructors) are already completed and shared by everyone who uses the form, while other sections (abstract methods) are deliberately left blank, marked "must be filled in by whoever uses this" — and the form itself can never be submitted (instantiated) until every blank is filled.

Quick Reference

Feature                        Abstract class support
------------------------------ ----------------------------------------------------
Instance fields                  yes
Constructors                     yes (run when a concrete subclass is constructed)
Concrete (implemented) methods   yes
Abstract methods                 yes -- no body, must be overridden by concrete subclass
Direct instantiation (new)       NOT allowed, even with a public constructor
Number of abstract superclasses  at most one, per Java's single class inheritance rule

Key Takeaways

An abstract class is declared with abstract and cannot be instantiated directly.

It can mix abstract methods (no body) with fully implemented concrete methods, fields, and constructors.

Every concrete subclass must implement all inherited abstract methods, or itself be declared abstract.

Abstract classes are ideal when subclasses share real state and implementation, not just a contract.

The Template Method pattern uses a concrete method to fix an algorithm's structure while abstract methods vary its steps.

abstract class Shape { abstract double area(); } Shape s = new Shape(); // compile error: Shape is abstract; cannot be instantiated abstract class Shape { abstract double area(); } class Circle extends Shape { double radius = 2; @Override double area() { return Math.PI * radius * radius; } } Shape s = new Circle(); // OK: Circle is concrete

An abstract class, regardless of what constructors it declares, can never be instantiated directly — only a concrete subclass can be constructed.

abstract class Vehicle { abstract void drive(); } class Car extends Vehicle { // forgot to implement drive() -- compile error } abstract class Vehicle { abstract void drive(); } class Car extends Vehicle { @Override void drive() { System.out.println("driving"); } }

Every abstract method must be implemented by a concrete subclass; leaving one out means the subclass would itself need to be declared abstract, or it fails to compile.

abstract class Shape { abstract double area() { return 0; } // compile error: abstract methods cannot have a body } abstract class Shape { abstract double area(); // no body -- correct double defaultArea() { // if a default is genuinely wanted, use a separate concrete method return 0; } }

An abstract method is defined by having no body at all; giving it braces and a body is a compile error, since that would make it a concrete method instead.

abstract class Base { abstract void step(); } abstract class Mid extends Base { // Mid does not implement step() -- this is fine ONLY because Mid is also abstract } class Leaf extends Mid { // forgot step() again -- compile error here, since Leaf is concrete } abstract class Base { abstract void step(); } abstract class Mid extends Base { } class Leaf extends Mid { @Override void step() { System.out.println("done"); } }

An abstract subclass may legally leave inherited abstract methods unimplemented and propagate the obligation further down, but the first concrete class in the chain must implement everything.

Can an abstract class have a constructor, and if so, when does it run?

Yes. It runs whenever a concrete subclass object is constructed, via an implicit or explicit super(...) call — exactly like any other superclass constructor — even though the abstract class itself can never be instantiated directly.

What must a concrete (non-abstract) subclass of an abstract class do regarding inherited abstract methods?

It must provide an implementation (override) for every inherited abstract method, or it must itself be declared abstract, deferring the obligation further down the hierarchy.

Can an abstract class contain fully implemented (concrete) methods?

Yes — an abstract class can freely mix concrete methods, abstract methods, fields, and constructors; only the presence of at least one abstract method (or the explicit abstract keyword on the class) is required to make the class itself abstract.

Why can't new be used directly on an abstract class?

An abstract class is explicitly incomplete by design — it may declare abstract methods with no implementation — so an instance of it would have no defined behavior for those methods; Java forbids this at compile time.

What is the Template Method pattern, and how does it use abstract classes?

It is a design where a concrete method in an abstract class defines the fixed overall structure of an algorithm, calling one or more abstract methods at specific steps; each concrete subclass supplies those steps' implementations while the overall structure stays the same across all subclasses.

When would you choose an abstract class over an interface for a hierarchy of related classes?

When the classes genuinely share instance state and/or common implemented behavior, and a true single-superclass is-a relationship exists among them — since Java allows only one abstract (or any) superclass but multiple interfaces.

Interview-style: why does Java allow an abstract class to have a constructor even though it can never be "new"ed directly?

Because the constructor's real job is initializing the shared state (fields) that every concrete subclass inherits — it runs as part of constructing the concrete subclass object, not as a way to create a standalone abstract-class instance, which remains forbidden.

Write an abstract Appliance class with a concrete method powerOn() (prints a message) and an abstract method run(), then two concrete subclasses (Toaster, Blender) implementing run().

abstract class Appliance { protected final String name; protected Appliance(String name) { this.name = name; } void powerOn() { System.out.println(name + " powering on..."); } abstract void run(); } class Toaster extends Appliance { Toaster() { super("Toaster"); } @Override void run() { System.out.println("Toasting bread"); } } class Blender extends Appliance { Blender() { super("Blender"); } @Override void run() { System.out.println("Blending"); } } powerOn() is shared, concrete behavior inherited unchanged by both subclasses, while run() must be independently implemented by each, since Appliance leaves it abstract.

What is printed, and how does computeTwice() work without knowing what compute() actually does?

Prints 20. computeTwice() is a concrete method in the abstract class that calls the abstract compute() method twice; because AddFive supplies compute(x) = x + 5, computeTwice(10) evaluates compute(compute(10)) = compute(15) = 20 — the abstract class defines the reuse pattern without needing to know the concrete arithmetic.

Write an abstract DataExporter class with a template method export() that calls abstract fetchData() and abstract formatData(), plus a concrete CsvExporter subclass.

abstract class DataExporter { final void export() { String data = fetchData(); String formatted = formatData(data); System.out.println(formatted); } abstract String fetchData(); abstract String formatData(String raw); } class CsvExporter extends DataExporter { @Override String fetchData() { return "name,age\nAda,36"; } @Override String formatData(String raw) { return "CSV:\n" + raw; } } export() is final so its two-step structure (fetch, then format) can never be changed by a subclass — only the two abstract steps vary, which is the Template Method pattern.

What does this print?

Prints 18.0. Square(3).area() computes 3*3=9.0, and describe() (a concrete method inherited from Shape) doubles it via area() * 2, giving 18.0 — describe() relies on the abstract area() being filled in by the concrete subclass.

abstract class Base {
    abstract int compute(int x);
    int computeTwice(int x) {
        return compute(compute(x));
    }
}
class AddFive extends Base {
    @Override int compute(int x) { return x + 5; }
}
public class Main {
    public static void main(String[] args) {
        Base b = new AddFive();
        System.out.println(b.computeTwice(10));
    }
}
abstract class Shape {
    abstract double area();
    double describe() {
        return area() * 2;
    }
}
class Square extends Shape {
    double side;
    Square(double side) { this.side = side; }
    @Override double area() { return side * side; }
}
System.out.println(new Square(3).describe());