Abstract Class in Java: When and Why to Use It (Examples)
-
Last Updated: January 6, 2026
-
By: javahandson
-
Series
Learn what an abstract class in Java is, when to use it versus an interface, and why it matters — with clear, runnable code examples.
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.
Here is the plan:
You need to know extends before starting. If that feels shaky, read our guide to inheritance in Java first.
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.
Developers usually patch this in one of three ways. All three hurt.
Notice the common thread. Each workaround pushes a mistake later, into runtime, where it costs far more to find.
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.
You have been using abstract classes for a while now, probably without noticing. The Java library leans on them heavily.
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.
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.
An abstract class is a real class. It holds almost everything a normal class holds:
Only one thing sets it apart. The new keyword refuses to work on it.
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 roadsRead 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.
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 bodyThe 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.
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.VehicleRead 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.
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 appliedEach level adds what it knows. The chain ends when a concrete class fills the last gap.
Some combinations make no sense, so Java bans them:
Each ban follows the same logic. An abstract member exists so somebody can override it, and each of these keywords blocks overriding.
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 instantiatedThe 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.
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 waterOne 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.
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 movesThe 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.
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.
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 constructorThe 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.
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.
| Feature | Abstract Class | Interface |
|---|---|---|
| Keyword to use it | extends | implements |
| How many per class | One only | As many as you like |
| Instance fields | Yes | No, constants only |
| Constructors | Yes | No |
| Method bodies | Yes, ordinary methods | Yes, default and static since Java 8 |
| Member access levels | All four | public, plus private helpers since Java 9 |
| Models | What a thing IS | What a thing CAN DO |
| Typical name | A noun, like Vehicle | Often an -able word, like Comparable |
Two rows matter more than the rest. Instance fields and the one-parent limit decide most real arguments.
Older articles will tell you interfaces cannot hold code. That stopped being true in Java 8.
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.
Use an abstract class when:
Use an interface when:
Plenty of good designs use both. Our guide to interfaces in Java covers the other side in detail.
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: '{' expectedTwo abstract parents are still two parents. Java allows exactly one.
Chains work beautifully, though. An abstract class may extend another abstract class, and each level can do three things:
Section 4.3 showed exactly this pattern in code. Behaviour sharpens as you move down.
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.0Spot 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.
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.
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
// ---Four ideas from this article show up in that run:
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.
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.
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.
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.
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: nullWhere 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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.