Abstract Class in Java: When and Why to Use It (Examples)

  • Last Updated: January 6, 2026
  • By: javahandson
  • Series
img

Abstract Class in Java: When and Why to Use It (Examples)

Learn what an abstract class in Java is, when to use it versus an interface, and why it matters — with clear, runnable code examples.

1. Introduction

An abstract class in Java is a class you cannot create an object of. It sits there waiting for a child class to finish the job.

That sounds odd at first. Why write a class nobody can use directly?

Think about the word “vehicle”. You have never seen a vehicle. You have seen a car, a bike, and a bus. Vehicle is a useful idea, but nothing in the world is just a vehicle and nothing else.

Shapes work the same way. Every shape has an area. Yet you cannot work out the area until you know whether it is a circle or a square.

An abstract class captures exactly that. It holds the parts every child shares. It also marks the parts each child must supply on its own. Then it blocks anyone from creating a half-finished object.

1.1 What This Article Covers

Here is the plan:

  • The design problem that plain classes cannot solve
  • How to write an abstract class and an abstract method
  • Why new fails, and the one line that looks like an exception
  • Constructors, and why an abstract class has them
  • Abstract class against interface, with a table and a rule
  • Multilevel chains, and mixing in interfaces
  • The template method pattern, built step by step
  • Common mistakes and ten interview questions

You need to know extends before starting. If that feels shaky, read our guide to inheritance in Java first.

2. Why Abstract Classes Exist

2.1 The Problem With a Plain Parent

Start with a normal parent class. Every method needs a body, so you write one.

class Vehicle {
    void move() {
        System.out.println("Vehicle is moving");
    }
}

class Car extends Vehicle { }
class Boat extends Vehicle { }

Now look at what you built. A car moves on roads. A boat moves on water. “Vehicle is moving” describes neither one.

Worse, both children compile happily without overriding anything. Call move() on a Boat and you get that vague line. Nothing warned you.

The parent made a promise it had no business making. It claimed to know how every vehicle moves, and it does not.

2.2 Three Bad Workarounds

Developers usually patch this in one of three ways. All three hurt.

  • A vague body. Print something generic and hope every child overrides it. Nothing forces them to.
  • An empty body. Leave the braces empty. Now a missing override fails silently at runtime.
  • Throwing on purpose. Throw UnsupportedOperationException from the parent. That turns a compile-time problem into a crash in production.

Notice the common thread. Each workaround pushes a mistake later, into runtime, where it costs far more to find.

2.3 How abstract Fixes It

The abstract keyword lets the parent say something honest. “Every vehicle moves, but I refuse to guess how.”

abstract class Vehicle {
    abstract void move();     // no body, on purpose
}

class Car extends Vehicle {
    @Override
    void move() {
        System.out.println("Car moves on roads");
    }
}

class Boat extends Vehicle {
    @Override
    void move() {
        System.out.println("Boat moves on water");
    }
}

Now the compiler does the policing. Skip that override and your code will not build.

You also gained something quieter. Nobody can write new Vehicle() any more, so a meaningless vehicle object can never exist.

2.4 Where You Have Already Used One

You have been using abstract classes for a while now, probably without noticing. The Java library leans on them heavily.

  • Number. Integer, Double, and Long all extend it. Try new Number() and the compiler says no.
  • InputStream. Buffering and skipping come for free, while the actual read() falls to each source.
  • AbstractList. Most of the List methods are written once, so a custom list only supplies get() and size().
  • HttpServlet. The request plumbing is sorted out already, and your class fills in doGet or doPost.

Look at the shape they share. Each one does the boring common work and leaves a small, well-marked hole.

That is the pattern worth copying. An abstract class pays off when the shared part is real code, not just a list of method names.

3. Declaring an Abstract Class

3.1 The abstract Keyword

One keyword in front of class does the whole job.

abstract class Vehicle {
    // fields, constructors, and methods go here
}

Here is a fact that surprises most beginners. An abstract class does not need a single abstract method.

Mark a class abstract with nothing but ordinary methods inside, and it still works. You lose the ability to create objects of it, which is sometimes the entire point.

3.2 What It May Contain

An abstract class is a real class. It holds almost everything a normal class holds:

  • Instance fields, including private ones
  • Constructors, which run when a child object appears
  • Concrete methods with full bodies
  • Abstract methods with no body at all
  • Static methods, static blocks, and constants
  • A main method, which runs just fine

Only one thing sets it apart. The new keyword refuses to work on it.

3.3 A Complete Example

This one puts every piece together.

package com.javahandson;

abstract class Vehicle {

    int speed;                          // instance field

    Vehicle(int speed) {                // constructor
        this.speed = speed;
    }

    abstract void move();               // abstract method, no body

    void start() {                      // concrete method
        System.out.println("Started at speed " + speed);
    }

    static void info() {                // static method
        System.out.println("This is a vehicle");
    }
}

class Car extends Vehicle {

    Car(int speed) {
        super(speed);
    }

    @Override
    void move() {
        System.out.println("Car moves on roads");
    }
}

public class Main {
    public static void main(String[] args) {
        Vehicle.info();

        Car car = new Car(70);
        car.start();
        car.move();
    }
}
// Output:
// This is a vehicle
// Started at speed 70
// Car moves on roads

Read the split carefully. Vehicle knows how to start, because every vehicle starts the same way. Vehicle has no idea how to move, so it hands that to Car.

4. Abstract Methods

4.1 A Method With No Body

An abstract method gives you a signature and a semicolon. No braces, no code.

abstract void move();          // correct

abstract void move() { }       // error: abstract methods cannot have a body

The second line looks harmless, yet the compiler rejects it. Empty braces still count as a body.

One more rule follows from this. An abstract method may only live inside an abstract class. Put one in a normal class and the build fails.

4.2 The Mandatory Override Rule

Every concrete child must supply a body for every abstract method it inherits. Miss one and the compiler stops you.

abstract class Vehicle {
    abstract void move();
    abstract void stop();
}

class Car extends Vehicle {
    @Override
    void move() {
        System.out.println("Car moves on roads");
    }
    // stop() is missing
}
// Output:
// java: com.javahandson.Car is not abstract and does not override
//       abstract method stop() in com.javahandson.Vehicle

Read that message closely. It offers you a choice, and most people miss it.

Either write the missing method, or mark Car abstract too. Both fixes compile.

4.3 Passing the Job Down the Chain

That second fix is genuinely useful. A middle class can handle some methods and leave the rest for later.

package com.javahandson;

abstract class Vehicle {
    abstract void move();
    abstract void stop();
}

abstract class MotorVehicle extends Vehicle {
    @Override
    void stop() {                       // handled here
        System.out.println("Brakes applied");
    }
    // move() still abstract, still someone else's job
}

class Car extends MotorVehicle {
    @Override
    void move() {                       // finished here
        System.out.println("Car moves on roads");
    }
}

public class Main {
    public static void main(String[] args) {
        Car car = new Car();
        car.move();
        car.stop();
    }
}
// Output:
// Car moves on roads
// Brakes applied

Each level adds what it knows. The chain ends when a concrete class fills the last gap.

4.4 Keywords That Clash With abstract

Some combinations make no sense, so Java bans them:

  • abstract + final on a class. One says “extend me”, the other says “do not”. Pick one.
  • abstract + private on a method. A child cannot see it, so a child could never override it.
  • abstract + static on a method. Static methods belong to the class and never take part in overriding.
  • abstract + final on a method. Same clash as the class version.

Each ban follows the same logic. An abstract member exists so somebody can override it, and each of these keywords blocks overriding.

5. Why You Cannot Create the Object

5.1 The Compile-Time Error

Try new on an abstract class and the build stops.

package com.javahandson;

abstract class Vehicle {
    abstract void move();
}

public class Main {
    public static void main(String[] args) {
        Vehicle v = new Vehicle();      // not allowed
    }
}
// Output:
// java: Vehicle is abstract; cannot be instantiated

The reason is simple. That object would have a move() method with no code behind it. Calling it could only fail.

Java catches this at compile time rather than runtime. You find out while typing, not while a customer is on the phone.

5.2 References Still Work Fine

Here is the distinction that trips people in interviews. You cannot create the object. You can absolutely declare the variable.

package com.javahandson;

abstract class Vehicle {
    abstract void move();
}

class Car extends Vehicle {
    @Override
    void move() {
        System.out.println("Car moves on roads");
    }
}

class Boat extends Vehicle {
    @Override
    void move() {
        System.out.println("Boat moves on water");
    }
}

public class Main {
    public static void main(String[] args) {
        Vehicle[] fleet = { new Car(), new Boat() };

        for (Vehicle v : fleet) {
            v.move();
        }
    }
}
// Output:
// Car moves on roads
// Boat moves on water

One array, two very different objects, zero if statements. The abstract type gave you a common label to hold them under.

This is polymorphism doing its job. Our guide to polymorphism in Java covers how the right method gets picked.

5.3 The Code That Looks Like an Exception

Sooner or later you will meet this line and wonder if the rule just broke.

Vehicle v = new Vehicle() {        // this compiles
    @Override
    void move() {
        System.out.println("Something moves");
    }
};

v.move();
// Output:
// Something moves

The rule held. Look at the braces after the brackets.

Java quietly wrote a brand new class for you, one that extends Vehicle and fills in move(). That nameless class is what got created, not Vehicle itself.

People call this an anonymous inner class. It stays a subclass, so the rule survives intact.

6. Constructors in an Abstract Class

6.1 Why They Exist at All

A constructor builds an object. An abstract class cannot become an object. So why does it get a constructor?

Because part of every child object comes from the parent. The parent’s fields need setting up, and a constructor is what sets them up.

Think of it as building one floor of a house. Nobody lives on that floor alone, yet the floors above it need it in place first.

6.2 The Order Things Run

Create a child and the parent constructor goes first, every time.

package com.javahandson;

abstract class Vehicle {
    int speed;

    Vehicle(int speed) {
        this.speed = speed;
        System.out.println("Vehicle constructor, speed " + this.speed);
    }
}

class Car extends Vehicle {
    Car(int speed) {
        super(speed);
        System.out.println("Car constructor");
    }
}

public class Main {
    public static void main(String[] args) {
        Car car = new Car(70);
    }
}
// Output:
// Vehicle constructor, speed 70
// Car constructor

The speed field holds a real value before the Car constructor body starts. That ordering is the whole reason for the rule.

Our guide to this and super in Java goes deeper into how super() threads through a hierarchy.

6.3 Forcing Data In

A parent constructor with parameters becomes a gate. Every child has to pass through it.

package com.javahandson;

abstract class Vehicle {
    final String plate;

    Vehicle(String plate) {
        if (plate == null || plate.isBlank()) {
            throw new IllegalArgumentException("plate required");
        }
        this.plate = plate;
    }

    abstract void move();
}

class Car extends Vehicle {
    Car(String plate) {
        super(plate);              // no way around this
    }

    @Override
    void move() {
        System.out.println(plate + " moves on roads");
    }
}

No Car can exist without a plate now. The check lives in one place and covers every child, current and future.

That is a common use for abstract classes in real code. Shared validation, written once.

7. Abstract Class vs Interface

7.1 The Comparison Table

FeatureAbstract ClassInterface
Keyword to use itextendsimplements
How many per classOne onlyAs many as you like
Instance fieldsYesNo, constants only
ConstructorsYesNo
Method bodiesYes, ordinary methodsYes, default and static since Java 8
Member access levelsAll fourpublic, plus private helpers since Java 9
ModelsWhat a thing ISWhat a thing CAN DO
Typical nameA noun, like VehicleOften an -able word, like Comparable

Two rows matter more than the rest. Instance fields and the one-parent limit decide most real arguments.

7.2 What Java 8 Changed

Older articles will tell you interfaces cannot hold code. That stopped being true in Java 8.

  • Default methods arrived in Java 8, and they carry a real body
  • Static methods on an interface landed in the same release
  • Private helper methods followed in Java 9, for sharing code between defaults

So the old headline difference has faded. One gap never closed, though.

An interface still cannot hold instance state. Every field on an interface is public, static, and final, whether you type those words or not. The moment your design needs a mutable field per object, only an abstract class will do.

7.3 Picking One

Use an abstract class when:

  • Children share fields that change per object
  • You want a constructor to demand or check data
  • The IS-A sentence reads well, as in “a Car is a Vehicle”
  • Real shared code lives in the parent, not just signatures

Use an interface when:

  • Unrelated classes need the same ability
  • A class must take on several roles at once
  • You only want to name a capability, with no state behind it

Plenty of good designs use both. Our guide to interfaces in Java covers the other side in detail.

8. Abstract Classes and Inheritance

8.1 Still One Parent Only

Marking a class abstract changes nothing about the single-inheritance rule.

abstract class Vehicle {
    abstract void move();
}

abstract class Machine {
    abstract void start();
}

// Not allowed, even though both parents are abstract
class Car extends Vehicle, Machine {
    @Override
    void move()  { System.out.println("Car moves"); }

    @Override
    void start() { System.out.println("Car starts"); }
}
// Output:
// java: '{' expected

Two abstract parents are still two parents. Java allows exactly one.

8.2 Multilevel Chains

Chains work beautifully, though. An abstract class may extend another abstract class, and each level can do three things:

  • Fill in some of the abstract methods it inherited
  • Leave the rest for classes below it
  • Declare brand new abstract methods of its own

Section 4.3 showed exactly this pattern in code. Behaviour sharpens as you move down.

8.3 Mixing With Interfaces

Need behaviour from two directions? Extend one abstract class and implement as many interfaces as you want.

package com.javahandson;

interface Rentable {
    double dailyRate();
}

abstract class Vehicle {
    abstract void move();
}

// An abstract class may implement an interface
// without writing the method
abstract class MotorVehicle extends Vehicle implements Rentable {
    void refuel() {
        System.out.println("Tank filled");
    }
}

class Car extends MotorVehicle {
    @Override
    void move() {
        System.out.println("Car moves on roads");
    }

    @Override
    public double dailyRate() {
        return 2500.0;
    }
}

public class Main {
    public static void main(String[] args) {
        Car car = new Car();
        car.refuel();
        car.move();
        System.out.println("Rate: " + car.dailyRate());
    }
}
// Output:
// Tank filled
// Car moves on roads
// Rate: 2500.0

Spot what MotorVehicle got away with. It implements Rentable yet never writes dailyRate().

An abstract class is allowed to skip that, because it stays incomplete by design. Car is concrete, so Car has to finish the job.

9. A Practical Walkthrough

9.1 The Fixed Skeleton

Here is where abstract classes really earn their place. Say you process payments. Every payment follows the same four steps.

Validate the amount. Charge the customer. Save a record. Send a receipt.

Only the charging step differs between a card and UPI. So fix the order once, and leave one hole.

package com.javahandson;

abstract class Payment {

    // The skeleton. final, so no child can reorder the steps.
    public final void process(double amount) {
        validate(amount);
        charge(amount);
        saveRecord(amount);
        sendReceipt();
    }

    private void validate(double amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("amount must be positive");
        }
        System.out.println("Validated " + amount);
    }

    protected abstract void charge(double amount);   // the one hole

    private void saveRecord(double amount) {
        System.out.println("Saved record for " + amount);
    }

    private void sendReceipt() {
        System.out.println("Receipt sent");
    }
}

Notice process is final. Children fill the gap, but nobody rearranges the steps.

9.2 Filling In the Steps

Each payment type now writes one short method.

package com.javahandson;

class CardPayment extends Payment {
    @Override
    protected void charge(double amount) {
        System.out.println("Charged " + amount + " to the card");
    }
}

class UpiPayment extends Payment {
    @Override
    protected void charge(double amount) {
        System.out.println("Collected " + amount + " over UPI");
    }
}

public class Main {
    public static void main(String[] args) {
        Payment[] methods = { new CardPayment(), new UpiPayment() };

        for (Payment payment : methods) {
            payment.process(499.0);
            System.out.println("---");
        }
    }
}
// Output:
// Validated 499.0
// Charged 499.0 to the card
// Saved record for 499.0
// Receipt sent
// ---
// Validated 499.0
// Collected 499.0 over UPI
// Saved record for 499.0
// Receipt sent
// ---

9.3 Reading the Output

Four ideas from this article show up in that run:

  • Shared code, written once. Validation, saving, and receipts live in the parent only.
  • An enforced gap. Every payment type must supply charge, because the compiler insists.
  • A locked order. Marking process final means no child skips the validation step.
  • One common type. The array holds Payment references and treats both kinds alike.

Adding wallet payments tomorrow costs you one class and one method. Nothing else changes.

This shape has a name, by the way. Developers call it the template method pattern, and abstract classes are its natural home.

10. Common Mistakes and Pitfalls

10.1 Forgetting One Abstract Method

Add a new abstract method to a parent and every concrete child breaks at once. That is the feature working, not a bug.

Still, it stings on a big hierarchy. Think twice before adding abstract methods to a class other teams extend.

10.2 Reaching for new

Beginners try new Vehicle() and read the error as a bug in their setup. It is not.

Create a concrete child instead. Keep the abstract type on the left of the assignment if you want the flexible reference.

10.3 Using an Abstract Class as a Pure Contract

An abstract class with no fields, no constructor, and nothing but abstract methods is really an interface in disguise.

Switch it to an interface. You free up the single extends slot for something that genuinely needs it, and classes can implement several.

10.4 Calling an Abstract Method From the Constructor

This one produces a bug that looks impossible at first glance.

package com.javahandson;

abstract class Vehicle {
    Vehicle() {
        move();                 // the child runs before it is ready
    }

    abstract void move();
}

class Car extends Vehicle {
    String type = "sedan";

    @Override
    void move() {
        System.out.println("Car type: " + type);
    }
}

public class Main {
    public static void main(String[] args) {
        new Car();
    }
}
// Output:
// Car type: null

Where did “sedan” go? The parent constructor finished before Java set that field, so the override read a null.

Keep constructors dull. Do the real work in a separate method the caller invokes afterwards.

10.5 Mixing Up abstract and default Methods

Both let a parent type hand code to a child. They are not the same thing.

An abstract method has no body and demands an override. A default method sits on an interface, carries a body, and an override stays optional.

11. Interview Questions

Q: What is an abstract class in Java?

A: It is a class marked with the abstract keyword that you cannot create an object of. It exists to be extended. An abstract class can mix ordinary methods with abstract ones, so it shares real code while forcing children to supply the parts that differ.

Q: Can an abstract class have no abstract methods?

A: Yes, and it compiles fine. Marking a class abstract only blocks object creation. Developers sometimes do this on purpose for a base class that should never stand alone, even when every method already has a body.

Q: Why can we not create an object of an abstract class?

A: The object would carry methods with no code behind them, so any call to one could only fail. Java blocks it at compile time rather than letting the problem surface at runtime. You can still declare a variable of that type and point it at a concrete child.

Q: Why does an abstract class have a constructor?

A: Part of every child object comes from the parent, and those fields need setting up. When you create a child, the parent constructor runs first through super(). It is also a handy place to validate data that every child must supply.

Q: What is the difference between an abstract class and an interface?

A: An abstract class can hold instance fields and constructors, and a class may extend only one. An interface holds no instance state, and a class may implement many. Since Java 8 interfaces can carry default and static method bodies, so the real dividing line today is state, not code.

Q: Can an abstract class have a main method?

A: Yes, and you can run it. The main method is static, so the JVM never needs an object of the class to call it. The same reasoning lets an abstract class hold any static method or static block.

Q: Can an abstract class implement an interface without writing its methods?

A: Yes. An abstract class is allowed to stay incomplete, so it can implement an interface and leave every method unwritten. The first concrete class down the chain then has to supply them all.

Q: Can an abstract method be private, static, or final?

A: No, none of the three. An abstract method exists so a child can override it, and each of those keywords blocks overriding. A private method stays invisible to children, a static method belongs to the class, and final forbids any change.

Q: What does new Vehicle() { … } mean if Vehicle is abstract?

A: Those braces make it an anonymous inner class. Java writes a nameless subclass on the spot, fills in the abstract methods from your block, and creates an object of that subclass. The abstract class itself never gets created, so the rule still holds.

Q: When should I choose an abstract class over an interface?

A: Pick an abstract class when children share fields that change per object, or when a constructor must demand and check data. Pick an interface when unrelated classes need the same ability, or when one class has to take on several roles. If your abstract class has no fields and no constructor, it should probably be an interface.

12. Conclusion

Let us wrap up what we covered. An abstract class in Java is a base class you cannot create an object of, and that limit is the point.

It solves a real problem. A plain parent has to write a body for every method, even when it has no honest answer. Marking the method abstract moves that job to the children and lets the compiler enforce it.

Inside, an abstract class is an ordinary class. Fields, constructors, static members, and finished methods all work normally. Only new stops working.

Against an interface, the dividing line is state. An abstract class holds fields that change per object, and a class may extend just one. Interfaces hold no instance state, and a class may implement many.

The template method pattern in section 9 shows the payoff. Lock the order of steps in a final method, leave one abstract hole, and every new type costs you a single override.

Further Reading

Leave a Comment