21. Abstract Classes
An abstract class cannot be instantiated and may contain abstract methods (no body) that subclasses must implement, alongside concrete methods, fields, and constructors โ use it when you want to share state or a partial implementation.
Java 21 (core language)Explanation
Abstract classes sit between concrete classes (full implementation) and interfaces (pure contract). The key advantages: they can hold state (instance fields), have constructors (called via super() in subclasses), and provide concrete methods that subclasses inherit without change.
Use an abstract class when: multiple classes share meaningful common state or implementation (a Template Method pattern), you want to provide a protected internal API, or you need constructor logic to run before the subclass completes initialisation.
Prefer interfaces over abstract classes in most cases โ especially since Java 8 added default methods to interfaces. The interface approach allows a class to satisfy multiple contracts (implements A, B), while abstract class inheritance is limited to one (extends). The classic rule: choose interface for the "can-do" capability, abstract class for the "is-a" shared skeleton.
Example
public class AbstractClassDemo {
abstract static class Report {
private final String title; // state shared by all reports
private final String author;
Report(String title, String author) {
this.title = title;
this.author = author;
}
// Template Method pattern: concrete structure, abstract steps
public final void generate() {
System.out.println("=== " + title + " by " + author + " ===");
printBody(); // abstract โ subclass fills in
printFooter(); // concrete default
}
protected abstract void printBody();
protected void printFooter() {
System.out.println("--- End of report ---");
}
public String getTitle() { return title; }
}
static class SalesReport extends Report {
private final double total;
SalesReport(String author, double total) {
super("Sales Report", author);
this.total = total;
}
@Override
protected void printBody() {
System.out.printf("Total sales: $%.2f%n", total);
}
}
static class AuditReport extends Report {
AuditReport(String author) { super("Audit Report", author); }
@Override
protected void printBody() {
System.out.println("All systems nominal.");
}
@Override
protected void printFooter() {
System.out.println("Verified by compliance team.");
}
}
public static void main(String[] args) {
Report[] reports = {
new SalesReport("Alice", 12_500.75),
new AuditReport("Bob")
};
for (Report r : reports) {
r.generate();
System.out.println();
}
}
}Sources
- https://docs.oracle.com/javase/tutorial/java/IandI/abstract.html โ Abstract methods and classes
- https://dev.java/learn/oop/abstract-classes/ โ Abstract classes on dev.java