1. Abstract class syntax
Let’s figure out what an abstract class is in Java and why you need it at all. Imagine you have a LEGO kit: you have a set of parts (for example, “wheels”, “bricks”), and there are special parts for particular models. An abstract class is like a general instruction for handling the parts without describing the assembly of a specific model: bricks connect by studs, wheels attach to axles, and parts can be stacked in layers. The abstract class describes these common rules and required steps, but does not spell out the assembly of a concrete model. Concrete models are subclasses: they take the general instruction and add the missing steps.
In Java, an abstract class is a class that cannot be instantiated directly (you can’t do new AbstractClass()), but you can inherit from it and implement its “unfinished” methods.
Abstract classes are useful when:
- A group of objects shares common behavior or state, but part of the logic differs.
- You want to implement some “default” functionality and leave some to subclasses.
- You want to forbid creating instances of this class directly (for example, an “Animal” doesn’t exist by itself, but a “Cat” certainly does).
How to declare an abstract class
It’s simple: put the class keyword after the abstract keyword.
public abstract class Animal {
// Fields (for example, the animal's name)
protected String name;
// Constructor
public Animal(String name) {
this.name = name;
}
// Abstract method — declaration only, no implementation!
public abstract void makeSound();
// Regular (implemented) method
public void sleep() {
System.out.println(name + " sleeps: Zzz...");
}
}
Key points:
- An abstract class can contain both implemented and abstract methods.
- An abstract class can contain fields, constructors, and even static methods.
- You cannot create an instance of an abstract class directly:
-
Animal a = new Animal("Someone"); // Error!
How to declare an abstract method
An abstract method is a method without a body, that is, without curly braces and code inside. It is declared with the abstract keyword and must end with a semicolon ;.
public abstract void makeSound();
- Abstract methods can be declared only inside an abstract class.
- A class that extends an abstract class must implement all abstract methods; otherwise, it also becomes abstract.
2. Inheriting abstract classes: how it works
Let’s look at a concrete example. Suppose we have an abstract class Animal with an abstract method makeSound(). Now we’ll create a subclass Dog that implements this method:
public class Dog extends Animal {
public Dog(String name) {
super(name); // Call to the base class constructor
}
@Override
public void makeSound() {
System.out.println(name + " barks: Woof-woof!");
}
}
And one more subclass:
public class Cat extends Animal {
public Cat(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println(name + " meows: Meow!");
}
}
Now we can use these classes in a program:
public class Main {
public static void main(String[] args) {
Animal dog = new Dog("Sharik");
Animal cat = new Cat("Murka");
dog.makeSound(); // Sharik barks: Woof-woof!
cat.makeSound(); // Murka meows: Meow!
dog.sleep(); // Sharik sleeps: Zzz...
cat.sleep(); // Murka sleeps: Zzz...
}
}
Note:
We can declare variables of type Animal, but we can instantiate only concrete (non-abstract) subclasses.
Diagram: how it looks
. Animal (abstract)
/ \
Dog Cat
(implements makeSound) (implements makeSound)
3. When to use an abstract class vs an interface?
This is one of the most common interview questions—and for good reason. Let’s break it down:
- Abstract class — when objects share common state (for example, fields), common logic (methods with implementations), and you want to provide a “skeleton” of behavior with room for extension.
- Interface — when you want to specify only a set of methods (a contract), without implementation and state. Since Java 8, interfaces gained default/static methods, but an interface is still about “what an object can do,” not “how it does it.”
Real-world example:
“Bird” is an abstract class: all birds have beaks and wings, and they can fly (but differently).
“Flyable” is an interface: not only birds can fly—airplanes and superheroes can, too! They all fly differently, but the point is they can fly.
4. Practical examples
Example 1: Abstract class with a partial implementation
Suppose you’re building a game with different types of transport. They all can move, but do so differently. However, they all have a speed, a name, and a standard way to stop.
public abstract class Transport {
protected String name;
protected int speed;
public Transport(String name, int speed) {
this.name = name;
this.speed = speed;
}
// Abstract method — implemented in subclasses
public abstract void move();
// Implemented method
public void stop() {
System.out.println(name + " stopped.");
}
}
public class Car extends Transport {
public Car(String name, int speed) {
super(name, speed);
}
@Override
public void move() {
System.out.println(name + " drives on the road at " + speed + " km/h.");
}
}
public class Bicycle extends Transport {
public Bicycle(String name, int speed) {
super(name, speed);
}
@Override
public void move() {
System.out.println(name + " pedals at " + speed + " km/h.");
}
}
Usage:
Transport car = new Car("Toyota", 120);
Transport bike = new Bicycle("Stels", 25);
car.move(); // Toyota drives on the road at 120 km/h.
bike.move(); // Stels pedals at 25 km/h.
car.stop(); // Toyota stopped.
bike.stop(); // Stels stopped.
Example 2: Abstract method with a parameter
public abstract class Shape {
public abstract double area();
}
public class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
}
public class Rectangle extends Shape {
private double width, height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double area() {
return width * height;
}
}
Usage:
Shape c = new Circle(3);
Shape r = new Rectangle(4, 5);
System.out.println("Area of the circle: " + c.area());
System.out.println("Area of the rectangle: " + r.area());
5. Features and nuances of abstract classes
- An abstract class can use any access modifiers: public, protected, private (for example, for fields).
- You can declare an abstract class without abstract methods. This can be useful if you simply want to forbid creating instances of a base class.
- If a class extends an abstract class but does not implement all abstract methods, it must also be declared abstract.
- An abstract class can have constructors. They are called when creating a subclass instance (via super(...)).
- Abstract methods cannot be private (otherwise they can’t be implemented in a subclass).
- Abstract classes can contain static methods and fields.
- An abstract class can implement interfaces but does not have to implement their methods—subclasses can do that.
Table: abstract class vs interface
| Abstract class | Interface | |
|---|---|---|
| Keyword | |
|
| Can contain fields | Yes (any) | Since Java 8 — only static/final |
| Methods with implementation | Yes | Since Java 8 — default/static |
| Abstract methods | Yes | Yes |
| Multiple inheritance | No | Yes (a class can implement many interfaces) |
| Constructors | Yes | No |
| Can be instantiated | No | No |
6. Common mistakes when working with abstract classes and methods
Error #1: trying to create an instance of an abstract class.
If you write new Animal("Someone"), the compiler will immediately remind you that abstract classes aren’t meant for this. Remember: abstraction is a “skeleton,” not a “living organism.”
Error #2: forgetting to implement all abstract methods in a subclass.
If your class doesn’t implement at least one abstract method of the base class, it must itself be declared abstract, otherwise you’ll get a compilation error.
Error #3: declaring an abstract method outside an abstract class.
In Java you cannot declare an abstract method in a regular (non-abstract) class—the compiler will complain.
Error #4: trying to make an abstract method private or static.
Abstract methods cannot be private (otherwise they can’t be overridden) and cannot be static (because static methods are not overridden). They also cannot be final, because final forbids overriding.
Error #5: forgetting the access modifier on an abstract method.
If you don’t explicitly specify a modifier, the method will have package-private visibility, which might not be what you intended.
GO TO FULL VERSION