OOP Basics ·
‹ Previous Next ›
⏱ 5 min read Modified: 2026-08-08

Abstract Classes and Methods in Java

The line Figure figure = new Figure(10, 10); looks completely harmless — the class has fields, it has a constructor, everything is in place. The compiler still refuses: Figure is abstract; cannot be instantiated. A single keyword in the class declaration permanently forbids creating objects of that class, and that is not a limitation but the whole point of the construct.

An abstract class in Java is a class declared with the abstract modifier: you cannot create an object of it directly, and it exists to serve as a common parent for subclasses. An abstract method is a method with no body — it has a signature, but no implementation. In plain English, "abstract" means "existing as an idea, not tied to any one concrete thing", which is exactly what these classes and methods are: they describe what subclasses can do without saying how.

Abstract method: syntax and rules

Abstract methods are methods that have no implementation. Instead of a body in curly braces after the signature, you simply put a semicolon.

General form:

abstract returnType methodName(parameterList);

Example:

public abstract double calculateArea();

Rules for an abstract method:

  • it has no body — only a signature followed by a semicolon; an empty body { } is not the same thing, that is already an ordinary method;
  • it must be implemented by the first concrete (non-abstract) subclass in the inheritance chain — an intermediate subclass may stay abstract and pass the obligation further down;
  • it can only be declared inside an abstract class or an interface;
  • it cannot be combined with static, final or private.

Abstract class and the abstract keyword

Any class that contains at least one abstract method must itself be declared abstract. To do that, put the keyword abstract before class:

public abstract class Figure {
    // ...
    public abstract double calculateArea();
}

An abstract class cannot have objects of its own, but its type can still be used for references to objects of its subclasses:

public class Demo {
    public static void main(String[] args) {
        // Figure figure = new Figure(10, 10); // compile-time error:
        // Figure is abstract; cannot be instantiated

        Figure figure = new Rectangle(10, 20); // fine: a Figure reference
        System.out.println(figure.calculateArea());
    }
}

Important

The rule does not work in reverse: a class can be abstract without a single abstract method. Such a class still cannot be instantiated — it is a legitimate way of saying "use my subclasses, not me". The JDK does exactly this with java.util.Calendar, for instance.

What abstraction in Java actually means

Abstraction is one of the four pillars of OOP, alongside encapsulation, inheritance and polymorphism. In practical terms, class abstraction in Java means working with an object through its general contract, without knowing which implementation is behind it: a variable, a method parameter or a collection element is declared with the common abstract type, and the concrete object is plugged in at run time.

public static double totalArea(Figure[] figures) {
    double sum = 0;
    for (Figure figure : figures) { // the method knows nothing about Rectangle or Triangle
        sum += figure.calculateArea();
    }
    return sum;
}

Java gives you two tools for this: the abstract class and the interface. An abstract class is the right one when subclasses are variations of a single entity and can share state and ready-made code; the comparison table below shows where the two differ.

Example: Figure, Rectangle and Triangle

Let's reuse the Figure, Triangle and Rectangle classes from the "Overriding Methods" lesson. There, Figure was a plain class whose calculateArea() returned a meaningless zero. Conceptually, Figure describes an abstract shape that has no area of its own, so it makes sense to turn calculateArea() into an abstract method — and the class itself into an abstract class.

Abstract class Figure and its subclasses Rectangle and Triangle in Java

public abstract class Figure {
    double dimension1;
    double dimension2;

    public Figure(double dimension1, double dimension2) {
        this.dimension1 = dimension1;
        this.dimension2 = dimension2;
    }

    public abstract double calculateArea();
}

Notice that the abstract class still has fields and a constructor, even though it never has objects of its own. The constructor exists for the subclasses — they call it through super(...) to initialise the inherited part of their state.

Any subclass of an abstract class must either implement all inherited abstract methods, or be declared abstract itself:

public class Rectangle extends Figure {
    public Rectangle(double dimension1, double dimension2) {
        super(dimension1, dimension2);
    }

    @Override
    public double calculateArea() {
        System.out.println("Calculating the area of a rectangle.");
        return dimension1 * dimension2;
    }
}
public class Triangle extends Figure {
    public Triangle(double dimension1, double dimension2) {
        super(dimension1, dimension2);
    }

    @Override
    public double calculateArea() {
        System.out.println("Calculating the area of a triangle.");
        return dimension1 * dimension2 / 2;
    }
}

Tip

Always put @Override above the implementation of an abstract method. If you misspell the name or get the parameter list wrong, the compiler tells you immediately; without the annotation you would just declare a brand-new method, the abstract one would stay unimplemented, and you would get a confusing error somewhere else in the file instead.

An array of an abstract type and polymorphism

You cannot have objects of an abstract class, but you can absolutely create an array (or a List) of the abstract type: it simply holds references to objects of its subclasses.

public class FindAreasExample {
    public static void main(String[] args) {
        Figure[] figures = new Figure[3];

        // figures[0] = new Figure(10, 10); // will not compile: Figure is abstract

        figures[0] = new Rectangle(10, 10);
        figures[1] = new Rectangle(20, 10);
        figures[2] = new Triangle(10, 10);

        for (Figure figure : figures) {
            double area = figure.calculateArea();
            System.out.println(area);
        }
    }
}

Program output:

Calculating the area of a rectangle.
100.0
Calculating the area of a rectangle.
200.0
Calculating the area of a triangle.
50.0

Compile and run the example like this:

javac Figure.java Rectangle.java Triangle.java FindAreasExample.java
java FindAreasExample

At compile time the only type visible is Figure, and the call is allowed because the method is declared in the abstract class. Which implementation actually runs is decided by the JVM at run time, from the real type of the object — this is dynamic dispatch (late binding).

What is allowed and not allowed in an abstract class

Element Allowed? Why
Abstract methods Yes Zero, one or many — there is no limit
Regular methods with a body Yes Ready-made behaviour shared by all subclasses
Fields, including non-final ones Yes Ordinary mutable state; an interface cannot do this
Constructor Yes Invoked from a subclass via super(...)
Static methods, static fields, static blocks Yes But a static method itself cannot be abstract
main method Yes It is static, so it runs without an instance of the class
Creating an object with new No Figure is abstract; cannot be instantiated. The new Figure(...) { ... } syntax is not an exception — it creates an anonymous subclass
abstract + final No A contradiction: final blocks inheritance, and an abstract class is useless without subclasses
abstract + static on a method No Static methods are not overridden, so there is no way to plug an implementation in
abstract + private on a method No A private method is invisible to subclasses, so it cannot be implemented there
Abstract constructor No Constructors are never inherited or overridden

Modern Java

Since Java 17 an abstract class can also be sealed: public sealed abstract class Figure permits Rectangle, Triangle { }. The class still cannot be instantiated, but now the list of subclasses is fixed and known to the compiler — which lets switch pattern matching check that you have covered every case.

Abstract classes in the JDK: AbstractList example

The standard library is full of abstract classes whose names start with Abstract: AbstractList, AbstractMap, AbstractSet, InputStream, Number. They are skeletal implementations: the class writes out all the repetitive code for you and leaves only a couple of abstract methods to fill in.

java.util.AbstractList is the clearest example. Implement just get(int) and size(), and you get a working read-only List with iterator(), indexOf(), contains(), equals(), hashCode() and toString() already provided:

import java.util.AbstractList;

public class SquaresList extends AbstractList<Integer> {
    private final int size;

    public SquaresList(int size) {
        this.size = size;
    }

    @Override
    public Integer get(int index) {
        return index * index;
    }

    @Override
    public int size() {
        return size;
    }
}

// new SquaresList(5) behaves like a List: [0, 1, 4, 9, 16]

Writing the same class from scratch as implements List<Integer> would mean implementing about 25 methods by hand. That is the practical value of an abstract class: shared code lives in the parent, and the subclass only supplies what is genuinely unique to it.

Abstract class vs interface

Since default methods arrived in Java 8 and private interface methods in Java 9, the line between an interface and an abstract class has become thinner — but it has not disappeared.

Criterion Abstract class Interface
How many you can extend / implement Only one As many as you like
Mutable state (fields) Yes, any fields No, only public static final constants
Constructor Has one Does not have one
Ready-made code Any methods with a body default, static and (since Java 9) private methods
Access modifiers on members Any, including protected and private public by default; private allowed for helper methods only
When to choose it Subclasses are variations of one entity and share state and code You need a contract or a role for otherwise unrelated classes

The JDK routinely uses both at once: List is the interface (the contract), AbstractList is the abstract class (the shared implementation), and ArrayList is the concrete class you actually instantiate.

Where developers get tripped up

  • Trying to instantiate an abstract class. new Figure(10, 10) will not compile, even though the class has a perfectly good constructor.
  • Forgetting to implement a method in a subclass. The compiler then demands either an implementation or that the subclass itself be marked abstract.
  • Narrowing visibility in the implementation. A method declared public abstract cannot be implemented as protected — when overriding you may widen access, never restrict it.
  • Confusing "abstract" with "empty". A method with a body like { } or return 0; is an ordinary method: nobody is forced to override it, and the bug surfaces at run time instead of at compile time.
  • Making a class abstract when nothing extends it. An abstraction with no implementation behind it is just code that can never run.
  • Calling an abstract method from the constructor. The subclass overrides it, but at that moment the subclass fields are not initialised yet — you get 0 or null instead of real values.

Practice

Write an abstract class Employee with fields name and baseSalary, a constructor and an abstract method double calculateSalary(). Add two subclasses: Manager (base salary plus a 20% bonus) and Developer (base salary plus a fixed 500 for overtime). Put the objects into an Employee[] array and print the total payroll in a loop.

Self-check: try adding a third subclass without implementing calculateSalary() and read the compiler message carefully — it is the same one you will meet in interviews.

Official reference: Abstract Methods and Classes — The Java Tutorials.

Frequently asked questions

Can you create an object of an abstract class?

Not directly: new Figure(10, 10) is a compile-time error. What you can do is create an object of an anonymous subclass — new Figure(10, 10) { public double calculateArea() { return 0; } } compiles fine, because it does not create a Figure, it creates an unnamed subclass that implements the abstract method.

Why does an abstract class need a constructor if it never has objects?

The constructor of an abstract class initialises the inherited fields and is invoked from a subclass constructor via super(...). If a subclass does not call it explicitly, the compiler inserts a call to super() automatically — so if the abstract class has no no-argument constructor, the subclass will not compile at all.

What happens if a subclass does not implement an abstract method?

The compiler reports an error along the lines of "is not abstract and does not override abstract method". There are two ways out: implement the method, or declare the subclass abstract as well and pass the obligation to the next class in the inheritance chain.

Can an abstract class extend a concrete class?

Yes. An abstract class can extend any non-abstract class and add abstract methods on top of it — this is a common way to make an existing class non-instantiable and force subclasses to fill in a missing step. The reverse works too: a concrete class can extend an abstract one as long as it implements every inherited abstract method.

What is the difference between abstraction and encapsulation?

Abstraction is about design: it decides what a type exposes and hides the implementation behind a general contract such as Figure or List. Encapsulation is about access control: it hides the internal data of an object behind private fields and accessor methods so that the state cannot be corrupted from outside. Abstraction hides complexity, encapsulation hides data — and normally you use both at the same time.

Comments

Please log in or register to have a possibility to add comment.